mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Invite Links Fixes
This commit is contained in:
parent
426e8724dc
commit
935f931034
@ -5883,8 +5883,11 @@ Sorry for the inconvenience.";
|
|||||||
"InviteLink.InviteLink" = "Invite Link";
|
"InviteLink.InviteLink" = "Invite Link";
|
||||||
"InviteLink.CreatedBy" = "Link Created By";
|
"InviteLink.CreatedBy" = "Link Created By";
|
||||||
|
|
||||||
|
"InviteLink.DeleteLinkAlert.Text" = "Are you sure you want to delete this link? It will be completely gone.";
|
||||||
|
"InviteLink.DeleteLinkAlert.Action" = "Delete";
|
||||||
|
|
||||||
"InviteLink.DeleteAllRevokedLinksAlert.Text" = "This will delete all revoked links.";
|
"InviteLink.DeleteAllRevokedLinksAlert.Text" = "This will delete all revoked links.";
|
||||||
"InviteLink.DeleteAllRevokedLinksAlert.Action" = "Delete";
|
"InviteLink.DeleteAllRevokedLinksAlert.Action" = "Delete All";
|
||||||
|
|
||||||
"InviteLink.ExpiresIn" = "expires in %@";
|
"InviteLink.ExpiresIn" = "expires in %@";
|
||||||
|
|
||||||
|
@ -1212,8 +1212,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
strongSelf.forEachController({ controller in
|
strongSelf.forEachController({ controller in
|
||||||
if let controller = controller as? UndoOverlayController {
|
if let controller = controller as? UndoOverlayController {
|
||||||
switch controller.content {
|
switch controller.content {
|
||||||
case let .archivedChat(archivedChat):
|
case let .archivedChat(peerId, _, _, _):
|
||||||
if peerIds.contains(PeerId(archivedChat.peerId)) {
|
if peerIds.contains(PeerId(peerId)) {
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -158,7 +158,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
return ChatListSearchItem(theme: theme, placeholder: strings.Contacts_SearchLabel, activate: {
|
return ChatListSearchItem(theme: theme, placeholder: strings.Contacts_SearchLabel, activate: {
|
||||||
interaction.activateSearch()
|
interaction.activateSearch()
|
||||||
})
|
})
|
||||||
case let .sort(theme, strings, sortOrder):
|
case let .sort(_, strings, sortOrder):
|
||||||
var text = strings.Contacts_SortedByName
|
var text = strings.Contacts_SortedByName
|
||||||
if case .presence = sortOrder {
|
if case .presence = sortOrder {
|
||||||
text = strings.Contacts_SortedByPresence
|
text = strings.Contacts_SortedByPresence
|
||||||
@ -166,17 +166,17 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .inline(dropDownIcon, .right), highlight: .alpha, header: nil, action: {
|
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .inline(dropDownIcon, .right), highlight: .alpha, header: nil, action: {
|
||||||
interaction.openSortMenu()
|
interaction.openSortMenu()
|
||||||
})
|
})
|
||||||
case let .permissionInfo(theme, title, text, suppressed):
|
case let .permissionInfo(_, title, text, suppressed):
|
||||||
return InfoListItem(presentationData: ItemListPresentationData(presentationData), title: title, text: .plain(text), style: .plain, closeAction: suppressed ? nil : {
|
return InfoListItem(presentationData: ItemListPresentationData(presentationData), title: title, text: .plain(text), style: .plain, closeAction: suppressed ? nil : {
|
||||||
interaction.suppressWarning()
|
interaction.suppressWarning()
|
||||||
})
|
})
|
||||||
case let .permissionEnable(theme, text):
|
case let .permissionEnable(_, text):
|
||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .none, header: nil, action: {
|
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .none, header: nil, action: {
|
||||||
interaction.authorize()
|
interaction.authorize()
|
||||||
})
|
})
|
||||||
case let .option(_, option, header, theme, _):
|
case let .option(_, option, header, _, _):
|
||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action)
|
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action)
|
||||||
case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, enabled):
|
case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, enabled):
|
||||||
var status: ContactsPeerItemStatus
|
var status: ContactsPeerItemStatus
|
||||||
let itemPeer: ContactsPeerItemPeer
|
let itemPeer: ContactsPeerItemPeer
|
||||||
var isContextActionEnabled = false
|
var isContextActionEnabled = false
|
||||||
@ -928,9 +928,9 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
|> mapToSignal { presentation in
|
|> mapToSignal { presentation in
|
||||||
var generateSections = false
|
var generateSections = false
|
||||||
var includeChatList = false
|
var includeChatList = false
|
||||||
if case let .natural(natural) = presentation {
|
if case let .natural(_, includeChatListValue) = presentation {
|
||||||
generateSections = true
|
generateSections = true
|
||||||
includeChatList = natural.includeChatList
|
includeChatList = includeChatListValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .search(query, searchChatList, searchDeviceContacts, searchGroups, searchChannels, globalSearch) = presentation {
|
if case let .search(query, searchChatList, searchDeviceContacts, searchGroups, searchChannels, globalSearch) = presentation {
|
||||||
|
@ -1225,7 +1225,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
|||||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||||
self.centralItemFooterContentNode.set(centralItemNode.footerContent())
|
self.centralItemFooterContentNode.set(centralItemNode.footerContent())
|
||||||
|
|
||||||
if let (media, _) = mediaForMessage(message: message) {
|
if let _ = mediaForMessage(message: message) {
|
||||||
centralItemNode.activateAsInitial()
|
centralItemNode.activateAsInitial()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .usageCustomPicker(theme, value, focused):
|
case let .usageCustomPicker(theme, value, focused):
|
||||||
let text = value.flatMap { String($0) } ?? (focused ? "" : presentationData.strings.InviteLink_Create_UsersLimitNumberOfUsersUnlimited)
|
let text = value.flatMap { String($0) } ?? (focused ? "" : presentationData.strings.InviteLink_Create_UsersLimitNumberOfUsersUnlimited)
|
||||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: presentationData.strings.InviteLink_Create_UsersLimitNumberOfUsers, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .number, alignment: .right, tag: nil, sectionId: self.section, textUpdated: { updatedText in
|
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: presentationData.strings.InviteLink_Create_UsersLimitNumberOfUsers, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .number, alignment: .right, selectAllOnFocus: true, tag: nil, sectionId: self.section, textUpdated: { updatedText in
|
||||||
guard !updatedText.isEmpty else {
|
guard !updatedText.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -300,9 +300,10 @@ private struct InviteLinkEditControllerState: Equatable {
|
|||||||
var time: InviteLinkTimeLimit
|
var time: InviteLinkTimeLimit
|
||||||
var pickingTimeLimit = false
|
var pickingTimeLimit = false
|
||||||
var pickingUsageLimit = false
|
var pickingUsageLimit = false
|
||||||
|
var updating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func inviteLinkEditController(context: AccountContext, peerId: PeerId, invite: ExportedInvitation?, completion: (() -> Void)? = nil) -> ViewController {
|
public func inviteLinkEditController(context: AccountContext, peerId: PeerId, invite: ExportedInvitation?, completion: ((ExportedInvitation?) -> Void)? = nil) -> ViewController {
|
||||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||||
let actionsDisposable = DisposableSet()
|
let actionsDisposable = DisposableSet()
|
||||||
|
|
||||||
@ -359,8 +360,17 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
|
|||||||
dismissAction()
|
dismissAction()
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
|
||||||
let _ = (revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
let _ = (revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link)
|
||||||
completion?()
|
|> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic))
|
||||||
|
|> deliverOnMainQueue).start(next: { invite in
|
||||||
|
completion?(invite)
|
||||||
|
}, error: { _ in
|
||||||
|
updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.updating = false
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
@ -369,6 +379,7 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
|
|||||||
presentControllerImpl?(controller, nil)
|
presentControllerImpl?(controller, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let previousState = Atomic<InviteLinkEditControllerState?>(value: nil)
|
||||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get())
|
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get())
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
@ -376,7 +387,13 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
|
|||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
})
|
})
|
||||||
|
|
||||||
let rightNavigationButton = ItemListNavigationButton(content: .text(invite == nil ? presentationData.strings.Common_Create : presentationData.strings.Common_Save), style: .bold, enabled: true, action: {
|
let rightNavigationButton = ItemListNavigationButton(content: .text(invite == nil ? presentationData.strings.Common_Create : presentationData.strings.Common_Save), style: state.updating ? .activity : .bold, enabled: true, action: {
|
||||||
|
updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.updating = true
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
|
||||||
let expireDate: Int32?
|
let expireDate: Int32?
|
||||||
if case let .custom(value) = state.time {
|
if case let .custom(value) = state.time {
|
||||||
expireDate = value
|
expireDate = value
|
||||||
@ -390,21 +407,43 @@ public func inviteLinkEditController(context: AccountContext, peerId: PeerId, in
|
|||||||
let usageLimit = state.usage.value
|
let usageLimit = state.usage.value
|
||||||
if invite == nil {
|
if invite == nil {
|
||||||
let _ = (createPeerExportedInvitation(account: context.account, peerId: peerId, expireDate: expireDate, usageLimit: usageLimit)
|
let _ = (createPeerExportedInvitation(account: context.account, peerId: peerId, expireDate: expireDate, usageLimit: usageLimit)
|
||||||
|> deliverOnMainQueue).start(next: { result in
|
|> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic))
|
||||||
completion?()
|
|> deliverOnMainQueue).start(next: { invite in
|
||||||
|
completion?(invite)
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
}, error: { _ in
|
||||||
|
updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.updating = false
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
})
|
})
|
||||||
} else if let invite = invite {
|
} else if let invite = invite {
|
||||||
let _ = (editPeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link, expireDate: expireDate, usageLimit: usageLimit)
|
let _ = (editPeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link, expireDate: expireDate, usageLimit: usageLimit)
|
||||||
|> deliverOnMainQueue).start(next: { result in
|
|> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic))
|
||||||
completion?()
|
|> deliverOnMainQueue).start(next: { invite in
|
||||||
|
completion?(invite)
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
}, error: { _ in
|
||||||
|
updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.updating = false
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let previousState = previousState.swap(state)
|
||||||
|
var animateChanges = false
|
||||||
|
if let previousState = previousState, previousState.pickingTimeLimit != state.pickingTimeLimit {
|
||||||
|
animateChanges = true
|
||||||
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(invite == nil ? presentationData.strings.InviteLink_Create_Title : presentationData.strings.InviteLink_Create_EditTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(invite == nil ? presentationData.strings.InviteLink_Create_Title : presentationData.strings.InviteLink_Create_EditTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: false)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -309,19 +309,14 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
|
|
||||||
var getControllerImpl: (() -> ViewController?)?
|
var getControllerImpl: (() -> ViewController?)?
|
||||||
|
|
||||||
let invitesPromise = Promise<ExportedInvitations?>()
|
let invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: false)
|
||||||
invitesPromise.set(.single(nil)
|
let revokedInvitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: true, forceUpdate: true)
|
||||||
|> then(peerExportedInvitations(account: context.account, peerId: peerId, revoked: false)))
|
|
||||||
|
|
||||||
let revokedInvitesPromise = Promise<ExportedInvitations?>()
|
|
||||||
revokedInvitesPromise.set(.single(nil)
|
|
||||||
|> then(peerExportedInvitations(account: context.account, peerId: peerId, revoked: true)))
|
|
||||||
|
|
||||||
let arguments = InviteLinkListControllerArguments(context: context, shareMainLink: { invite in
|
let arguments = InviteLinkListControllerArguments(context: context, shareMainLink: { invite in
|
||||||
let shareController = ShareController(context: context, subject: .url(invite.link))
|
let shareController = ShareController(context: context, subject: .url(invite.link))
|
||||||
presentControllerImpl?(shareController, nil)
|
presentControllerImpl?(shareController, nil)
|
||||||
}, openMainLink: { invite in
|
}, openMainLink: { invite in
|
||||||
let controller = InviteLinkViewController(context: context, peerId: peerId, invite: invite, importersContext: nil)
|
let controller = InviteLinkViewController(context: context, peerId: peerId, invite: invite, invitationsContext: nil, importersContext: nil)
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}, copyLink: { invite in
|
}, copyLink: { invite in
|
||||||
UIPasteboard.general.string = invite.link
|
UIPasteboard.general.string = invite.link
|
||||||
@ -390,7 +385,9 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
updatedState.revokingPrivateLink = false
|
updatedState.revokingPrivateLink = false
|
||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
invitesPromise.set(peerExportedInvitations(account: context.account, peerId: peerId, revoked: false))
|
|
||||||
|
invitesContext.reload()
|
||||||
|
revokedInvitesContext.reload()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -403,14 +400,16 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
|
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
|
||||||
presentInGlobalOverlayImpl?(contextController)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}, createLink: {
|
}, createLink: {
|
||||||
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: nil, completion: {
|
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: nil, completion: { invite in
|
||||||
invitesPromise.set(peerExportedInvitations(account: context.account, peerId: peerId, revoked: false))
|
if let invite = invite {
|
||||||
|
invitesContext.add(invite)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}, openLink: { invite in
|
}, openLink: { invite in
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
let controller = InviteLinkViewController(context: context, peerId: peerId, invite: invite, importersContext: nil)
|
let controller = InviteLinkViewController(context: context, peerId: peerId, invite: invite, invitationsContext: invitesContext, importersContext: nil)
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}
|
}
|
||||||
}, linkContextAction: { invite, node, gesture in
|
}, linkContextAction: { invite, node, gesture in
|
||||||
@ -446,8 +445,15 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
|
|
||||||
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: {
|
let controller = inviteLinkEditController(context: context, peerId: peerId, invite: invite, completion: { invite in
|
||||||
invitesPromise.set(peerExportedInvitations(account: context.account, peerId: peerId, revoked: false))
|
if let invite = invite {
|
||||||
|
if invite.isRevoked {
|
||||||
|
invitesContext.remove(invite)
|
||||||
|
revokedInvitesContext.add(invite)
|
||||||
|
} else {
|
||||||
|
invitesContext.update(invite)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
@ -466,13 +472,15 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
}
|
}
|
||||||
controller.setItemGroups([
|
controller.setItemGroups([
|
||||||
ActionSheetItemGroup(items: [
|
ActionSheetItemGroup(items: [
|
||||||
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text),
|
ActionSheetTextItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Text),
|
||||||
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
|
ActionSheetButtonItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Action, color: .destructive, action: {
|
||||||
dismissAction()
|
dismissAction()
|
||||||
|
|
||||||
revokeLinkDisposable.set((deletePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
revokeLinkDisposable.set((deletePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
||||||
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
revokedInvitesContext.remove(invite)
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
@ -498,6 +506,9 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
revokeLinkDisposable.set((revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
revokeLinkDisposable.set((revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
|
||||||
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
invitesContext.remove(invite)
|
||||||
|
revokedInvitesContext.add(invite)
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
@ -521,8 +532,9 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
dismissAction()
|
dismissAction()
|
||||||
|
|
||||||
deleteAllRevokedLinksDisposable.set((deleteAllRevokedPeerExportedInvitations(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: {
|
deleteAllRevokedLinksDisposable.set((deleteAllRevokedPeerExportedInvitations(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: {
|
||||||
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
revokedInvitesContext.clear()
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
@ -552,11 +564,11 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId) ->
|
|||||||
importersState.set(context.state |> map(Optional.init))
|
importersState.set(context.state |> map(Optional.init))
|
||||||
}
|
}
|
||||||
|
|
||||||
let signal = combineLatest(context.sharedContext.presentationData, peerView, importersContext, importersState.get(), invitesPromise.get(), revokedInvitesPromise.get())
|
let signal = combineLatest(context.sharedContext.presentationData, peerView, importersContext, importersState.get(), invitesContext.state, revokedInvitesContext.state)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, view, importersContext, importers, invites, revokedInvites -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, view, importersContext, importers, invites, revokedInvites -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.InviteLink_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.InviteLink_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkListControllerEntries(presentationData: presentationData, view: view, invites: invites?.list, revokedInvites: revokedInvites?.list, mainPeers: importers?.importers.compactMap { $0.peer.peer } ?? []), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: false)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkListControllerEntries(presentationData: presentationData, view: view, invites: invites.invitations, revokedInvites: revokedInvites.invitations, mainPeers: importers?.importers.compactMap { $0.peer.peer } ?? []), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: false)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -342,7 +342,6 @@ public final class InviteLinkQRCodeController: ViewController {
|
|||||||
self.containerLayout = (layout, navigationBarHeight)
|
self.containerLayout = (layout, navigationBarHeight)
|
||||||
|
|
||||||
var insets = layout.insets(options: [.statusBar, .input])
|
var insets = layout.insets(options: [.statusBar, .input])
|
||||||
let cleanInsets = layout.insets(options: [.statusBar])
|
|
||||||
insets.top = max(10.0, insets.top)
|
insets.top = max(10.0, insets.top)
|
||||||
|
|
||||||
let makeImageLayout = self.qrImageNode.asyncLayout()
|
let makeImageLayout = self.qrImageNode.asyncLayout()
|
||||||
|
@ -118,7 +118,7 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .importer(lhsIndex, lhsTheme, lhsDateTimeFormat, lhsPeer, lhsDate, lhsLoading):
|
case let .importer(lhsIndex, lhsTheme, lhsDateTimeFormat, lhsPeer, lhsDate, lhsLoading):
|
||||||
if case let .importer(rhsIndex, rhsTheme, rhsDateTimeFormat, rhsPeer, rhsDate, rhsLoading) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsDate == rhsDate {
|
if case let .importer(rhsIndex, rhsTheme, rhsDateTimeFormat, rhsPeer, rhsDate, rhsLoading) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsDate == rhsDate, lhsLoading == rhsLoading {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -192,7 +192,7 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
let dateString = stringForFullDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: dateTimeFormat)
|
let dateString = stringForFullDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: dateTimeFormat)
|
||||||
return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: interaction.context, peer: peer, height: .generic, nameStyle: .distinctBold, presence: nil, text: .text(dateString, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: 0, action: {
|
return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: interaction.context, peer: peer, height: .generic, nameStyle: .distinctBold, presence: nil, text: .text(dateString, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: 0, action: {
|
||||||
interaction.openPeer(peer.id)
|
interaction.openPeer(peer.id)
|
||||||
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, hasTopStripe: false, noInsets: true, tag: nil, shimmering: ItemListPeerItemShimmering(alternationIndex: 0))
|
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, hasTopStripe: false, noInsets: true, tag: nil, shimmering: loading ? ItemListPeerItemShimmering(alternationIndex: 0) : nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,14 +217,16 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
private let invite: ExportedInvitation
|
private let invite: ExportedInvitation
|
||||||
|
private let invitationsContext: PeerExportedInvitationsContext?
|
||||||
private let importersContext: PeerInvitationImportersContext?
|
private let importersContext: PeerInvitationImportersContext?
|
||||||
|
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
public init(context: AccountContext, peerId: PeerId, invite: ExportedInvitation, importersContext: PeerInvitationImportersContext?) {
|
public init(context: AccountContext, peerId: PeerId, invite: ExportedInvitation, invitationsContext: PeerExportedInvitationsContext?, importersContext: PeerInvitationImportersContext?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.invite = invite
|
self.invite = invite
|
||||||
|
self.invitationsContext = invitationsContext
|
||||||
self.importersContext = importersContext
|
self.importersContext = importersContext
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: nil)
|
super.init(navigationBarPresentationData: nil)
|
||||||
@ -539,8 +541,17 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
let navigationController = self.controller?.navigationController as? NavigationController
|
let navigationController = self.controller?.navigationController as? NavigationController
|
||||||
self.controller?.dismiss()
|
self.controller?.dismiss()
|
||||||
|
|
||||||
|
let invitationsContext = self.controller?.invitationsContext
|
||||||
if let navigationController = navigationController {
|
if let navigationController = navigationController {
|
||||||
let controller = inviteLinkEditController(context: self.context, peerId: self.peerId, invite: self.invite)
|
let controller = inviteLinkEditController(context: self.context, peerId: self.peerId, invite: self.invite, completion: { [weak self] invite in
|
||||||
|
if let invite = invite {
|
||||||
|
if invite.isRevoked {
|
||||||
|
invitationsContext?.remove(invite)
|
||||||
|
} else {
|
||||||
|
invitationsContext?.update(invite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
navigationController.pushViewController(controller)
|
navigationController.pushViewController(controller)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
private let bottomStripeNode: ASDisplayNode
|
private let bottomStripeNode: ASDisplayNode
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
private let datePicker: UIDatePicker
|
private var datePicker: UIDatePicker?
|
||||||
|
|
||||||
private var item: ItemListDatePickerItem?
|
private var item: ItemListDatePickerItem?
|
||||||
|
|
||||||
@ -98,25 +98,31 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
self.bottomStripeNode = ASDisplayNode()
|
self.bottomStripeNode = ASDisplayNode()
|
||||||
self.bottomStripeNode.isLayerBacked = true
|
self.bottomStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
self.datePicker = UIDatePicker()
|
|
||||||
self.datePicker.minimumDate = Date()
|
|
||||||
self.datePicker.datePickerMode = .dateAndTime
|
|
||||||
if #available(iOS 14.0, *) {
|
|
||||||
self.datePicker.preferredDatePickerStyle = .inline
|
|
||||||
}
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false)
|
super.init(layerBacked: false, dynamicBounce: false)
|
||||||
|
|
||||||
self.datePicker.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func didLoad() {
|
public override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
self.view.addSubview(self.datePicker)
|
let datePicker = UIDatePicker()
|
||||||
|
datePicker.minimumDate = Date()
|
||||||
|
datePicker.datePickerMode = .dateAndTime
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
datePicker.preferredDatePickerStyle = .inline
|
||||||
|
}
|
||||||
|
|
||||||
|
datePicker.addTarget(self, action: #selector(self.datePickerUpdated), for: .valueChanged)
|
||||||
|
|
||||||
|
self.view.addSubview(datePicker)
|
||||||
|
self.datePicker = datePicker
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func datePickerUpdated() {
|
@objc private func datePickerUpdated() {
|
||||||
self.item?.updated?(Int32(self.datePicker.date.timeIntervalSince1970))
|
guard let datePicker = self.datePicker else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.item?.updated?(Int32(datePicker.date.timeIntervalSince1970))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func asyncLayout() -> (_ item: ItemListDatePickerItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
public func asyncLayout() -> (_ item: ItemListDatePickerItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
@ -162,8 +168,8 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.datePicker.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) } ?? Date()
|
strongSelf.datePicker?.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) } ?? Date()
|
||||||
strongSelf.datePicker.frame = CGRect(origin: CGPoint(x: 16.0, y: 3.0), size: CGSize(width: contentSize.width - 32.0, height: contentSize.height))
|
strongSelf.datePicker?.frame = CGRect(origin: CGPoint(x: 16.0, y: 3.0), size: CGSize(width: contentSize.width - 32.0, height: contentSize.height))
|
||||||
|
|
||||||
switch item.style {
|
switch item.style {
|
||||||
case .plain:
|
case .plain:
|
||||||
|
@ -130,7 +130,6 @@ public class ItemListInviteLinkGridItemNode: ListViewItemNode, ItemListItemNode
|
|||||||
let itemSeparatorColor: UIColor
|
let itemSeparatorColor: UIColor
|
||||||
|
|
||||||
let leftInset = 16.0 + params.leftInset
|
let leftInset = 16.0 + params.leftInset
|
||||||
let rightInset = 16.0 + params.rightInset
|
|
||||||
|
|
||||||
var height: CGFloat
|
var height: CGFloat
|
||||||
let count = item.invites?.count ?? 0
|
let count = item.invites?.count ?? 0
|
||||||
|
@ -46,6 +46,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
let clearType: ItemListSingleLineInputClearType
|
let clearType: ItemListSingleLineInputClearType
|
||||||
let maxLength: Int
|
let maxLength: Int
|
||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
|
let selectAllOnFocus: Bool
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let textUpdated: (String) -> Void
|
let textUpdated: (String) -> Void
|
||||||
@ -55,7 +56,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
let cleared: (() -> Void)?
|
let cleared: (() -> Void)?
|
||||||
public let tag: ItemListItemTag?
|
public let tag: ItemListItemTag?
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, alignment: ItemListSingleLineInputAlignment = .default, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, maxLength: Int = 0, enabled: Bool = true, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void, cleared: (() -> Void)? = nil) {
|
public init(presentationData: ItemListPresentationData, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, alignment: ItemListSingleLineInputAlignment = .default, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, maxLength: Int = 0, enabled: Bool = true, selectAllOnFocus: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void, cleared: (() -> Void)? = nil) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
@ -67,6 +68,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
self.clearType = clearType
|
self.clearType = clearType
|
||||||
self.maxLength = maxLength
|
self.maxLength = maxLength
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
|
self.selectAllOnFocus = selectAllOnFocus
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.textUpdated = textUpdated
|
self.textUpdated = textUpdated
|
||||||
@ -494,6 +496,13 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
|
|||||||
|
|
||||||
@objc public func textFieldDidBeginEditing(_ textField: UITextField) {
|
@objc public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||||
self.item?.updatedFocus?(true)
|
self.item?.updatedFocus?(true)
|
||||||
|
if self.item?.selectAllOnFocus == true {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
let startPosition = self.textNode.textField.beginningOfDocument
|
||||||
|
let endPosition = self.textNode.textField.endOfDocument
|
||||||
|
self.textNode.textField.selectedTextRange = self.textNode.textField.textRange(from: startPosition, to: endPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
self.updateClearButtonVisibility()
|
self.updateClearButtonVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ private enum ChannelAdminEntry: ItemListNodeEntry {
|
|||||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
let arguments = arguments as! ChannelAdminControllerArguments
|
let arguments = arguments as! ChannelAdminControllerArguments
|
||||||
switch self {
|
switch self {
|
||||||
case let .info(_, strings, dateTimeFormat, peer, presence):
|
case let .info(_, _, dateTimeFormat, peer, presence):
|
||||||
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, cachedData: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
|
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, cachedData: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
|
||||||
}, avatarTapped: {
|
}, avatarTapped: {
|
||||||
})
|
})
|
||||||
|
@ -182,17 +182,17 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
let arguments = arguments as! ChannelMembersControllerArguments
|
let arguments = arguments as! ChannelMembersControllerArguments
|
||||||
switch self {
|
switch self {
|
||||||
case let .addMember(theme, text):
|
case let .addMember(_, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.addMember()
|
arguments.addMember()
|
||||||
})
|
})
|
||||||
case let .inviteLink(theme, text):
|
case let .inviteLink(_, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.inviteViaLink()
|
arguments.inviteViaLink()
|
||||||
})
|
})
|
||||||
case let .addMemberInfo(theme, text):
|
case let .addMemberInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled):
|
case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled):
|
||||||
let text: ItemListPeerItemText
|
let text: ItemListPeerItemText
|
||||||
if let user = participant.peer as? TelegramUser, let _ = user.botInfo {
|
if let user = participant.peer as? TelegramUser, let _ = user.botInfo {
|
||||||
text = .text(strings.Bot_GenericBotStatus, .secondary)
|
text = .text(strings.Bot_GenericBotStatus, .secondary)
|
||||||
@ -465,6 +465,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|
|||||||
}
|
}
|
||||||
}, inviteViaLink: {
|
}, inviteViaLink: {
|
||||||
if let controller = getControllerImpl?() {
|
if let controller = getControllerImpl?() {
|
||||||
|
dismissInputImpl?()
|
||||||
presentControllerImpl?(InviteLinkInviteController(context: context, peerId: peerId, parentNavigationController: controller.navigationController as? NavigationController), nil)
|
presentControllerImpl?(InviteLinkInviteController(context: context, peerId: peerId, parentNavigationController: controller.navigationController as? NavigationController), nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -79,7 +79,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||||||
case publicLinkAvailability(PresentationTheme, String, Bool)
|
case publicLinkAvailability(PresentationTheme, String, Bool)
|
||||||
case editablePublicLink(PresentationTheme, PresentationStrings, String, String)
|
case editablePublicLink(PresentationTheme, PresentationStrings, String, String)
|
||||||
case privateLinkHeader(PresentationTheme, String)
|
case privateLinkHeader(PresentationTheme, String)
|
||||||
case privateLink(PresentationTheme, ExportedInvitation?)
|
case privateLink(PresentationTheme, ExportedInvitation?, Bool)
|
||||||
case privateLinkInfo(PresentationTheme, String)
|
case privateLinkInfo(PresentationTheme, String)
|
||||||
case privateLinkManage(PresentationTheme, String)
|
case privateLinkManage(PresentationTheme, String)
|
||||||
case privateLinkManageInfo(PresentationTheme, String)
|
case privateLinkManageInfo(PresentationTheme, String)
|
||||||
@ -184,8 +184,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .privateLink(lhsTheme, lhsInvite):
|
case let .privateLink(lhsTheme, lhsInvite, lhsDisplayImporters):
|
||||||
if case let .privateLink(rhsTheme, rhsInvite) = rhs, lhsTheme === rhsTheme, lhsInvite == rhsInvite {
|
if case let .privateLink(rhsTheme, rhsInvite, rhsDisplayImporters) = rhs, lhsTheme === rhsTheme, lhsInvite == rhsInvite, lhsDisplayImporters == rhsDisplayImporters {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -292,8 +292,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
|||||||
return ItemListActivityTextItem(displayActivity: value, presentationData: presentationData, text: attr, sectionId: self.section)
|
return ItemListActivityTextItem(displayActivity: value, presentationData: presentationData, text: attr, sectionId: self.section)
|
||||||
case let .privateLinkHeader(_, title):
|
case let .privateLinkHeader(_, title):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
|
||||||
case let .privateLink(_, invite):
|
case let .privateLink(_, invite, displayImporters):
|
||||||
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, peers: [], displayButton: true, displayImporters: true, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, peers: [], displayButton: true, displayImporters: displayImporters, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
arguments.copyLink(invite)
|
arguments.copyLink(invite)
|
||||||
}
|
}
|
||||||
@ -602,7 +602,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||||||
case .privateChannel:
|
case .privateChannel:
|
||||||
let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation
|
let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation
|
||||||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
||||||
entries.append(.privateLink(presentationData.theme, invite))
|
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||||
if isGroup {
|
if isGroup {
|
||||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
||||||
} else {
|
} else {
|
||||||
@ -621,7 +621,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||||||
case .privateLink:
|
case .privateLink:
|
||||||
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
|
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
|
||||||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
||||||
entries.append(.privateLink(presentationData.theme, invite))
|
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
|
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
|
||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup:
|
||||||
@ -720,7 +720,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||||||
case .privateChannel:
|
case .privateChannel:
|
||||||
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
|
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
|
||||||
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_PermanentLink.uppercased()))
|
||||||
entries.append(.privateLink(presentationData.theme, invite))
|
entries.append(.privateLink(presentationData.theme, invite, mode != .initialSetup))
|
||||||
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
|
||||||
switch mode {
|
switch mode {
|
||||||
case .initialSetup:
|
case .initialSetup:
|
||||||
@ -1059,7 +1059,6 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
|
|||||||
return state.withUpdatedUpdatingAddressName(false)
|
return state.withUpdatedUpdatingAddressName(false)
|
||||||
}
|
}
|
||||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
|
|
||||||
}, completed: {
|
}, completed: {
|
||||||
updateState { state in
|
updateState { state in
|
||||||
return state.withUpdatedUpdatingAddressName(false)
|
return state.withUpdatedUpdatingAddressName(false)
|
||||||
|
@ -3,20 +3,24 @@ import Postbox
|
|||||||
public final class CachedStickerQueryResult: PostboxCoding {
|
public final class CachedStickerQueryResult: PostboxCoding {
|
||||||
public let items: [TelegramMediaFile]
|
public let items: [TelegramMediaFile]
|
||||||
public let hash: Int32
|
public let hash: Int32
|
||||||
|
public let timestamp: Int32
|
||||||
|
|
||||||
public init(items: [TelegramMediaFile], hash: Int32) {
|
public init(items: [TelegramMediaFile], hash: Int32, timestamp: Int32) {
|
||||||
self.items = items
|
self.items = items
|
||||||
self.hash = hash
|
self.hash = hash
|
||||||
|
self.timestamp = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
self.items = decoder.decodeObjectArrayForKey("it").map { $0 as! TelegramMediaFile }
|
self.items = decoder.decodeObjectArrayForKey("it").map { $0 as! TelegramMediaFile }
|
||||||
self.hash = decoder.decodeInt32ForKey("h", orElse: 0)
|
self.hash = decoder.decodeInt32ForKey("h", orElse: 0)
|
||||||
|
self.timestamp = decoder.decodeInt32ForKey("t", orElse: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
encoder.encodeObjectArray(self.items, forKey: "it")
|
encoder.encodeObjectArray(self.items, forKey: "it")
|
||||||
encoder.encodeInt32(self.hash, forKey: "h")
|
encoder.encodeInt32(self.hash, forKey: "h")
|
||||||
|
encoder.encodeInt32(self.timestamp, forKey: "t")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func cacheKey(_ query: String) -> ValueBoxKey {
|
public static func cacheKey(_ query: String) -> ValueBoxKey {
|
||||||
|
@ -74,6 +74,7 @@ public struct Namespaces {
|
|||||||
public static let cachedContextResults: Int8 = 10
|
public static let cachedContextResults: Int8 = 10
|
||||||
public static let proximityNotificationStoredState: Int8 = 11
|
public static let proximityNotificationStoredState: Int8 = 11
|
||||||
public static let cachedPeerInvitationImporters: Int8 = 12
|
public static let cachedPeerInvitationImporters: Int8 = 12
|
||||||
|
public static let cachedPeerExportedInvitations: Int8 = 13
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UnorderedItemList {
|
public struct UnorderedItemList {
|
||||||
|
@ -170,6 +170,8 @@ private var declaredEncodables: Void = {
|
|||||||
declareEncodable(ValidationMessageAttribute.self, f: { ValidationMessageAttribute(decoder: $0) })
|
declareEncodable(ValidationMessageAttribute.self, f: { ValidationMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(EmojiSearchQueryMessageAttribute.self, f: { EmojiSearchQueryMessageAttribute(decoder: $0) })
|
declareEncodable(EmojiSearchQueryMessageAttribute.self, f: { EmojiSearchQueryMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(CachedPeerInvitationImporters.self, f: { CachedPeerInvitationImporters(decoder: $0) })
|
declareEncodable(CachedPeerInvitationImporters.self, f: { CachedPeerInvitationImporters(decoder: $0) })
|
||||||
|
declareEncodable(CachedPeerExportedInvitations.self, f: { CachedPeerExportedInvitations(decoder: $0) })
|
||||||
|
declareEncodable(ExportedInvitation.self, f: { ExportedInvitation(decoder: $0) })
|
||||||
|
|
||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
|
@ -57,8 +57,12 @@ public func revokePersistentPeerExportedInvitation(account: Account, peerId: Pee
|
|||||||
} |> switchToLatest
|
} |> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
public func createPeerExportedInvitation(account: Account, peerId: PeerId, expireDate: Int32?, usageLimit: Int32?) -> Signal<ExportedInvitation?, NoError> {
|
public enum CreatePeerExportedInvitationError {
|
||||||
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, NoError> in
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func createPeerExportedInvitation(account: Account, peerId: PeerId, expireDate: Int32?, usageLimit: Int32?) -> Signal<ExportedInvitation?, CreatePeerExportedInvitationError> {
|
||||||
|
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, CreatePeerExportedInvitationError> in
|
||||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if let _ = expireDate {
|
if let _ = expireDate {
|
||||||
@ -68,7 +72,7 @@ public func createPeerExportedInvitation(account: Account, peerId: PeerId, expir
|
|||||||
flags |= (1 << 1)
|
flags |= (1 << 1)
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: expireDate, usageLimit: usageLimit))
|
return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: expireDate, usageLimit: usageLimit))
|
||||||
|> retryRequest
|
|> mapError { _ in return CreatePeerExportedInvitationError.generic }
|
||||||
|> map { result -> ExportedInvitation? in
|
|> map { result -> ExportedInvitation? in
|
||||||
if let invitation = ExportedInvitation(apiExportedInvite: result) {
|
if let invitation = ExportedInvitation(apiExportedInvite: result) {
|
||||||
return invitation
|
return invitation
|
||||||
@ -79,7 +83,9 @@ public func createPeerExportedInvitation(account: Account, peerId: PeerId, expir
|
|||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
} |> switchToLatest
|
}
|
||||||
|
|> castError(CreatePeerExportedInvitationError.self)
|
||||||
|
|> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ExportedInvitations : Equatable {
|
public struct ExportedInvitations : Equatable {
|
||||||
@ -258,9 +264,10 @@ final class CachedPeerExportedInvitations: PostboxCoding {
|
|||||||
let canLoadMore: Bool
|
let canLoadMore: Bool
|
||||||
let count: Int32
|
let count: Int32
|
||||||
|
|
||||||
public static func key(peerId: PeerId) -> ValueBoxKey {
|
public static func key(peerId: PeerId, revoked: Bool) -> ValueBoxKey {
|
||||||
let key = ValueBoxKey(length: 8 + 4)
|
let key = ValueBoxKey(length: 8 + 4)
|
||||||
key.setInt64(0, value: peerId.toInt64())
|
key.setInt64(0, value: peerId.toInt64())
|
||||||
|
key.setInt32(8, value: revoked ? 1 : 0)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,26 +294,32 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
|
private let revoked: Bool
|
||||||
|
private var forceUpdate: Bool
|
||||||
private let disposable = MetaDisposable()
|
private let disposable = MetaDisposable()
|
||||||
|
private let updateDisposable = MetaDisposable()
|
||||||
private var isLoadingMore: Bool = false
|
private var isLoadingMore: Bool = false
|
||||||
private var hasLoadedOnce: Bool = false
|
private var hasLoadedOnce: Bool = false
|
||||||
private var canLoadMore: Bool = true
|
private var canLoadMore: Bool = true
|
||||||
|
private var loadedFromCache: Bool = false
|
||||||
private var results: [ExportedInvitation] = []
|
private var results: [ExportedInvitation] = []
|
||||||
private var count: Int32
|
private var count: Int32
|
||||||
private var populateCache: Bool = true
|
private var populateCache: Bool = true
|
||||||
|
|
||||||
let state = Promise<PeerExportedInvitationsState>()
|
let state = Promise<PeerExportedInvitationsState>()
|
||||||
|
|
||||||
init(queue: Queue, account: Account, peerId: PeerId) {
|
init(queue: Queue, account: Account, peerId: PeerId, revoked: Bool, forceUpdate: Bool) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.account = account
|
self.account = account
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
|
self.revoked = revoked
|
||||||
|
self.forceUpdate = forceUpdate
|
||||||
|
|
||||||
self.count = 0
|
self.count = 0
|
||||||
|
|
||||||
self.isLoadingMore = true
|
self.isLoadingMore = true
|
||||||
self.disposable.set((account.postbox.transaction { transaction -> CachedPeerExportedInvitations? in
|
self.disposable.set((account.postbox.transaction { transaction -> CachedPeerExportedInvitations? in
|
||||||
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerInvitationImporters, key: CachedPeerExportedInvitations.key(peerId: peerId))) as? CachedPeerExportedInvitations
|
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerExportedInvitations, key: CachedPeerExportedInvitations.key(peerId: peerId, revoked: revoked))) as? CachedPeerExportedInvitations
|
||||||
}
|
}
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] cachedResult in
|
|> deliverOn(self.queue)).start(next: { [weak self] cachedResult in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -318,6 +331,7 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
strongSelf.count = cachedResult.count
|
strongSelf.count = cachedResult.count
|
||||||
strongSelf.hasLoadedOnce = true
|
strongSelf.hasLoadedOnce = true
|
||||||
strongSelf.canLoadMore = cachedResult.canLoadMore
|
strongSelf.canLoadMore = cachedResult.canLoadMore
|
||||||
|
strongSelf.loadedFromCache = true
|
||||||
}
|
}
|
||||||
strongSelf.loadMore()
|
strongSelf.loadMore()
|
||||||
}))
|
}))
|
||||||
@ -327,6 +341,12 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.disposable.dispose()
|
self.disposable.dispose()
|
||||||
|
self.updateDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
func reload() {
|
||||||
|
self.forceUpdate = true
|
||||||
|
self.loadMore()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMore() {
|
func loadMore() {
|
||||||
@ -336,16 +356,33 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
self.isLoadingMore = true
|
self.isLoadingMore = true
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let peerId = self.peerId
|
let peerId = self.peerId
|
||||||
let lastResult = self.results.last
|
let revoked = self.revoked
|
||||||
|
var lastResult = self.results.last
|
||||||
|
|
||||||
|
if self.forceUpdate {
|
||||||
|
self.forceUpdate = false
|
||||||
|
lastResult = nil
|
||||||
|
}
|
||||||
|
if !self.forceUpdate && self.loadedFromCache {
|
||||||
|
self.populateCache = false
|
||||||
|
self.loadedFromCache = false
|
||||||
|
}
|
||||||
let populateCache = self.populateCache
|
let populateCache = self.populateCache
|
||||||
|
|
||||||
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
}
|
}
|
||||||
|> mapToSignal { inputPeer -> Signal<([ExportedInvitation], Int32), NoError> in
|
|> mapToSignal { inputPeer -> Signal<([ExportedInvitation], Int32), NoError> in
|
||||||
if let inputPeer = inputPeer {
|
if let inputPeer = inputPeer {
|
||||||
let offsetLink = lastResult?.link
|
let offsetLink = lastResult?.link
|
||||||
|
var flags: Int32 = 0
|
||||||
let signal = account.network.request(Api.functions.messages.getExportedChatInvites(flags: 0, peer: inputPeer, adminId: nil, offsetLink: offsetLink, limit: lastResult == nil ? 50 : 100))
|
if let _ = offsetLink {
|
||||||
|
flags |= (1 << 2)
|
||||||
|
}
|
||||||
|
if revoked {
|
||||||
|
flags |= (1 << 3)
|
||||||
|
}
|
||||||
|
let signal = account.network.request(Api.functions.messages.getExportedChatInvites(flags: flags, peer: inputPeer, adminId: nil, offsetLink: offsetLink, limit: lastResult == nil ? 50 : 100))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.messages.ExportedChatInvites?, NoError> in
|
|> `catch` { _ -> Signal<Api.messages.ExportedChatInvites?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -366,7 +403,7 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
})
|
})
|
||||||
let invitations: [ExportedInvitation] = invites.compactMap { ExportedInvitation(apiExportedInvite: $0) }
|
let invitations: [ExportedInvitation] = invites.compactMap { ExportedInvitation(apiExportedInvite: $0) }
|
||||||
if populateCache {
|
if populateCache {
|
||||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerInvitationImporters, key: CachedPeerExportedInvitations.key(peerId: peerId)), entry: CachedPeerExportedInvitations(invitations: invitations, canLoadMore: count >= 50, count: count), collectionSpec: cachedPeerExportedInvitationsCollectionSpec)
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerExportedInvitations, key: CachedPeerExportedInvitations.key(peerId: peerId, revoked: revoked)), entry: CachedPeerExportedInvitations(invitations: invitations, canLoadMore: count >= 50, count: count), collectionSpec: cachedPeerExportedInvitationsCollectionSpec)
|
||||||
}
|
}
|
||||||
return (invitations, count)
|
return (invitations, count)
|
||||||
}
|
}
|
||||||
@ -401,10 +438,63 @@ private final class PeerExportedInvitationsContextImpl {
|
|||||||
strongSelf.count = Int32(strongSelf.results.count)
|
strongSelf.count = Int32(strongSelf.results.count)
|
||||||
}
|
}
|
||||||
strongSelf.updateState()
|
strongSelf.updateState()
|
||||||
|
|
||||||
|
if strongSelf.forceUpdate {
|
||||||
|
strongSelf.loadMore()
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
self.updateState()
|
self.updateState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func add(_ invite: ExportedInvitation) {
|
||||||
|
var results = self.results
|
||||||
|
results.removeAll(where: { $0.link == invite.link})
|
||||||
|
results.insert(invite, at: 0)
|
||||||
|
self.results = results
|
||||||
|
self.updateState()
|
||||||
|
self.updateCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(_ invite: ExportedInvitation) {
|
||||||
|
var results = self.results
|
||||||
|
if let index = self.results.firstIndex(where: { $0.link == invite.link }) {
|
||||||
|
results[index] = invite
|
||||||
|
}
|
||||||
|
self.results = results
|
||||||
|
self.updateState()
|
||||||
|
self.updateCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func remove(_ invite: ExportedInvitation) {
|
||||||
|
var results = self.results
|
||||||
|
results.removeAll(where: { $0.link == invite.link})
|
||||||
|
self.results = results
|
||||||
|
self.updateState()
|
||||||
|
self.updateCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func clear() {
|
||||||
|
self.results = []
|
||||||
|
self.count = 0
|
||||||
|
self.updateState()
|
||||||
|
self.updateCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateCache() {
|
||||||
|
guard self.hasLoadedOnce && !self.isLoadingMore else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let peerId = self.peerId
|
||||||
|
let revoked = self.revoked
|
||||||
|
let invitations = Array(self.results.prefix(50))
|
||||||
|
let canLoadMore = self.canLoadMore
|
||||||
|
let count = self.count
|
||||||
|
self.updateDisposable.set(self.account.postbox.transaction({ transaction in
|
||||||
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerExportedInvitations, key: CachedPeerExportedInvitations.key(peerId: peerId, revoked: revoked)), entry: CachedPeerExportedInvitations(invitations: invitations, canLoadMore: canLoadMore, count: count), collectionSpec: cachedPeerExportedInvitationsCollectionSpec)
|
||||||
|
}).start())
|
||||||
|
}
|
||||||
|
|
||||||
private func updateState() {
|
private func updateState() {
|
||||||
self.state.set(.single(PeerExportedInvitationsState(invitations: self.results, isLoadingMore: self.isLoadingMore, hasLoadedOnce: self.hasLoadedOnce, canLoadMore: self.canLoadMore, count: self.count)))
|
self.state.set(.single(PeerExportedInvitationsState(invitations: self.results, isLoadingMore: self.isLoadingMore, hasLoadedOnce: self.hasLoadedOnce, canLoadMore: self.canLoadMore, count: self.count)))
|
||||||
}
|
}
|
||||||
@ -426,18 +516,48 @@ public final class PeerExportedInvitationsContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(account: Account, peerId: PeerId, invite: ExportedInvitation) {
|
public init(account: Account, peerId: PeerId, revoked: Bool, forceUpdate: Bool) {
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
return PeerExportedInvitationsContextImpl(queue: queue, account: account, peerId: peerId)
|
return PeerExportedInvitationsContextImpl(queue: queue, account: account, peerId: peerId, revoked: revoked, forceUpdate: forceUpdate)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func reload() {
|
||||||
|
self.impl.with { impl in
|
||||||
|
impl.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func loadMore() {
|
public func loadMore() {
|
||||||
self.impl.with { impl in
|
self.impl.with { impl in
|
||||||
impl.loadMore()
|
impl.loadMore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func add(_ invite: ExportedInvitation) {
|
||||||
|
self.impl.with { impl in
|
||||||
|
impl.add(invite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(_ invite: ExportedInvitation) {
|
||||||
|
self.impl.with { impl in
|
||||||
|
impl.update(invite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func remove(_ invite: ExportedInvitation) {
|
||||||
|
self.impl.with { impl in
|
||||||
|
impl.remove(invite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func clear() {
|
||||||
|
self.impl.with { impl in
|
||||||
|
impl.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -521,6 +641,7 @@ private final class PeerInvitationImportersContextImpl {
|
|||||||
private var isLoadingMore: Bool = false
|
private var isLoadingMore: Bool = false
|
||||||
private var hasLoadedOnce: Bool = false
|
private var hasLoadedOnce: Bool = false
|
||||||
private var canLoadMore: Bool = true
|
private var canLoadMore: Bool = true
|
||||||
|
private var loadedFromCache = false
|
||||||
private var results: [PeerInvitationImportersState.Importer] = []
|
private var results: [PeerInvitationImportersState.Importer] = []
|
||||||
private var count: Int32
|
private var count: Int32
|
||||||
private var populateCache: Bool = true
|
private var populateCache: Bool = true
|
||||||
@ -562,6 +683,7 @@ private final class PeerInvitationImportersContextImpl {
|
|||||||
strongSelf.results = cachedPeers
|
strongSelf.results = cachedPeers
|
||||||
strongSelf.hasLoadedOnce = true
|
strongSelf.hasLoadedOnce = true
|
||||||
strongSelf.canLoadMore = canLoadMore
|
strongSelf.canLoadMore = canLoadMore
|
||||||
|
strongSelf.loadedFromCache = true
|
||||||
}
|
}
|
||||||
strongSelf.loadMore()
|
strongSelf.loadMore()
|
||||||
}))
|
}))
|
||||||
@ -581,8 +703,14 @@ private final class PeerInvitationImportersContextImpl {
|
|||||||
let account = self.account
|
let account = self.account
|
||||||
let peerId = self.peerId
|
let peerId = self.peerId
|
||||||
let link = self.link
|
let link = self.link
|
||||||
let lastResult = self.results.last
|
|
||||||
let populateCache = self.populateCache
|
let populateCache = self.populateCache
|
||||||
|
|
||||||
|
var lastResult = self.results.last
|
||||||
|
if self.loadedFromCache {
|
||||||
|
self.loadedFromCache = false
|
||||||
|
lastResult = nil
|
||||||
|
}
|
||||||
|
|
||||||
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,26 @@ import SwiftSignalKit
|
|||||||
|
|
||||||
import SyncCore
|
import SyncCore
|
||||||
|
|
||||||
|
private struct SearchStickersConfiguration {
|
||||||
|
static var defaultValue: SearchStickersConfiguration {
|
||||||
|
return SearchStickersConfiguration(cacheTimeout: 86400)
|
||||||
|
}
|
||||||
|
|
||||||
|
public let cacheTimeout: Int32
|
||||||
|
|
||||||
|
fileprivate init(cacheTimeout: Int32) {
|
||||||
|
self.cacheTimeout = cacheTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
static func with(appConfiguration: AppConfiguration) -> SearchStickersConfiguration {
|
||||||
|
if let data = appConfiguration.data, let value = data["stickers_emoji_cache_time"] as? Int32 {
|
||||||
|
return SearchStickersConfiguration(cacheTimeout: value)
|
||||||
|
} else {
|
||||||
|
return .defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class FoundStickerItem: Equatable {
|
public final class FoundStickerItem: Equatable {
|
||||||
public let file: TelegramMediaFile
|
public let file: TelegramMediaFile
|
||||||
public let stringRepresentations: [String]
|
public let stringRepresentations: [String]
|
||||||
@ -140,7 +160,15 @@ public func searchStickers(account: Account, query: String, scope: SearchSticker
|
|||||||
result.append(contentsOf: installedItems)
|
result.append(contentsOf: installedItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query))) as? CachedStickerQueryResult
|
var cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query))) as? CachedStickerQueryResult
|
||||||
|
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
|
||||||
|
let searchStickersConfiguration = SearchStickersConfiguration.with(appConfiguration: appConfiguration)
|
||||||
|
|
||||||
|
if let currentCached = cached, currentTime > currentCached.timestamp + searchStickersConfiguration.cacheTimeout {
|
||||||
|
cached = nil
|
||||||
|
}
|
||||||
|
|
||||||
return (result, cached)
|
return (result, cached)
|
||||||
} |> mapToSignal { localItems, cached -> Signal<[FoundStickerItem], NoError> in
|
} |> mapToSignal { localItems, cached -> Signal<[FoundStickerItem], NoError> in
|
||||||
@ -199,7 +227,8 @@ public func searchStickers(account: Account, query: String, scope: SearchSticker
|
|||||||
result.append(contentsOf: animatedItems)
|
result.append(contentsOf: animatedItems)
|
||||||
result.append(contentsOf: items)
|
result.append(contentsOf: items)
|
||||||
|
|
||||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query)), entry: CachedStickerQueryResult(items: files, hash: hash), collectionSpec: collectionSpec)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerQueryResults, key: CachedStickerQueryResult.cacheKey(query)), entry: CachedStickerQueryResult(items: files, hash: hash, timestamp: currentTime), collectionSpec: collectionSpec)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
case .stickersNotModified:
|
case .stickersNotModified:
|
||||||
|
@ -6,6 +6,7 @@ import SyncCore
|
|||||||
|
|
||||||
public enum ServerProvidedSuggestion: String {
|
public enum ServerProvidedSuggestion: String {
|
||||||
case autoarchivePopular = "AUTOARCHIVE_POPULAR"
|
case autoarchivePopular = "AUTOARCHIVE_POPULAR"
|
||||||
|
case newcomerTicks = "NEWCOMER_TICKS"
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getServerProvidedSuggestions(postbox: Postbox) -> Signal<[ServerProvidedSuggestion], NoError> {
|
public func getServerProvidedSuggestions(postbox: Postbox) -> Signal<[ServerProvidedSuggestion], NoError> {
|
||||||
@ -22,12 +23,7 @@ public func getServerProvidedSuggestions(postbox: Postbox) -> Signal<[ServerProv
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return list.compactMap { item -> ServerProvidedSuggestion? in
|
return list.compactMap { item -> ServerProvidedSuggestion? in
|
||||||
switch item {
|
return ServerProvidedSuggestion(rawValue: item)
|
||||||
case "AUTOARCHIVE_POPULAR":
|
|
||||||
return .autoarchivePopular
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
@ -205,10 +205,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
|
|
||||||
let peerStatusSettings = PeerStatusSettings(apiSettings: userFull.settings)
|
let peerStatusSettings = PeerStatusSettings(apiSettings: userFull.settings)
|
||||||
|
|
||||||
var hasScheduledMessages = false
|
let hasScheduledMessages = (userFull.flags & 1 << 12) != 0
|
||||||
if (userFull.flags & 1 << 12) != 0 {
|
|
||||||
hasScheduledMessages = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
|
return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -305,6 +305,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private var applicationInForegroundDisposable: Disposable?
|
private var applicationInForegroundDisposable: Disposable?
|
||||||
private var applicationInFocusDisposable: Disposable?
|
private var applicationInFocusDisposable: Disposable?
|
||||||
|
|
||||||
|
private let checksTooltipDisposable = MetaDisposable()
|
||||||
|
private var shouldDisplayChecksTooltip = false
|
||||||
|
|
||||||
private var checkedPeerChatServiceActions = false
|
private var checkedPeerChatServiceActions = false
|
||||||
|
|
||||||
private var willAppear = false
|
private var willAppear = false
|
||||||
@ -3424,6 +3427,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.peekTimerDisposable.dispose()
|
self.peekTimerDisposable.dispose()
|
||||||
self.hasActiveGroupCallDisposable?.dispose()
|
self.hasActiveGroupCallDisposable?.dispose()
|
||||||
self.createVoiceChatDisposable.dispose()
|
self.createVoiceChatDisposable.dispose()
|
||||||
|
self.checksTooltipDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
|
public func updatePresentationMode(_ mode: ChatControllerPresentationMode) {
|
||||||
@ -4761,7 +4765,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let editingMessage = strongSelf.editingMessage
|
|
||||||
let text = trimChatInputText(convertMarkdownToAttributes(editMessage.inputState.inputText))
|
let text = trimChatInputText(convertMarkdownToAttributes(editMessage.inputState.inputText))
|
||||||
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
||||||
var entitiesAttribute: TextEntitiesMessageAttribute?
|
var entitiesAttribute: TextEntitiesMessageAttribute?
|
||||||
@ -6236,7 +6239,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.openScheduledMessages()
|
strongSelf.openScheduledMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
// strongSelf.displayChecksTooltip()
|
if strongSelf.shouldDisplayChecksTooltip {
|
||||||
|
strongSelf.displayChecksTooltip()
|
||||||
|
strongSelf.shouldDisplayChecksTooltip = false
|
||||||
|
strongSelf.checksTooltipDisposable.set(dismissServerProvidedSuggestion(account: strongSelf.context.account, suggestion: .newcomerTicks).start())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -6482,7 +6489,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strongSelf.context.sharedContext.currentMediaInputSettings.with { $0.enableRaiseToSpeak } {
|
if !strongSelf.context.sharedContext.currentMediaInputSettings.with({ $0.enableRaiseToSpeak }) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6707,6 +6714,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.checksTooltipDisposable.set((getServerProvidedSuggestions(postbox: self.context.account.postbox)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] values in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !values.contains(.newcomerTicks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.shouldDisplayChecksTooltip = true
|
||||||
|
}))
|
||||||
|
|
||||||
if self.scheduledActivateInput {
|
if self.scheduledActivateInput {
|
||||||
self.scheduledActivateInput = false
|
self.scheduledActivateInput = false
|
||||||
|
|
||||||
@ -7481,7 +7499,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|> deliverOnMainQueue).start(next: { [weak self, weak controller] result in
|
|> deliverOnMainQueue).start(next: { [weak self, weak controller] result in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
|
|
||||||
guard let strongSelf = self, case let .result(stats) = result, var categories = stats.media[peer.id] else {
|
guard let strongSelf = self, case let .result(stats) = result, let categories = stats.media[peer.id] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
@ -7682,16 +7700,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func editMessageMediaWithMessages(_ messages: [EnqueueMessage]) {
|
private func editMessageMediaWithMessages(_ messages: [EnqueueMessage]) {
|
||||||
if let message = messages.first, case let .message(desc) = message, let mediaReference = desc.mediaReference {
|
if let message = messages.first, case let .message(text, _, maybeMediaReference, _, _) = message, let mediaReference = maybeMediaReference {
|
||||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||||
var state = state
|
var state = state
|
||||||
if let editMessageState = state.editMessageState, case let .media(options) = editMessageState.content, !options.isEmpty {
|
if let editMessageState = state.editMessageState, case let .media(options) = editMessageState.content, !options.isEmpty {
|
||||||
state = state.updatedEditMessageState(ChatEditInterfaceMessageState(content: editMessageState.content, mediaReference: mediaReference))
|
state = state.updatedEditMessageState(ChatEditInterfaceMessageState(content: editMessageState.content, mediaReference: mediaReference))
|
||||||
}
|
}
|
||||||
if !desc.text.isEmpty {
|
if !text.isEmpty {
|
||||||
state = state.updatedInterfaceState { state in
|
state = state.updatedInterfaceState { state in
|
||||||
if let editMessage = state.editMessage {
|
if let editMessage = state.editMessage {
|
||||||
return state.withUpdatedEditMessage(editMessage.withUpdatedInputState(ChatTextInputState(inputText: NSAttributedString(string: desc.text))))
|
return state.withUpdatedEditMessage(editMessage.withUpdatedInputState(ChatTextInputState(inputText: NSAttributedString(string: text))))
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@ -9338,7 +9356,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let complete = results.completed
|
|
||||||
var navigateIndex: MessageIndex?
|
var navigateIndex: MessageIndex?
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in
|
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in
|
||||||
if let data = current.search {
|
if let data = current.search {
|
||||||
@ -10111,8 +10128,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
case let .withBotStartPayload(botStart):
|
case let .withBotStartPayload(botStart):
|
||||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart))
|
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(peerId), botStart: botStart))
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -10532,7 +10547,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private func openUrl(_ url: String, concealed: Bool, message: Message? = nil) {
|
private func openUrl(_ url: String, concealed: Bool, message: Message? = nil) {
|
||||||
self.commitPurposefulAction()
|
self.commitPurposefulAction()
|
||||||
|
|
||||||
self.presentVoiceMessageDiscardAlert(action: {
|
let _ = self.presentVoiceMessageDiscardAlert(action: {
|
||||||
if self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback {
|
if self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback {
|
||||||
if url.hasSuffix(".m3u8") {
|
if url.hasSuffix(".m3u8") {
|
||||||
let navigationController = self.navigationController as? NavigationController
|
let navigationController = self.navigationController as? NavigationController
|
||||||
|
@ -159,6 +159,7 @@ final class PeerInfoScreenData {
|
|||||||
let members: PeerInfoMembersData?
|
let members: PeerInfoMembersData?
|
||||||
let encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
let encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||||
let globalSettings: TelegramGlobalSettings?
|
let globalSettings: TelegramGlobalSettings?
|
||||||
|
let invitations: PeerExportedInvitationsState?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
peer: Peer?,
|
peer: Peer?,
|
||||||
@ -172,7 +173,8 @@ final class PeerInfoScreenData {
|
|||||||
linkedDiscussionPeer: Peer?,
|
linkedDiscussionPeer: Peer?,
|
||||||
members: PeerInfoMembersData?,
|
members: PeerInfoMembersData?,
|
||||||
encryptionKeyFingerprint: SecretChatKeyFingerprint?,
|
encryptionKeyFingerprint: SecretChatKeyFingerprint?,
|
||||||
globalSettings: TelegramGlobalSettings?
|
globalSettings: TelegramGlobalSettings?,
|
||||||
|
invitations: PeerExportedInvitationsState?
|
||||||
) {
|
) {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.cachedData = cachedData
|
self.cachedData = cachedData
|
||||||
@ -186,6 +188,7 @@ final class PeerInfoScreenData {
|
|||||||
self.members = members
|
self.members = members
|
||||||
self.encryptionKeyFingerprint = encryptionKeyFingerprint
|
self.encryptionKeyFingerprint = encryptionKeyFingerprint
|
||||||
self.globalSettings = globalSettings
|
self.globalSettings = globalSettings
|
||||||
|
self.invitations = invitations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +445,8 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, account
|
|||||||
linkedDiscussionPeer: nil,
|
linkedDiscussionPeer: nil,
|
||||||
members: nil,
|
members: nil,
|
||||||
encryptionKeyFingerprint: nil,
|
encryptionKeyFingerprint: nil,
|
||||||
globalSettings: globalSettings
|
globalSettings: globalSettings,
|
||||||
|
invitations: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,7 +468,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
linkedDiscussionPeer: nil,
|
linkedDiscussionPeer: nil,
|
||||||
members: nil,
|
members: nil,
|
||||||
encryptionKeyFingerprint: nil,
|
encryptionKeyFingerprint: nil,
|
||||||
globalSettings: nil
|
globalSettings: nil,
|
||||||
|
invitations: nil
|
||||||
))
|
))
|
||||||
case let .user(userPeerId, secretChatId, kind):
|
case let .user(userPeerId, secretChatId, kind):
|
||||||
let groupsInCommon: GroupsInCommonContext?
|
let groupsInCommon: GroupsInCommonContext?
|
||||||
@ -603,7 +608,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
linkedDiscussionPeer: nil,
|
linkedDiscussionPeer: nil,
|
||||||
members: nil,
|
members: nil,
|
||||||
encryptionKeyFingerprint: encryptionKeyFingerprint,
|
encryptionKeyFingerprint: encryptionKeyFingerprint,
|
||||||
globalSettings: nil
|
globalSettings: nil,
|
||||||
|
invitations: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .channel:
|
case .channel:
|
||||||
@ -623,13 +629,19 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([PreferencesKeys.globalNotifications]))
|
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([PreferencesKeys.globalNotifications]))
|
||||||
var combinedKeys: [PostboxViewKey] = []
|
var combinedKeys: [PostboxViewKey] = []
|
||||||
combinedKeys.append(globalNotificationsKey)
|
combinedKeys.append(globalNotificationsKey)
|
||||||
|
|
||||||
|
let invitationsContextPromise = Promise<PeerExportedInvitationsContext?>(nil)
|
||||||
|
let invitationsStatePromise = Promise<PeerExportedInvitationsState?>(nil)
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId),
|
peerInfoAvailableMediaPanes(context: context, peerId: peerId),
|
||||||
context.account.postbox.combinedView(keys: combinedKeys),
|
context.account.postbox.combinedView(keys: combinedKeys),
|
||||||
status
|
status,
|
||||||
|
invitationsContextPromise.get(),
|
||||||
|
invitationsStatePromise.get()
|
||||||
)
|
)
|
||||||
|> map { peerView, availablePanes, combinedView, status -> PeerInfoScreenData in
|
|> map { peerView, availablePanes, combinedView, status, currentInvitationsContext, invitations -> PeerInfoScreenData in
|
||||||
var globalNotificationSettings: GlobalNotificationSettings = .defaultSettings
|
var globalNotificationSettings: GlobalNotificationSettings = .defaultSettings
|
||||||
if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView {
|
if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView {
|
||||||
if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings {
|
if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings {
|
||||||
@ -642,6 +654,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
discussionPeer = peer
|
discussionPeer = peer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)), currentInvitationsContext == nil {
|
||||||
|
let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||||
|
invitationsContextPromise.set(.single(invitationsContext))
|
||||||
|
invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||||
|
}
|
||||||
|
|
||||||
return PeerInfoScreenData(
|
return PeerInfoScreenData(
|
||||||
peer: peerView.peers[peerId],
|
peer: peerView.peers[peerId],
|
||||||
cachedData: peerView.cachedData,
|
cachedData: peerView.cachedData,
|
||||||
@ -654,7 +672,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
linkedDiscussionPeer: discussionPeer,
|
linkedDiscussionPeer: discussionPeer,
|
||||||
members: nil,
|
members: nil,
|
||||||
encryptionKeyFingerprint: nil,
|
encryptionKeyFingerprint: nil,
|
||||||
globalSettings: nil
|
globalSettings: nil,
|
||||||
|
invitations: invitations
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case let .group(groupId):
|
case let .group(groupId):
|
||||||
@ -751,14 +770,20 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([PreferencesKeys.globalNotifications]))
|
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([PreferencesKeys.globalNotifications]))
|
||||||
var combinedKeys: [PostboxViewKey] = []
|
var combinedKeys: [PostboxViewKey] = []
|
||||||
combinedKeys.append(globalNotificationsKey)
|
combinedKeys.append(globalNotificationsKey)
|
||||||
|
|
||||||
|
let invitationsContextPromise = Promise<PeerExportedInvitationsContext?>(nil)
|
||||||
|
let invitationsStatePromise = Promise<PeerExportedInvitationsState?>(nil)
|
||||||
|
|
||||||
return combineLatest(queue: .mainQueue(),
|
return combineLatest(queue: .mainQueue(),
|
||||||
context.account.viewTracker.peerView(groupId, updateData: true),
|
context.account.viewTracker.peerView(groupId, updateData: true),
|
||||||
peerInfoAvailableMediaPanes(context: context, peerId: groupId),
|
peerInfoAvailableMediaPanes(context: context, peerId: groupId),
|
||||||
context.account.postbox.combinedView(keys: combinedKeys),
|
context.account.postbox.combinedView(keys: combinedKeys),
|
||||||
status,
|
status,
|
||||||
membersData
|
membersData,
|
||||||
|
invitationsContextPromise.get(),
|
||||||
|
invitationsStatePromise.get()
|
||||||
)
|
)
|
||||||
|> map { peerView, availablePanes, combinedView, status, membersData -> PeerInfoScreenData in
|
|> map { peerView, availablePanes, combinedView, status, membersData, currentInvitationsContext, invitations -> PeerInfoScreenData in
|
||||||
var globalNotificationSettings: GlobalNotificationSettings = .defaultSettings
|
var globalNotificationSettings: GlobalNotificationSettings = .defaultSettings
|
||||||
if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView {
|
if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView {
|
||||||
if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings {
|
if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings {
|
||||||
@ -780,6 +805,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let group = peerViewMainPeer(peerView) as? TelegramGroup, case .creator = group.role, currentInvitationsContext == nil {
|
||||||
|
let invitationsContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, revoked: false, forceUpdate: true)
|
||||||
|
invitationsContextPromise.set(.single(invitationsContext))
|
||||||
|
invitationsStatePromise.set(invitationsContext.state |> map(Optional.init))
|
||||||
|
}
|
||||||
|
|
||||||
return PeerInfoScreenData(
|
return PeerInfoScreenData(
|
||||||
peer: peerView.peers[groupId],
|
peer: peerView.peers[groupId],
|
||||||
cachedData: peerView.cachedData,
|
cachedData: peerView.cachedData,
|
||||||
@ -792,7 +823,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
linkedDiscussionPeer: discussionPeer,
|
linkedDiscussionPeer: discussionPeer,
|
||||||
members: membersData,
|
members: membersData,
|
||||||
encryptionKeyFingerprint: nil,
|
encryptionKeyFingerprint: nil,
|
||||||
globalSettings: nil
|
globalSettings: nil,
|
||||||
|
invitations: invitations
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1255,7 +1255,14 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
interaction.editingOpenPublicLinkSetup()
|
interaction.editingOpenPublicLinkSetup()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(""), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
let invitesText: String
|
||||||
|
if let count = data.invitations?.count, count > 0 {
|
||||||
|
invitesText = "\(count)"
|
||||||
|
} else {
|
||||||
|
invitesText = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||||
interaction.editingOpenInviteLinksSetup()
|
interaction.editingOpenInviteLinksSetup()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -1330,7 +1337,14 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(""), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
let invitesText: String
|
||||||
|
if let count = data.invitations?.count, count > 0 {
|
||||||
|
invitesText = "\(count)"
|
||||||
|
} else {
|
||||||
|
invitesText = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, action: {
|
||||||
interaction.editingOpenInviteLinksSetup()
|
interaction.editingOpenInviteLinksSetup()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -4544,6 +4558,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
strongSelf.view.endEditing(true)
|
||||||
let mode: ChannelVisibilityControllerMode
|
let mode: ChannelVisibilityControllerMode
|
||||||
if groupPeer.addressName != nil {
|
if groupPeer.addressName != nil {
|
||||||
let visibilityController = channelVisibilityController(context: strongSelf.context, peerId: groupPeer.id, mode: .generic, upgradedToSupergroup: { _, f in f() }, onDismissRemoveController: contactsController)
|
let visibilityController = channelVisibilityController(context: strongSelf.context, peerId: groupPeer.id, mode: .generic, upgradedToSupergroup: { _, f in f() }, onDismissRemoveController: contactsController)
|
||||||
|
@ -91,8 +91,8 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
|
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
|
||||||
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
|
if case let .file(id, _, _, _, _, _, slug, _, settings) = presentationTheme.chat.defaultWallpaper, id == 0 {
|
||||||
resolvedWallpaper = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|
resolvedWallpaper = cachedWallpaper(account: account, slug: slug, settings: settings)
|
||||||
|> map { wallpaper in
|
|> map { wallpaper in
|
||||||
return wallpaper?.wallpaper
|
return wallpaper?.wallpaper
|
||||||
}
|
}
|
||||||
@ -102,15 +102,15 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|
|||||||
|
|
||||||
return resolvedWallpaper
|
return resolvedWallpaper
|
||||||
|> mapToSignal { wallpaper -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
|
|> mapToSignal { wallpaper -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
|
||||||
if let wallpaper = wallpaper, case let .file(file) = wallpaper {
|
if let wallpaper = wallpaper, case let .file(_, _, _, _, _, _, slug, file, _) = wallpaper {
|
||||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
|
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(slug), resource: file.resource)))
|
||||||
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|
||||||
|> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
|
|> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
|
||||||
guard complete, let fullSizeData = fullSizeData else {
|
guard complete, let fullSizeData = fullSizeData else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData, synchronous: true)
|
accountManager.mediaBox.storeResourceData(file.resource.id, data: fullSizeData, synchronous: true)
|
||||||
return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper, creatorAccountId: theme.isCreator ? account.id : nil)), presentationTheme))
|
return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper, creatorAccountId: theme.isCreator ? account.id : nil)), presentationTheme))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user