mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Added recent stickers clearing Added sending logs via email Added forward recipient change on forward acccessory panel tap Tweaked undo panel design Various UI fixes
1056 lines
56 KiB
Swift
1056 lines
56 KiB
Swift
import Foundation
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import LegacyComponents
|
|
|
|
private final class ChannelInfoControllerArguments {
|
|
let account: Account
|
|
let avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext
|
|
let tapAvatarAction: () -> Void
|
|
let changeProfilePhoto: () -> Void
|
|
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
|
|
let updateEditingDescriptionText: (String) -> Void
|
|
let openChannelTypeSetup: () -> Void
|
|
let changeNotificationMuteSettings: () -> Void
|
|
let openSharedMedia: () -> Void
|
|
let openStats: () -> Void
|
|
let openAdmins: () -> Void
|
|
let openMembers: () -> Void
|
|
let openBanned: () -> Void
|
|
let reportChannel: () -> Void
|
|
let leaveChannel: () -> Void
|
|
let deleteChannel: () -> Void
|
|
let displayAddressNameContextMenu: (String) -> Void
|
|
let displayContextMenu: (ChannelInfoEntryTag, String) -> Void
|
|
let aboutLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
|
let toggleSignatures:(Bool) -> Void
|
|
init(account: Account, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, tapAvatarAction: @escaping () -> Void, changeProfilePhoto: @escaping () -> Void, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updateEditingDescriptionText: @escaping (String) -> Void, openChannelTypeSetup: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openStats: @escaping () -> Void, openAdmins: @escaping () -> Void, openMembers: @escaping () -> Void, openBanned: @escaping () -> Void, reportChannel: @escaping () -> Void, leaveChannel: @escaping () -> Void, deleteChannel: @escaping () -> Void, displayAddressNameContextMenu: @escaping (String) -> Void, displayContextMenu: @escaping (ChannelInfoEntryTag, String) -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, toggleSignatures: @escaping(Bool)->Void) {
|
|
self.account = account
|
|
self.avatarAndNameInfoContext = avatarAndNameInfoContext
|
|
self.tapAvatarAction = tapAvatarAction
|
|
self.changeProfilePhoto = changeProfilePhoto
|
|
self.updateEditingName = updateEditingName
|
|
self.updateEditingDescriptionText = updateEditingDescriptionText
|
|
self.openChannelTypeSetup = openChannelTypeSetup
|
|
self.changeNotificationMuteSettings = changeNotificationMuteSettings
|
|
self.openSharedMedia = openSharedMedia
|
|
self.openStats = openStats
|
|
self.openAdmins = openAdmins
|
|
self.openMembers = openMembers
|
|
self.openBanned = openBanned
|
|
self.reportChannel = reportChannel
|
|
self.leaveChannel = leaveChannel
|
|
self.deleteChannel = deleteChannel
|
|
self.displayAddressNameContextMenu = displayAddressNameContextMenu
|
|
self.displayContextMenu = displayContextMenu
|
|
self.aboutLinkAction = aboutLinkAction
|
|
self.toggleSignatures = toggleSignatures
|
|
}
|
|
}
|
|
|
|
private enum ChannelInfoSection: ItemListSectionId {
|
|
case info
|
|
case discriptionAndType
|
|
case sharedMediaAndNotifications
|
|
case members
|
|
case reportOrLeave
|
|
}
|
|
|
|
private enum ChannelInfoEntryTag {
|
|
case about
|
|
case link
|
|
}
|
|
|
|
private enum ChannelInfoEntry: ItemListNodeEntry {
|
|
case info(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, peer: Peer?, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?)
|
|
case about(theme: PresentationTheme, text: String, value: String)
|
|
case addressName(theme: PresentationTheme, text: String, value: String)
|
|
case channelPhotoSetup(theme: PresentationTheme, text: String)
|
|
case channelTypeSetup(theme: PresentationTheme, text: String, value: String)
|
|
case channelDescriptionSetup(theme: PresentationTheme, placeholder: String, value: String)
|
|
case admins(theme: PresentationTheme, text: String, value: String)
|
|
case members(theme: PresentationTheme, text: String, value: String)
|
|
case banned(theme: PresentationTheme, text: String, value: String)
|
|
case notifications(theme: PresentationTheme, text: String, value: String)
|
|
case sharedMedia(theme: PresentationTheme, text: String)
|
|
case stats(theme: PresentationTheme, text: String)
|
|
case signMessages(theme: PresentationTheme, text: String, value: Bool)
|
|
case signInfo(theme: PresentationTheme, text: String)
|
|
case report(theme: PresentationTheme, text: String)
|
|
case leave(theme: PresentationTheme, text: String)
|
|
case deleteChannel(theme: PresentationTheme, text: String)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .info, .about, .addressName, .channelPhotoSetup, .channelDescriptionSetup:
|
|
return ChannelInfoSection.info.rawValue
|
|
case .channelTypeSetup, .signMessages, .signInfo:
|
|
return ChannelInfoSection.discriptionAndType.rawValue
|
|
case .admins, .members, .banned:
|
|
return ChannelInfoSection.members.rawValue
|
|
case .sharedMedia, .notifications, .stats:
|
|
return ChannelInfoSection.sharedMediaAndNotifications.rawValue
|
|
case .report, .leave, .deleteChannel:
|
|
return ChannelInfoSection.reportOrLeave.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .info:
|
|
return 0
|
|
case .channelPhotoSetup:
|
|
return 1
|
|
case .addressName:
|
|
return 2
|
|
case .about:
|
|
return 3
|
|
case .channelDescriptionSetup:
|
|
return 4
|
|
case .channelTypeSetup:
|
|
return 5
|
|
case .signMessages:
|
|
return 6
|
|
case .signInfo:
|
|
return 7
|
|
case .admins:
|
|
return 8
|
|
case .members:
|
|
return 9
|
|
case .banned:
|
|
return 10
|
|
case .notifications:
|
|
return 12
|
|
case .sharedMedia:
|
|
return 14
|
|
case .stats:
|
|
return 15
|
|
case .report:
|
|
return 16
|
|
case .leave:
|
|
return 17
|
|
case .deleteChannel:
|
|
return 18
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: ChannelInfoEntry, rhs: ChannelInfoEntry) -> Bool {
|
|
switch lhs {
|
|
case let .info(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsCachedData, lhsState, lhsUpdatingAvatar):
|
|
if case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsState, rhsUpdatingAvatar) = rhs {
|
|
if lhsTheme !== rhsTheme {
|
|
return false
|
|
}
|
|
if lhsStrings !== rhsStrings {
|
|
return false
|
|
}
|
|
if lhsDateTimeFormat != rhsDateTimeFormat {
|
|
return false
|
|
}
|
|
if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer {
|
|
if !lhsPeer.isEqual(rhsPeer) {
|
|
return false
|
|
}
|
|
} else if (lhsPeer == nil) != (rhsPeer != nil) {
|
|
return false
|
|
}
|
|
if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData {
|
|
if !lhsCachedData.isEqual(to: rhsCachedData) {
|
|
return false
|
|
}
|
|
} else if (lhsCachedData != nil) != (rhsCachedData != nil) {
|
|
return false
|
|
}
|
|
if lhsState != rhsState {
|
|
return false
|
|
}
|
|
if lhsUpdatingAvatar != rhsUpdatingAvatar {
|
|
return false
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .about(lhsTheme, lhsText, lhsValue):
|
|
if case let .about(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
|
|
case let .addressName(lhsTheme, lhsText, lhsValue):
|
|
if case let .addressName(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .channelPhotoSetup(lhsTheme, lhsText):
|
|
if case let .channelPhotoSetup(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .channelTypeSetup(lhsTheme, lhsText, lhsValue):
|
|
if case let .channelTypeSetup(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .channelDescriptionSetup(lhsTheme, lhsPlaceholder, lhsValue):
|
|
if case let .channelDescriptionSetup(rhsTheme, rhsPlaceholder, rhsValue) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .admins(lhsTheme, lhsText, lhsValue):
|
|
if case let .admins(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .members(lhsTheme, lhsText, lhsValue):
|
|
if case let .members(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .banned(lhsTheme, lhsText, lhsValue):
|
|
if case let .banned(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .signMessages(lhsTheme, lhsText, lhsValue):
|
|
if case let .signMessages(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .signInfo(lhsTheme, lhsText):
|
|
if case let .signInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .sharedMedia(lhsTheme, lhsText):
|
|
if case let .sharedMedia(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .stats(lhsTheme, lhsText):
|
|
if case let .stats(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .report(lhsTheme, lhsText):
|
|
if case let .report(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .leave(lhsTheme, lhsText):
|
|
if case let .leave(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .deleteChannel(lhsTheme, lhsText):
|
|
if case let .deleteChannel(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .notifications(lhsTheme, lhsText, lhsValue):
|
|
if case let .notifications(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
static func <(lhs: ChannelInfoEntry, rhs: ChannelInfoEntry) -> Bool {
|
|
return lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
func item(_ arguments: ChannelInfoControllerArguments) -> ListViewItem {
|
|
switch self {
|
|
case let .info(theme, strings, dateTimeFormat, peer, cachedData, state, updatingAvatar):
|
|
return ItemListAvatarAndNameInfoItem(account: arguments.account, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: nil, cachedData: cachedData, state: state, sectionId: self.section, style: .plain, editingNameUpdated: { editingName in
|
|
arguments.updateEditingName(editingName)
|
|
}, avatarTapped: {
|
|
arguments.tapAvatarAction()
|
|
}, context: arguments.avatarAndNameInfoContext, updatingImage: updatingAvatar)
|
|
case let .about(theme, text, value):
|
|
return ItemListTextWithLabelItem(theme: theme, label: text, text: value, enabledEntitiyTypes: [.url, .mention, .hashtag], multiline: true, sectionId: self.section, action: nil, longTapAction: {
|
|
arguments.displayContextMenu(ChannelInfoEntryTag.about, value)
|
|
}, linkItemAction: { action, itemLink in
|
|
arguments.aboutLinkAction(action, itemLink)
|
|
}, tag: ChannelInfoEntryTag.about)
|
|
case let .addressName(theme, text, value):
|
|
return ItemListTextWithLabelItem(theme: theme, label: text, text: "https://t.me/\(value)", textColor: .accent, enabledEntitiyTypes: [], multiline: false, sectionId: self.section, action: {
|
|
arguments.displayAddressNameContextMenu("https://t.me/\(value)")
|
|
}, longTapAction: {
|
|
arguments.displayContextMenu(ChannelInfoEntryTag.link, "https://t.me/\(value)")
|
|
}, tag: ChannelInfoEntryTag.link)
|
|
case let .channelPhotoSetup(theme, text):
|
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.changeProfilePhoto()
|
|
})
|
|
case let .channelTypeSetup(theme, text, value):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.openChannelTypeSetup()
|
|
})
|
|
case let .channelDescriptionSetup(theme, placeholder, value):
|
|
return ItemListMultilineInputItem(theme: theme, text: value, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: 255, display: true), sectionId: self.section, style: .plain, textUpdated: { updatedText in
|
|
arguments.updateEditingDescriptionText(updatedText)
|
|
}, action: {
|
|
|
|
})
|
|
case let .admins(theme, text, value):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.openAdmins()
|
|
})
|
|
case let .members(theme, text, value):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.openMembers()
|
|
})
|
|
case let .banned(theme, text, value):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.openBanned()
|
|
})
|
|
case let .signMessages(theme, text, value):
|
|
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .plain, updated: { updated in
|
|
arguments.toggleSignatures(updated)
|
|
})
|
|
case let .signInfo(theme, text):
|
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section, style: .plain)
|
|
case let .sharedMedia(theme, text):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .plain, action: {
|
|
arguments.openSharedMedia()
|
|
})
|
|
case let .stats(theme, text):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .plain, action: {
|
|
arguments.openStats()
|
|
})
|
|
case let .notifications(theme, text, value):
|
|
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .plain, action: {
|
|
arguments.changeNotificationMuteSettings()
|
|
})
|
|
case let .report(theme, text):
|
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.reportChannel()
|
|
})
|
|
case let .leave(theme, text):
|
|
return ItemListActionItem(theme: theme, title: text, kind: .destructive, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.leaveChannel()
|
|
})
|
|
case let .deleteChannel(theme, text):
|
|
return ItemListActionItem(theme: theme, title: text, kind: .destructive, alignment: .natural, sectionId: self.section, style: .plain, action: {
|
|
arguments.deleteChannel()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct ChannelInfoState: Equatable {
|
|
let updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?
|
|
let editingState: ChannelInfoEditingState?
|
|
let savingData: Bool
|
|
|
|
init(updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?, editingState: ChannelInfoEditingState?, savingData: Bool) {
|
|
self.updatingAvatar = updatingAvatar
|
|
self.editingState = editingState
|
|
self.savingData = savingData
|
|
}
|
|
|
|
init() {
|
|
self.updatingAvatar = nil
|
|
self.editingState = nil
|
|
self.savingData = false
|
|
}
|
|
|
|
static func ==(lhs: ChannelInfoState, rhs: ChannelInfoState) -> Bool {
|
|
if lhs.updatingAvatar != rhs.updatingAvatar {
|
|
return false
|
|
}
|
|
if lhs.editingState != rhs.editingState {
|
|
return false
|
|
}
|
|
if lhs.savingData != rhs.savingData {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func withUpdatedUpdatingAvatar(_ updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?) -> ChannelInfoState {
|
|
return ChannelInfoState(updatingAvatar: updatingAvatar, editingState: self.editingState, savingData: self.savingData)
|
|
}
|
|
|
|
func withUpdatedEditingState(_ editingState: ChannelInfoEditingState?) -> ChannelInfoState {
|
|
return ChannelInfoState(updatingAvatar: self.updatingAvatar, editingState: editingState, savingData: self.savingData)
|
|
}
|
|
|
|
func withUpdatedSavingData(_ savingData: Bool) -> ChannelInfoState {
|
|
return ChannelInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, savingData: savingData)
|
|
}
|
|
}
|
|
|
|
private struct ChannelInfoEditingState: Equatable {
|
|
let editingName: ItemListAvatarAndNameInfoItemName?
|
|
let editingDescriptionText: String
|
|
|
|
func withUpdatedEditingDescriptionText(_ editingDescriptionText: String) -> ChannelInfoEditingState {
|
|
return ChannelInfoEditingState(editingName: self.editingName, editingDescriptionText: editingDescriptionText)
|
|
}
|
|
|
|
static func ==(lhs: ChannelInfoEditingState, rhs: ChannelInfoEditingState) -> Bool {
|
|
if lhs.editingName != rhs.editingName {
|
|
return false
|
|
}
|
|
if lhs.editingDescriptionText != rhs.editingDescriptionText {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
private func channelInfoEntries(account: Account, presentationData: PresentationData, view: PeerView, globalNotificationSettings: GlobalNotificationSettings, state: ChannelInfoState) -> [ChannelInfoEntry] {
|
|
var entries: [ChannelInfoEntry] = []
|
|
|
|
if let peer = view.peers[view.peerId] as? TelegramChannel {
|
|
let canEditChannel = peer.hasPermission(.changeInfo)
|
|
let canEditMembers = peer.hasPermission(.banMembers)
|
|
|
|
let infoState = ItemListAvatarAndNameInfoItemState(editingName: canEditChannel ? state.editingState?.editingName : nil, updatingName: nil)
|
|
entries.append(.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer, cachedData: view.cachedData, state: infoState, updatingAvatar: state.updatingAvatar))
|
|
|
|
if let editingState = state.editingState, canEditChannel {
|
|
entries.append(.channelPhotoSetup(theme: presentationData.theme, text: presentationData.strings.Channel_UpdatePhotoItem))
|
|
entries.append(.channelDescriptionSetup(theme: presentationData.theme, placeholder: presentationData.strings.Channel_About_Placeholder, value: editingState.editingDescriptionText))
|
|
}
|
|
|
|
if let _ = state.editingState, peer.flags.contains(.isCreator) {
|
|
let linkText: String
|
|
if let username = peer.username {
|
|
linkText = "@\(username)"
|
|
} else {
|
|
linkText = presentationData.strings.Channel_Setup_TypePrivate
|
|
}
|
|
entries.append(.channelTypeSetup(theme: presentationData.theme, text: presentationData.strings.Channel_TypeSetup_Title, value: linkText))
|
|
let messagesShouldHaveSignatures:Bool
|
|
switch peer.info {
|
|
case let .broadcast(info):
|
|
messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures)
|
|
default:
|
|
messagesShouldHaveSignatures = false
|
|
}
|
|
|
|
entries.append(.signMessages(theme: presentationData.theme, text: presentationData.strings.Channel_SignMessages, value: messagesShouldHaveSignatures))
|
|
entries.append(.signInfo(theme: presentationData.theme, text: presentationData.strings.Channel_SignMessages_Help))
|
|
} else if let username = peer.username, !username.isEmpty, state.editingState == nil {
|
|
entries.append(.addressName(theme: presentationData.theme, text: presentationData.strings.Channel_LinkItem, value: username))
|
|
}
|
|
|
|
if let cachedChannelData = view.cachedData as? CachedChannelData {
|
|
if let _ = state.editingState, canEditChannel {
|
|
} else {
|
|
if let about = cachedChannelData.about, !about.isEmpty {
|
|
entries.append(.about(theme: presentationData.theme, text: presentationData.strings.Channel_AboutItem, value: about))
|
|
}
|
|
}
|
|
}
|
|
|
|
if let cachedChannelData = view.cachedData as? CachedChannelData {
|
|
if canEditMembers {
|
|
if peer.adminRights != nil || peer.flags.contains(.isCreator) {
|
|
let adminCount = cachedChannelData.participantsSummary.adminCount ?? 0
|
|
entries.append(.admins(theme: presentationData.theme, text: presentationData.strings.GroupInfo_Administrators, value: "\(adminCount == 0 ? "" : "\(presentationStringsFormattedNumber(adminCount, presentationData.dateTimeFormat.groupingSeparator))")"))
|
|
|
|
let memberCount = cachedChannelData.participantsSummary.memberCount ?? 0
|
|
entries.append(.members(theme: presentationData.theme, text: presentationData.strings.Channel_Info_Subscribers, value: "\(memberCount == 0 ? "" : "\(presentationStringsFormattedNumber(memberCount, presentationData.dateTimeFormat.groupingSeparator))")"))
|
|
|
|
let bannedCount = cachedChannelData.participantsSummary.kickedCount ?? 0
|
|
entries.append(.banned(theme: presentationData.theme, text: presentationData.strings.GroupRemoved_Title, value: "\(bannedCount == 0 ? "" : "\(presentationStringsFormattedNumber(bannedCount, presentationData.dateTimeFormat.groupingSeparator))")"))
|
|
}
|
|
}
|
|
}
|
|
|
|
if state.editingState == nil, let notificationSettings = view.notificationSettings as? TelegramPeerNotificationSettings {
|
|
let notificationsText: String
|
|
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
|
if until < Int32.max - 1 {
|
|
notificationsText = stringForRemainingMuteInterval(strings: presentationData.strings, muteInterval: until)
|
|
} else {
|
|
notificationsText = presentationData.strings.UserInfo_NotificationsDisabled
|
|
}
|
|
} else if case .default = notificationSettings.messageSound {
|
|
notificationsText = presentationData.strings.UserInfo_NotificationsEnabled
|
|
} else {
|
|
notificationsText = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: notificationSettings.messageSound, default: globalNotificationSettings.effective.channels.sound)
|
|
}
|
|
entries.append(ChannelInfoEntry.notifications(theme: presentationData.theme, text: presentationData.strings.GroupInfo_Notifications, value: notificationsText))
|
|
}
|
|
if state.editingState == nil {
|
|
entries.append(ChannelInfoEntry.sharedMedia(theme: presentationData.theme, text: presentationData.strings.GroupInfo_SharedMedia))
|
|
if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.flags.contains(.canViewStats) {
|
|
entries.append(ChannelInfoEntry.stats(theme: presentationData.theme, text: presentationData.strings.ChannelInfo_Stats))
|
|
}
|
|
}
|
|
|
|
if peer.flags.contains(.isCreator) {
|
|
//if state.editingState != nil {
|
|
entries.append(ChannelInfoEntry.deleteChannel(theme: presentationData.theme, text: presentationData.strings.ChannelInfo_DeleteChannel))
|
|
//}
|
|
} else if state.editingState == nil {
|
|
entries.append(ChannelInfoEntry.report(theme: presentationData.theme, text: presentationData.strings.ReportPeer_Report))
|
|
if peer.participationStatus == .member {
|
|
entries.append(ChannelInfoEntry.leave(theme: presentationData.theme, text: presentationData.strings.Channel_LeaveChannel))
|
|
}
|
|
}
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
private func valuesRequiringUpdate(state: ChannelInfoState, view: PeerView) -> (title: String?, description: String?) {
|
|
if let peer = view.peers[view.peerId] as? TelegramChannel {
|
|
var titleValue: String?
|
|
var descriptionValue: String?
|
|
if let editingState = state.editingState {
|
|
if let title = editingState.editingName?.composedTitle, title != peer.title {
|
|
titleValue = title
|
|
}
|
|
if let cachedData = view.cachedData as? CachedChannelData {
|
|
if let about = cachedData.about {
|
|
if about != editingState.editingDescriptionText {
|
|
descriptionValue = editingState.editingDescriptionText
|
|
}
|
|
} else if !editingState.editingDescriptionText.isEmpty {
|
|
descriptionValue = editingState.editingDescriptionText
|
|
}
|
|
}
|
|
}
|
|
|
|
return (titleValue, descriptionValue)
|
|
} else {
|
|
return (nil, nil)
|
|
}
|
|
}
|
|
|
|
public func channelInfoController(context: AccountContext, peerId: PeerId) -> ViewController {
|
|
let statePromise = ValuePromise(ChannelInfoState(), ignoreRepeated: true)
|
|
let stateValue = Atomic(value: ChannelInfoState())
|
|
let updateState: ((ChannelInfoState) -> ChannelInfoState) -> Void = { f in
|
|
statePromise.set(stateValue.modify { f($0) })
|
|
}
|
|
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
|
var removePeerChatImpl: ((Peer, Bool) -> Void)?
|
|
var endEditingImpl: (() -> Void)?
|
|
|
|
let actionsDisposable = DisposableSet()
|
|
|
|
let updatePeerNameDisposable = MetaDisposable()
|
|
actionsDisposable.add(updatePeerNameDisposable)
|
|
|
|
let updatePeerDescriptionDisposable = MetaDisposable()
|
|
actionsDisposable.add(updatePeerDescriptionDisposable)
|
|
|
|
let changeMuteSettingsDisposable = MetaDisposable()
|
|
actionsDisposable.add(changeMuteSettingsDisposable)
|
|
|
|
let hiddenAvatarRepresentationDisposable = MetaDisposable()
|
|
actionsDisposable.add(hiddenAvatarRepresentationDisposable)
|
|
|
|
let updateAvatarDisposable = MetaDisposable()
|
|
actionsDisposable.add(updateAvatarDisposable)
|
|
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
|
|
|
|
let navigateDisposable = MetaDisposable()
|
|
actionsDisposable.add(navigateDisposable)
|
|
|
|
let statsUrlDisposable = MetaDisposable()
|
|
actionsDisposable.add(statsUrlDisposable)
|
|
|
|
var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)?
|
|
let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext()
|
|
var updateHiddenAvatarImpl: (() -> Void)?
|
|
|
|
var displayContextMenuImpl: ((ChannelInfoEntryTag, String) -> Void)?
|
|
var aboutLinkActionImpl: ((TextLinkItemActionType, TextLinkItem) -> Void)?
|
|
|
|
let arguments = ChannelInfoControllerArguments(account: context.account, avatarAndNameInfoContext: avatarAndNameInfoContext, tapAvatarAction: {
|
|
let _ = (context.account.postbox.loadedPeerWithId(peerId) |> take(1) |> deliverOnMainQueue).start(next: { peer in
|
|
if peer.profileImageRepresentations.isEmpty {
|
|
return
|
|
}
|
|
|
|
let galleryController = AvatarGalleryController(context: context, peer: peer, replaceRootController: { controller, ready in
|
|
|
|
})
|
|
hiddenAvatarRepresentationDisposable.set((galleryController.hiddenMedia |> deliverOnMainQueue).start(next: { entry in
|
|
avatarAndNameInfoContext.hiddenAvatarRepresentation = entry?.representations.first?.representation
|
|
updateHiddenAvatarImpl?()
|
|
}))
|
|
presentControllerImpl?(galleryController, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
|
|
return avatarGalleryTransitionArguments?(entry)
|
|
}))
|
|
})
|
|
}, changeProfilePhoto: {
|
|
endEditingImpl?()
|
|
|
|
let _ = (context.account.postbox.transaction { transaction -> (Peer?, SearchBotsConfiguration) in
|
|
return (transaction.getPeer(peerId), currentSearchBotsConfiguration(transaction: transaction))
|
|
} |> deliverOnMainQueue).start(next: { peer, searchBotsConfiguration in
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
|
legacyController.statusBar.statusBarStyle = .Ignore
|
|
|
|
let emptyController = LegacyEmptyController(context: legacyController.context)!
|
|
let navigationController = makeLegacyNavigationController(rootController: emptyController)
|
|
navigationController.setNavigationBarHidden(true, animated: false)
|
|
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
|
|
|
|
legacyController.bind(controller: navigationController)
|
|
|
|
presentControllerImpl?(legacyController, nil)
|
|
|
|
var hasPhotos = false
|
|
if let peer = peer, !peer.profileImageRepresentations.isEmpty {
|
|
hasPhotos = true
|
|
}
|
|
|
|
let completedImpl: (UIImage) -> Void = { image in
|
|
if let data = UIImageJPEGRepresentation(image, 0.6) {
|
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
|
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
|
let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource)
|
|
updateState {
|
|
$0.withUpdatedUpdatingAvatar(.image(representation, true))
|
|
}
|
|
updateAvatarDisposable.set((updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource), mapResourceToAvatarSizes: { resource, representations in
|
|
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
|
|
})
|
|
|> deliverOnMainQueue).start(next: { result in
|
|
switch result {
|
|
case .complete:
|
|
updateState {
|
|
$0.withUpdatedUpdatingAvatar(nil)
|
|
}
|
|
case .progress:
|
|
break
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
|
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos, hasViewButton: false, personalPhoto: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
|
let _ = currentAvatarMixin.swap(mixin)
|
|
mixin.requestSearchController = { assetsController in
|
|
let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer?.displayTitle, completion: { result in
|
|
assetsController?.dismiss()
|
|
completedImpl(result)
|
|
}))
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}
|
|
mixin.didFinishWithImage = { image in
|
|
if let image = image {
|
|
completedImpl(image)
|
|
}
|
|
}
|
|
mixin.didFinishWithDelete = {
|
|
let _ = currentAvatarMixin.swap(nil)
|
|
updateState {
|
|
if let profileImage = peer?.smallProfileImage {
|
|
return $0.withUpdatedUpdatingAvatar(.image(profileImage, false))
|
|
} else {
|
|
return $0.withUpdatedUpdatingAvatar(.none)
|
|
}
|
|
}
|
|
updateAvatarDisposable.set((updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
|
|
return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
|
|
}) |> deliverOnMainQueue).start(next: { result in
|
|
switch result {
|
|
case .complete:
|
|
updateState {
|
|
$0.withUpdatedUpdatingAvatar(nil)
|
|
}
|
|
case .progress:
|
|
break
|
|
}
|
|
}))
|
|
}
|
|
mixin.didDismiss = { [weak legacyController] in
|
|
let _ = currentAvatarMixin.swap(nil)
|
|
legacyController?.dismiss()
|
|
}
|
|
let menuController = mixin.present()
|
|
if let menuController = menuController {
|
|
menuController.customRemoveFromParentViewController = { [weak legacyController] in
|
|
legacyController?.dismiss()
|
|
}
|
|
}
|
|
})
|
|
}, updateEditingName: { editingName in
|
|
updateState { state in
|
|
if let editingState = state.editingState {
|
|
return state.withUpdatedEditingState(ChannelInfoEditingState(editingName: editingName, editingDescriptionText: editingState.editingDescriptionText))
|
|
} else {
|
|
return state
|
|
}
|
|
}
|
|
}, updateEditingDescriptionText: { text in
|
|
updateState { state in
|
|
if let editingState = state.editingState {
|
|
return state.withUpdatedEditingState(editingState.withUpdatedEditingDescriptionText(text))
|
|
}
|
|
return state
|
|
}
|
|
}, openChannelTypeSetup: {
|
|
presentControllerImpl?(channelVisibilityController(context: context, peerId: peerId, mode: .generic, upgradedToSupergroup: { _, f in f() }), ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
|
}, changeNotificationMuteSettings: {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let _ = (context.account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in
|
|
let peerSettings: TelegramPeerNotificationSettings = (transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings) ?? TelegramPeerNotificationSettings.defaultSettings
|
|
let globalSettings: GlobalNotificationSettings = (transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications) as? GlobalNotificationSettings) ?? GlobalNotificationSettings.defaultSettings
|
|
return (peerSettings, globalSettings)
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peerSettings, globalSettings in
|
|
let soundSettings: NotificationSoundSettings?
|
|
if case .default = peerSettings.messageSound {
|
|
soundSettings = NotificationSoundSettings(value: nil)
|
|
} else {
|
|
soundSettings = NotificationSoundSettings(value: peerSettings.messageSound)
|
|
}
|
|
let controller = notificationMuteSettingsController(presentationData: presentationData, notificationSettings: globalSettings.effective.groupChats, soundSettings: soundSettings, openSoundSettings: {
|
|
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: peerSettings.messageSound, defaultSound: globalSettings.effective.groupChats.sound, completion: { sound in
|
|
let _ = updatePeerNotificationSoundInteractive(account: context.account, peerId: peerId, sound: sound).start()
|
|
})
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}, updateSettings: { value in
|
|
changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: context.account, peerId: peerId, muteInterval: value).start())
|
|
})
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
})
|
|
}, openSharedMedia: {
|
|
if let controller = peerSharedMediaController(context: context, peerId: peerId) {
|
|
pushControllerImpl?(controller)
|
|
}
|
|
}, openStats: {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
var urlSignal = channelStatsUrl(postbox: context.account.postbox, network: context.account.network, peerId: peerId, params: "", darkTheme: presentationData.theme.chatList.searchBarKeyboardColor.keyboardAppearance == .dark)
|
|
|
|
var cancelImpl: (() -> Void)?
|
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
|
|
cancelImpl?()
|
|
}))
|
|
presentControllerImpl?(controller, nil)
|
|
return ActionDisposable { [weak controller] in
|
|
Queue.mainQueue().async() {
|
|
controller?.dismiss()
|
|
}
|
|
}
|
|
}
|
|
|> runOn(Queue.mainQueue())
|
|
|> delay(0.05, queue: Queue.mainQueue())
|
|
let progressDisposable = progressSignal.start()
|
|
|
|
urlSignal = urlSignal
|
|
|> afterDisposed {
|
|
Queue.mainQueue().async {
|
|
progressDisposable.dispose()
|
|
}
|
|
}
|
|
cancelImpl = {
|
|
statsUrlDisposable.set(nil)
|
|
}
|
|
|
|
statsUrlDisposable.set((urlSignal
|
|
|> deliverOnMainQueue).start(next: { url in
|
|
pushControllerImpl?(ChannelStatsController(context: context, url: url, peerId: peerId))
|
|
}, error: { _ in
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
|
}))
|
|
}, openAdmins: {
|
|
pushControllerImpl?(channelAdminsController(context: context, peerId: peerId))
|
|
}, openMembers: {
|
|
pushControllerImpl?(channelMembersController(context: context, peerId: peerId))
|
|
}, openBanned: {
|
|
pushControllerImpl?(channelBlacklistController(context: context, peerId: peerId))
|
|
}, reportChannel: {
|
|
presentControllerImpl?(peerReportOptionsController(context: context, subject: .peer(peerId), present: { c, a in
|
|
presentControllerImpl?(c, a)
|
|
}), nil)
|
|
}, leaveChannel: {
|
|
let _ = (context.account.postbox.transaction { transaction -> Peer? in
|
|
return transaction.getPeer(peerId)
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
|
let dismissAction: () -> Void = { [weak controller] in
|
|
controller?.dismissAnimated()
|
|
}
|
|
controller.setItemGroups([
|
|
ActionSheetItemGroup(items: [
|
|
ActionSheetButtonItem(title: presentationData.strings.Channel_LeaveChannel, color: .destructive, action: {
|
|
dismissAction()
|
|
removePeerChatImpl?(peer, false)
|
|
}),
|
|
]),
|
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
])
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
})
|
|
}, deleteChannel: {
|
|
let _ = (context.account.postbox.transaction { transaction -> Peer? in
|
|
return transaction.getPeer(peerId)
|
|
}
|
|
|> deliverOnMainQueue).start(next: { peer in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let controller = ActionSheetController(presentationTheme: presentationData.theme)
|
|
let dismissAction: () -> Void = { [weak controller] in
|
|
controller?.dismissAnimated()
|
|
}
|
|
controller.setItemGroups([
|
|
ActionSheetItemGroup(items: [
|
|
ActionSheetTextItem(title: presentationData.strings.ChannelInfo_DeleteChannelConfirmation),
|
|
ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_DeleteChannel, color: .destructive, action: {
|
|
dismissAction()
|
|
removePeerChatImpl?(peer, true)
|
|
}),
|
|
]),
|
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
|
])
|
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
})
|
|
}, displayAddressNameContextMenu: { text in
|
|
let shareController = ShareController(context: context, subject: .url(text))
|
|
presentControllerImpl?(shareController, nil)
|
|
}, displayContextMenu: { tag, text in
|
|
displayContextMenuImpl?(tag, text)
|
|
}, aboutLinkAction: { action, itemLink in
|
|
aboutLinkActionImpl?(action, itemLink)
|
|
}, toggleSignatures: { enabled in
|
|
actionsDisposable.add(toggleShouldChannelMessagesSignatures(account: context.account, peerId: peerId, enabled: enabled).start())
|
|
})
|
|
|
|
let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([PreferencesKeys.globalNotifications]))
|
|
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), context.account.viewTracker.peerView(peerId), context.account.postbox.combinedView(keys: [globalNotificationsKey]))
|
|
|> map { presentationData, state, view, combinedView -> (ItemListControllerState, (ItemListNodeState<ChannelInfoEntry>, ChannelInfoEntry.ItemGenerationArguments)) in
|
|
let peer = peerViewMainPeer(view)
|
|
|
|
var globalNotificationSettings: GlobalNotificationSettings = GlobalNotificationSettings.defaultSettings
|
|
if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView {
|
|
if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings {
|
|
globalNotificationSettings = settings
|
|
}
|
|
}
|
|
|
|
var canManageChannel = false
|
|
if let peer = peer as? TelegramChannel {
|
|
if peer.flags.contains(.isCreator) {
|
|
canManageChannel = true
|
|
} else if let adminRights = peer.adminRights, !adminRights.isEmpty {
|
|
canManageChannel = true
|
|
}
|
|
}
|
|
|
|
var canEditChannel = false
|
|
if let peer = view.peers[view.peerId] as? TelegramChannel {
|
|
canEditChannel = peer.hasPermission(.changeInfo)
|
|
}
|
|
|
|
var leftNavigationButton: ItemListNavigationButton?
|
|
var rightNavigationButton: ItemListNavigationButton?
|
|
if let editingState = state.editingState {
|
|
leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
|
updateState {
|
|
$0.withUpdatedEditingState(nil)
|
|
}
|
|
})
|
|
|
|
var doneEnabled = true
|
|
if let editingName = editingState.editingName, editingName.isEmpty {
|
|
doneEnabled = false
|
|
}
|
|
if peer is TelegramChannel {
|
|
if (view.cachedData as? CachedChannelData) == nil {
|
|
doneEnabled = false
|
|
}
|
|
}
|
|
|
|
if state.savingData {
|
|
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: doneEnabled, action: {})
|
|
} else {
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: doneEnabled, action: {
|
|
var updateValues: (title: String?, description: String?) = (nil, nil)
|
|
updateState { state in
|
|
updateValues = valuesRequiringUpdate(state: state, view: view)
|
|
if updateValues.0 != nil || updateValues.1 != nil {
|
|
return state.withUpdatedSavingData(true)
|
|
} else {
|
|
return state.withUpdatedEditingState(nil)
|
|
}
|
|
}
|
|
|
|
let updateTitle: Signal<Void, Void>
|
|
if let titleValue = updateValues.title {
|
|
updateTitle = updatePeerTitle(account: context.account, peerId: peerId, title: titleValue)
|
|
|> mapError { _ in return Void() }
|
|
} else {
|
|
updateTitle = .complete()
|
|
}
|
|
|
|
let updateDescription: Signal<Void, Void>
|
|
if let descriptionValue = updateValues.description {
|
|
updateDescription = updatePeerDescription(account: context.account, peerId: peerId, description: descriptionValue.isEmpty ? nil : descriptionValue)
|
|
|> mapError { _ in return Void() }
|
|
} else {
|
|
updateDescription = .complete()
|
|
}
|
|
|
|
let signal = combineLatest(updateTitle, updateDescription)
|
|
|
|
updatePeerNameDisposable.set((signal |> deliverOnMainQueue).start(error: { _ in
|
|
updateState { state in
|
|
return state.withUpdatedSavingData(false)
|
|
}
|
|
}, completed: {
|
|
updateState { state in
|
|
return state.withUpdatedSavingData(false).withUpdatedEditingState(nil)
|
|
}
|
|
}))
|
|
})
|
|
}
|
|
} else if canEditChannel {
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
|
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
|
var text = ""
|
|
if let cachedData = view.cachedData as? CachedChannelData, let about = cachedData.about {
|
|
text = about
|
|
}
|
|
updateState { state in
|
|
return state.withUpdatedEditingState(ChannelInfoEditingState(editingName: ItemListAvatarAndNameInfoItemName(channel), editingDescriptionText: text))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.UserInfo_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
|
let listState = ItemListNodeState(entries: channelInfoEntries(account: context.account, presentationData: presentationData, view: view, globalNotificationSettings: globalNotificationSettings, state: state), style: .plain)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
} |> afterDisposed {
|
|
actionsDisposable.dispose()
|
|
}
|
|
|
|
let controller = ItemListController(context: context, state: signal)
|
|
|
|
pushControllerImpl = { [weak controller] value in
|
|
(controller?.navigationController as? NavigationController)?.pushViewController(value)
|
|
}
|
|
presentControllerImpl = { [weak controller] value, presentationArguments in
|
|
controller?.present(value, in: .window(.root), with: presentationArguments)
|
|
}
|
|
removePeerChatImpl = { [weak controller] peer, deleteGloballyIfPossible in
|
|
guard let controller = controller, let navigationController = controller.navigationController as? NavigationController else {
|
|
return
|
|
}
|
|
guard let tabController = navigationController.viewControllers.first as? TabBarController else {
|
|
return
|
|
}
|
|
for childController in tabController.controllers {
|
|
if let chatListController = childController as? ChatListController {
|
|
chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] deleted in
|
|
if deleted {
|
|
navigationController?.popToRoot(animated: true)
|
|
}
|
|
}, removed: {
|
|
})
|
|
break
|
|
}
|
|
}
|
|
}
|
|
avatarGalleryTransitionArguments = { [weak controller] entry in
|
|
if let controller = controller {
|
|
var result: ((ASDisplayNode, () -> (UIView?, UIView?)), CGRect)?
|
|
controller.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
result = itemNode.avatarTransitionNode()
|
|
}
|
|
}
|
|
if let (node, _) = result {
|
|
return GalleryTransitionArguments(transitionNode: node, addToTransitionSurface: { _ in
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
updateHiddenAvatarImpl = { [weak controller] in
|
|
if let controller = controller {
|
|
controller.forEachItemNode { itemNode in
|
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
|
itemNode.updateAvatarHidden()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
displayContextMenuImpl = { [weak controller] tag, text in
|
|
if let strongController = controller {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
var resultItemNode: ListViewItemNode?
|
|
let _ = strongController.frameForItemNode({ itemNode in
|
|
if let itemNode = itemNode as? ItemListTextWithLabelItemNode {
|
|
if let itemTag = itemNode.tag as? ChannelInfoEntryTag {
|
|
if itemTag == tag {
|
|
resultItemNode = itemNode
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
if let resultItemNode = resultItemNode {
|
|
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: {
|
|
UIPasteboard.general.string = text
|
|
})])
|
|
strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
|
if let strongController = controller, let resultItemNode = resultItemNode {
|
|
return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds)
|
|
} else {
|
|
return nil
|
|
}
|
|
}))
|
|
|
|
}
|
|
}
|
|
}
|
|
aboutLinkActionImpl = { [weak controller] action, itemLink in
|
|
if let controller = controller {
|
|
handlePeerInfoAboutTextAction(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink)
|
|
}
|
|
}
|
|
endEditingImpl = {
|
|
[weak controller] in
|
|
controller?.view.endEditing(true)
|
|
}
|
|
return controller
|
|
}
|