Various fixes

This commit is contained in:
Ilya Laktyushin 2023-01-19 17:00:41 +04:00
parent ab9d4748ec
commit 587d700978
5 changed files with 232 additions and 20 deletions

View File

@ -8660,7 +8660,7 @@ Sorry for the inconvenience.";
"RequestPeer.Requirement.Group.Rights.Delete" = "delete messages"; "RequestPeer.Requirement.Group.Rights.Delete" = "delete messages";
"RequestPeer.Requirement.Group.Rights.Edit" = "edit messages"; "RequestPeer.Requirement.Group.Rights.Edit" = "edit messages";
"RequestPeer.Requirement.Group.Rights.Ban" = "ban users"; "RequestPeer.Requirement.Group.Rights.Ban" = "ban users";
"RequestPeer.Requirement.Group.Rights.Invite" = "invite users via link"; "RequestPeer.Requirement.Group.Rights.Invite" = "add members";
"RequestPeer.Requirement.Group.Rights.Pin" = "pin messages"; "RequestPeer.Requirement.Group.Rights.Pin" = "pin messages";
"RequestPeer.Requirement.Group.Rights.Topics" = "manage topics"; "RequestPeer.Requirement.Group.Rights.Topics" = "manage topics";
"RequestPeer.Requirement.Group.Rights.VideoChats" = "manage video chats"; "RequestPeer.Requirement.Group.Rights.VideoChats" = "manage video chats";

View File

@ -461,7 +461,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
} }
} }
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ListViewItem { public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, requestPeerType: ReplyMarkupButtonRequestPeerType?, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ListViewItem {
switch self { switch self {
case let .topic(peer, threadInfo, _, theme, strings, expandType): case let .topic(peer, threadInfo, _, theme, strings, expandType):
let actionTitle: String? let actionTitle: String?
@ -621,7 +621,11 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
if filter.contains(.onlyGroups) { if filter.contains(.onlyGroups) {
headerType = .chats headerType = .chats
} else { } else {
headerType = .localPeers if let _ = requestPeerType {
headerType = .chats
} else {
headerType = .localPeers
}
} }
header = ChatListSearchItemHeader(type: headerType, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { header = ChatListSearchItemHeader(type: headerType, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : {
toggleExpandLocalResults() toggleExpandLocalResults()
@ -826,12 +830,12 @@ private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates) return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates)
} }
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ChatListSearchContainerTransition { public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, requestPeerType: ReplyMarkupButtonRequestPeerType?, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ChatListSearchContainerTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused), directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused), directionHint: nil) }
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, animated: animated) return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, animated: animated)
} }
@ -2269,7 +2273,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let animated = selectionChanged || expandGlobalSearchChanged let animated = selectionChanged || expandGlobalSearchChanged
let firstTime = previousEntries == nil let firstTime = previousEntries == nil
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags != nil, isEmpty: !isSearching && (entriesAndFlags?.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, location: location, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags != nil, isEmpty: !isSearching && (entriesAndFlags?.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, requestPeerType: requestPeerType, location: location, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in
interaction.peerContextAction?(message, node, rect, gesture, location) interaction.peerContextAction?(message, node, rect, gesture, location)
}, toggleExpandLocalResults: { }, toggleExpandLocalResults: {
guard let strongSelf = self else { guard let strongSelf = self else {

View File

@ -113,7 +113,7 @@ public final class HashtagSearchController: TelegramBaseController {
}) })
let firstTime = previousEntries == nil let firstTime = previousEntries == nil
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, isEmpty: entries.isEmpty, isLoading: false, animated: false, context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], location: .chatList(groupId: .root), key: .chats, tagMask: nil, interaction: interaction, listInteraction: listInteraction, peerContextAction: nil, toggleExpandLocalResults: { let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, isEmpty: entries.isEmpty, isLoading: false, animated: false, context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], requestPeerType: nil, location: .chatList(groupId: .root), key: .chats, tagMask: nil, interaction: interaction, listInteraction: listInteraction, peerContextAction: nil, toggleExpandLocalResults: {
}, toggleExpandGlobalResults: { }, toggleExpandGlobalResults: {
}, searchPeer: { _ in }, searchPeer: { _ in
}, searchQuery: "", searchOptions: nil, messageContextAction: nil, openClearRecentlyDownloaded: {}, toggleAllPaused: {}) }, searchQuery: "", searchOptions: nil, messageContextAction: nil, openClearRecentlyDownloaded: {}, toggleAllPaused: {})

View File

@ -17,6 +17,7 @@ import WebSearchUI
import PeerInfoUI import PeerInfoUI
import MapResourceToAvatarSizes import MapResourceToAvatarSizes
import LegacyMediaPickerUI import LegacyMediaPickerUI
import TextFormat
private struct CreateChannelArguments { private struct CreateChannelArguments {
let context: AccountContext let context: AccountContext
@ -26,11 +27,14 @@ private struct CreateChannelArguments {
let done: () -> Void let done: () -> Void
let changeProfilePhoto: () -> Void let changeProfilePhoto: () -> Void
let focusOnDescription: () -> Void let focusOnDescription: () -> Void
let updatePublicLinkText: (String) -> Void
let openAuction: (String) -> Void
} }
private enum CreateChannelSection: Int32 { private enum CreateChannelSection: Int32 {
case info case info
case description case description
case username
} }
private enum CreateChannelEntryTag: ItemListItemTag { private enum CreateChannelEntryTag: ItemListItemTag {
@ -62,16 +66,23 @@ private enum CreateChannelEntryTag: ItemListItemTag {
private enum CreateChannelEntry: ItemListNodeEntry { private enum CreateChannelEntry: ItemListNodeEntry {
case channelInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?) case channelInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
case setProfilePhoto(PresentationTheme, String) case setProfilePhoto(PresentationTheme, String)
case descriptionSetup(PresentationTheme, String, String) case descriptionSetup(PresentationTheme, String, String)
case descriptionInfo(PresentationTheme, String) case descriptionInfo(PresentationTheme, String)
case usernameHeader(PresentationTheme, String)
case username(PresentationTheme, String, String)
case usernameStatus(PresentationTheme, String, AddressNameValidationStatus, String, String)
case usernameInfo(PresentationTheme, String)
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .channelInfo, .setProfilePhoto: case .channelInfo, .setProfilePhoto:
return CreateChannelSection.info.rawValue return CreateChannelSection.info.rawValue
case .descriptionSetup, .descriptionInfo: case .descriptionSetup, .descriptionInfo:
return CreateChannelSection.description.rawValue return CreateChannelSection.description.rawValue
case .usernameHeader, .username, .usernameStatus, .usernameInfo:
return CreateChannelSection.username.rawValue
} }
} }
@ -85,6 +96,14 @@ private enum CreateChannelEntry: ItemListNodeEntry {
return 2 return 2
case .descriptionInfo: case .descriptionInfo:
return 3 return 3
case .usernameHeader:
return 4
case .username:
return 5
case .usernameStatus:
return 6
case .usernameInfo:
return 7
} }
} }
@ -136,6 +155,30 @@ private enum CreateChannelEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .usernameHeader(lhsTheme, lhsText):
if case let .usernameHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .username(lhsTheme, lhsText, lhsValue):
if case let .username(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .usernameStatus(lhsTheme, lhsAddressName, lhsStatus, lhsText, lhsUsername):
if case let .usernameStatus(rhsTheme, rhsAddressName, rhsStatus, rhsText, rhsUsername) = rhs, lhsTheme === rhsTheme, lhsAddressName == rhsAddressName, lhsStatus == rhsStatus, lhsText == rhsText, lhsUsername == rhsUsername {
return true
} else {
return false
}
case let .usernameInfo(lhsTheme, lhsText):
if case let .usernameInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
} }
} }
@ -164,6 +207,37 @@ private enum CreateChannelEntry: ItemListNodeEntry {
}, tag: CreateChannelEntryTag.description) }, tag: CreateChannelEntryTag.description)
case let .descriptionInfo(_, text): case let .descriptionInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .usernameHeader(_, title):
return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
case let .username(theme, placeholder, text):
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .username, clearType: .always, tag: nil, sectionId: self.section, textUpdated: { updatedText in
arguments.updatePublicLinkText(updatedText)
}, action: {
})
case let .usernameStatus(_, _, status, text, username):
var displayActivity = false
let textColor: ItemListActivityTextItem.TextColor
switch status {
case .invalidFormat:
textColor = .destructive
case let .availability(availability):
switch availability {
case .available:
textColor = .constructive
case .purchaseAvailable:
textColor = .generic
case .invalid, .taken:
textColor = .destructive
}
case .checking:
textColor = .generic
displayActivity = true
}
return ItemListActivityTextItem(displayActivity: displayActivity, presentationData: presentationData, text: text, color: textColor, linkAction: { _ in
arguments.openAuction(username)
}, sectionId: self.section)
case let .usernameInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
} }
} }
} }
@ -173,6 +247,8 @@ private struct CreateChannelState: Equatable {
var editingName: ItemListAvatarAndNameInfoItemName var editingName: ItemListAvatarAndNameInfoItemName
var editingDescriptionText: String var editingDescriptionText: String
var avatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? var avatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?
var editingPublicLinkText: String?
var addressNameValidationStatus: AddressNameValidationStatus?
static func ==(lhs: CreateChannelState, rhs: CreateChannelState) -> Bool { static func ==(lhs: CreateChannelState, rhs: CreateChannelState) -> Bool {
if lhs.creating != rhs.creating { if lhs.creating != rhs.creating {
@ -187,11 +263,17 @@ private struct CreateChannelState: Equatable {
if lhs.avatar != rhs.avatar { if lhs.avatar != rhs.avatar {
return false return false
} }
if lhs.editingPublicLinkText != rhs.editingPublicLinkText {
return false
}
if lhs.addressNameValidationStatus != rhs.addressNameValidationStatus {
return false
}
return true return true
} }
} }
private func CreateChannelEntries(presentationData: PresentationData, state: CreateChannelState) -> [CreateChannelEntry] { private func CreateChannelEntries(presentationData: PresentationData, state: CreateChannelState, requestPeer: ReplyMarkupButtonRequestPeerType.Channel?) -> [CreateChannelEntry] {
var entries: [CreateChannelEntry] = [] var entries: [CreateChannelEntry] = []
let groupInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingName, updatingName: nil) let groupInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingName, updatingName: nil)
@ -203,6 +285,55 @@ private func CreateChannelEntries(presentationData: PresentationData, state: Cre
entries.append(.descriptionSetup(presentationData.theme, presentationData.strings.Channel_Edit_AboutItem, state.editingDescriptionText)) entries.append(.descriptionSetup(presentationData.theme, presentationData.strings.Channel_Edit_AboutItem, state.editingDescriptionText))
entries.append(.descriptionInfo(presentationData.theme, presentationData.strings.Channel_About_Help)) entries.append(.descriptionInfo(presentationData.theme, presentationData.strings.Channel_About_Help))
if let requestPeer {
if let hasUsername = requestPeer.hasUsername, hasUsername {
let currentUsername = state.editingPublicLinkText ?? ""
entries.append(.usernameHeader(presentationData.theme, presentationData.strings.CreateGroup_PublicLinkTitle.uppercased()))
entries.append(.username(presentationData.theme, presentationData.strings.Group_PublicLink_Placeholder, currentUsername))
if let status = state.addressNameValidationStatus {
let statusText: String
switch status {
case let .invalidFormat(error):
switch error {
case .startsWithDigit:
statusText = presentationData.strings.Username_InvalidStartsWithNumber
case .startsWithUnderscore:
statusText = presentationData.strings.Username_InvalidStartsWithUnderscore
case .endsWithUnderscore:
statusText = presentationData.strings.Username_InvalidEndsWithUnderscore
case .invalidCharacters:
statusText = presentationData.strings.Username_InvalidCharacters
case .tooShort:
statusText = presentationData.strings.Username_InvalidTooShort
}
case let .availability(availability):
switch availability {
case .available:
statusText = presentationData.strings.Username_UsernameIsAvailable(currentUsername).string
case .invalid:
statusText = presentationData.strings.Username_InvalidCharacters
case .taken:
statusText = presentationData.strings.Username_InvalidTaken
case .purchaseAvailable:
var markdownString = presentationData.strings.Username_UsernamePurchaseAvailable
let entities = generateTextEntities(markdownString, enabledTypes: [.mention])
if let entity = entities.first {
markdownString.insert(contentsOf: "]()", at: markdownString.index(markdownString.startIndex, offsetBy: entity.range.upperBound))
markdownString.insert(contentsOf: "[", at: markdownString.index(markdownString.startIndex, offsetBy: entity.range.lowerBound))
}
statusText = markdownString
}
case .checking:
statusText = presentationData.strings.Username_CheckingUsername
}
entries.append(.usernameStatus(presentationData.theme, currentUsername, status, statusText, currentUsername))
}
entries.append(.usernameInfo(presentationData.theme, presentationData.strings.CreateGroup_PublicLinkInfo))
}
}
return entries return entries
} }
@ -222,6 +353,7 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
var replaceControllerImpl: ((ViewController) -> Void)? var replaceControllerImpl: ((ViewController) -> Void)?
var pushControllerImpl: ((ViewController) -> Void)? var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)? var presentControllerImpl: ((ViewController, Any?) -> Void)?
var dismissImpl: (() -> Void)?
var endEditingImpl: (() -> Void)? var endEditingImpl: (() -> Void)?
var focusOnDescriptionImpl: (() -> Void)? var focusOnDescriptionImpl: (() -> Void)?
@ -232,6 +364,14 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
let uploadedAvatar = Promise<UploadedPeerPhotoData>() let uploadedAvatar = Promise<UploadedPeerPhotoData>()
var uploadedVideoAvatar: (Promise<UploadedPeerPhotoData?>, Double?)? = nil var uploadedVideoAvatar: (Promise<UploadedPeerPhotoData?>, Double?)? = nil
let checkAddressNameDisposable = MetaDisposable()
actionsDisposable.add(checkAddressNameDisposable)
var requestPeer: ReplyMarkupButtonRequestPeerType.Channel?
if case let .requestPeer(peerType) = mode {
requestPeer = peerType
}
let arguments = CreateChannelArguments(context: context, updateEditingName: { editingName in let arguments = CreateChannelArguments(context: context, updateEditingName: { editingName in
updateState { current in updateState { current in
var current = current var current = current
@ -250,8 +390,8 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
return current return current
} }
}, done: { }, done: {
let (creating, title, description) = stateValue.with { state -> (Bool, String, String) in let (creating, title, description, publicLink) = stateValue.with { state -> (Bool, String, String, String?) in
return (state.creating, state.editingName.composedTitle, state.editingDescriptionText) return (state.creating, state.editingName.composedTitle, state.editingDescriptionText, state.editingPublicLinkText)
} }
if !creating && !title.isEmpty { if !creating && !title.isEmpty {
@ -263,7 +403,23 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
} }
endEditingImpl?() endEditingImpl?()
actionsDisposable.add((context.engine.peers.createChannel(title: title, description: description.isEmpty ? nil : description)
var createSignal: Signal<PeerId, CreateChannelError> = context.engine.peers.createChannel(title: title, description: description.isEmpty ? nil : description)
if case .requestPeer = mode {
if let publicLink, !publicLink.isEmpty {
createSignal = createSignal
|> mapToSignal { peerId in
return context.engine.peers.updateAddressName(domain: .peer(peerId), name: publicLink)
|> mapError { _ in
return .generic
}
|> map { _ in
return peerId
}
}
}
}
actionsDisposable.add((createSignal
|> deliverOnMainQueue |> deliverOnMainQueue
|> afterDisposed { |> afterDisposed {
Queue.mainQueue().async { Queue.mainQueue().async {
@ -283,8 +439,14 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
}).start() }).start()
} }
let controller = channelVisibilityController(context: context, peerId: peerId, mode: .initialSetup, upgradedToSupergroup: { _, f in f() }) if case .requestPeer = mode {
replaceControllerImpl?(controller) completion?(peerId, {
dismissImpl?()
})
} else {
let controller = channelVisibilityController(context: context, peerId: peerId, mode: .initialSetup, upgradedToSupergroup: { _, f in f() })
replaceControllerImpl?(controller)
}
}, error: { error in }, error: { error in
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String? let text: String?
@ -504,21 +666,57 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
}) })
}, focusOnDescription: { }, focusOnDescription: {
focusOnDescriptionImpl?() focusOnDescriptionImpl?()
}, updatePublicLinkText: { text in
if text.isEmpty {
checkAddressNameDisposable.set(nil)
updateState { state in
var updated = state
updated.editingPublicLinkText = text
updated.addressNameValidationStatus = nil
return updated
}
} else {
updateState { state in
var updated = state
updated.editingPublicLinkText = text
return updated
}
checkAddressNameDisposable.set((context.engine.peers.validateAddressNameInteractive(domain: .peer(PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(0))), name: text)
|> deliverOnMainQueue).start(next: { result in
updateState { state in
var updated = state
updated.addressNameValidationStatus = result
return updated
}
}))
}
}, openAuction: { username in
endEditingImpl?()
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: "https://fragment.com/username/\(username)", forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
}) })
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) let signal = combineLatest(context.sharedContext.presentationData, statePromise.get())
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let rightNavigationButton: ItemListNavigationButton let rightNavigationButton: ItemListNavigationButton
if state.creating { if state.creating {
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
} else { } else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: !state.editingName.composedTitle.isEmpty, action: { var isEnabled = true
if state.editingName.composedTitle.isEmpty {
isEnabled = false
}
if case let .requestPeer(peerType) = mode, let hasUsername = peerType.hasUsername, hasUsername, (state.editingPublicLinkText ?? "").isEmpty {
isEnabled = false
}
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: isEnabled, action: {
arguments.done() arguments.done()
}) })
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelIntro_CreateChannel), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelIntro_CreateChannel), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: CreateChannelEntries(presentationData: presentationData, state: state), style: .blocks, focusItemTag: CreateChannelEntryTag.info) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: CreateChannelEntries(presentationData: presentationData, state: state, requestPeer: requestPeer), style: .blocks, focusItemTag: CreateChannelEntryTag.info)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} |> afterDisposed { } |> afterDisposed {
@ -535,6 +733,9 @@ public func createChannelController(context: AccountContext, mode: CreateChannel
presentControllerImpl = { [weak controller] c, a in presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a) controller?.present(c, in: .window(.root), with: a)
} }
dismissImpl = { [weak controller] in
controller?.dismiss()
}
controller.willDisappear = { _ in controller.willDisappear = { _ in
endEditingImpl?() endEditingImpl?()
} }

View File

@ -415,7 +415,7 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat
if let hasUsername = requestPeer.hasUsername, hasUsername { if let hasUsername = requestPeer.hasUsername, hasUsername {
let currentUsername = state.editingPublicLinkText ?? "" let currentUsername = state.editingPublicLinkText ?? ""
entries.append(.usernameHeader(presentationData.theme, presentationData.strings.CreateGroup_PublicLinkTitle.uppercased())) entries.append(.usernameHeader(presentationData.theme, presentationData.strings.CreateGroup_PublicLinkTitle.uppercased()))
entries.append(.username(presentationData.theme, "link", currentUsername)) entries.append(.username(presentationData.theme, presentationData.strings.Group_PublicLink_Placeholder, currentUsername))
if let status = state.addressNameValidationStatus { if let status = state.addressNameValidationStatus {
let statusText: String let statusText: String
@ -1180,7 +1180,14 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
if state.creating { if state.creating {
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {}) rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
} else { } else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Compose_Create), style: .bold, enabled: !state.editingName.composedTitle.isEmpty, action: { var isEnabled = true
if state.editingName.composedTitle.isEmpty {
isEnabled = false
}
if case let .requestPeer(peerType) = mode, let hasUsername = peerType.hasUsername, hasUsername, (state.editingPublicLinkText ?? "").isEmpty {
isEnabled = false
}
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Compose_Create), style: .bold, enabled: isEnabled, action: {
arguments.done() arguments.done()
}) })
} }