This commit is contained in:
Isaac 2024-03-25 14:36:10 +04:00
parent ba5378ebde
commit 841881f69e
7 changed files with 172 additions and 33 deletions

View File

@ -1784,5 +1784,33 @@ public extension TelegramEngine.EngineData.Item {
return view.values[PreferencesKeys.businessLinks()]?.get(TelegramBusinessChatLinks.self)
}
}
public struct PersonalChannel: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = CachedTelegramPersonalChannel
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .cachedPeerData(peerId: self.id)
}
func extract(view: PostboxView) -> Result {
guard let view = view as? CachedPeerDataView else {
preconditionFailure()
}
if let cachedData = view.cachedPeerData as? CachedUserData {
return cachedData.personalChannel
} else {
return .unknown
}
}
}
}
}

View File

@ -314,6 +314,8 @@ public enum PresentationResourceKey: Int32 {
case shareAvatarPremiumLockBadge
case sharedLinkIcon
case hideIconImage
}
public enum ChatExpiredStoryIndicatorType: Hashable {

View File

@ -402,4 +402,10 @@ public struct PresentationResourcesItemList {
})
})
}
public static func hideIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.hideIconImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/Archive/IconHide"), color: theme.list.itemAccentColor)
})
}
}

View File

@ -230,13 +230,15 @@ final class TelegramGlobalSettings {
final class PeerInfoPersonalChannelData: Equatable {
let peer: EnginePeer
let subscriberCount: Int
let subscriberCount: Int?
let topMessage: EngineMessage?
let isLoading: Bool
init(peer: EnginePeer, subscriberCount: Int, topMessage: EngineMessage?) {
init(peer: EnginePeer, subscriberCount: Int?, topMessage: EngineMessage?, isLoading: Bool) {
self.peer = peer
self.subscriberCount = subscriberCount
self.topMessage = topMessage
self.isLoading = isLoading
}
static func ==(lhs: PeerInfoPersonalChannelData, rhs: PeerInfoPersonalChannelData) -> Bool {
@ -252,6 +254,9 @@ final class PeerInfoPersonalChannelData: Equatable {
if lhs.topMessage != rhs.topMessage {
return false
}
if lhs.isLoading != rhs.isLoading {
return false
}
return true
}
}
@ -531,26 +536,49 @@ public func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId, c
}
}
private func peerInfoPersonalChannel(context: AccountContext, peerId: EnginePeer.Id) -> Signal<PeerInfoPersonalChannelData?, NoError> {
return context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudChannel, id: EnginePeer.Id.Id._internalFromInt64Value(1006503122)))
private func peerInfoPersonalChannel(context: AccountContext, peerId: EnginePeer.Id, isSettings: Bool) -> Signal<PeerInfoPersonalChannelData?, NoError> {
return context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.PersonalChannel(id: peerId)
)
|> mapToSignal { peer -> Signal<PeerInfoPersonalChannelData?, NoError> in
guard let peer else {
|> distinctUntilChanged
|> mapToSignal { personalChannel -> Signal<PeerInfoPersonalChannelData?, NoError> in
guard case let .known(personalChannelValue) = personalChannel, let personalChannelValue else {
return .single(nil)
}
return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peer.id, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 5, fixedCombinedReadStates: nil)
|> map { view, _, _ -> PeerInfoPersonalChannelData? in
guard let entry = view.entries.last else {
return nil
return context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Peer.Peer(id: personalChannelValue.peerId),
TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: personalChannelValue.peerId)
)
|> mapToSignal { channelPeer, participantCount -> Signal<PeerInfoPersonalChannelData?, NoError> in
guard let channelPeer else {
return .single(nil)
}
return PeerInfoPersonalChannelData(
peer: peer,
subscriberCount: 2000000,
topMessage: EngineMessage(entry.message)
)
//TODO:localize and keep updated
return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: channelPeer.id, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: 5, fixedCombinedReadStates: nil)
|> map { view, _, _ -> PeerInfoPersonalChannelData? in
let entry = view.entries.last
var isLoading = false
if entry == nil && view.isLoading {
isLoading = true
}
var mappedParticipantCount: Int?
if let participantCount {
mappedParticipantCount = participantCount
} else if let subscriberCount = personalChannelValue.subscriberCount {
mappedParticipantCount = Int(subscriberCount)
}
return PeerInfoPersonalChannelData(
peer: channelPeer,
subscriberCount: mappedParticipantCount,
topMessage: (entry?.message).flatMap(EngineMessage.init),
isLoading: isLoading
)
}
}
}
|> distinctUntilChanged
@ -699,7 +727,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|> distinctUntilChanged,
hasStories,
bots,
peerInfoPersonalChannel(context: context, peerId: peerId)
peerInfoPersonalChannel(context: context, peerId: peerId, isSettings: true)
)
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel -> PeerInfoScreenData in
let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications
@ -1020,7 +1048,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
hasSavedMessagesChats,
hasSavedMessages,
hasSavedMessageTags,
peerInfoPersonalChannel(context: context, peerId: peerId)
peerInfoPersonalChannel(context: context, peerId: peerId, isSettings: false)
)
|> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, personalChannel -> PeerInfoScreenData in
var availablePanes = availablePanes

View File

@ -1097,7 +1097,11 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
}))
//TODO:localize
items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerPersonalChannel, label: .text("Add"), text: "Personal Channel", icon: nil, action: {
var personalChannelTitle: String?
if let personalChannel = data.personalChannel {
personalChannelTitle = personalChannel.peer.compactDisplayTitle
}
items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerPersonalChannel, label: .text(personalChannelTitle ?? "Add"), text: "Personal Channel", icon: nil, action: {
interaction.editingOpenPersonalChannel()
}))
}
@ -1166,9 +1170,14 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
items[.calls]!.append(PeerInfoScreenCallListItem(id: 20, messages: callMessages))
}
if let personalChannel = data.personalChannel, !"".isEmpty {
if let personalChannel = data.personalChannel {
let peerId = personalChannel.peer.id
items[.personalChannel]?.append(PeerInfoScreenHeaderItem(id: 0, text: "PERSONAL CHANNEL", label: "2M subscribers"))
var label: String?
if let subscriberCount = personalChannel.subscriberCount {
label = presentationData.strings.Conversation_StatusSubscribers(Int32(subscriberCount))
}
//TODO:localize
items[.personalChannel]?.append(PeerInfoScreenHeaderItem(id: 0, text: "PERSONAL CHANNEL", label: label))
items[.personalChannel]?.append(PeerInfoScreenPersonalChannelItem(id: 1, context: context, data: personalChannel, requestLayout: { _ in
}, action: { [weak interaction] in
guard let interaction else {
@ -7611,13 +7620,24 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
private func editingOpenPersonalChannel() {
self.controller?.push(PeerSelectionScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, completion: { [weak self] channel in
let _ = (PeerSelectionScreen.initialData(context: self.context)
|> deliverOnMainQueue).start(next: { [weak self] initialData in
guard let self else {
return
}
let _ = self.context.engine.accountData.updatePersonalChannel(personalChannel: TelegramPersonalChannel(peerId: channel.peer.id, subscriberCount: channel.subscriberCount.flatMap(Int32.init(clamping:)), topMessageId: nil)).startStandalone()
}))
self.controller?.push(PeerSelectionScreen(context: self.context, initialData: initialData, updatedPresentationData: self.controller?.updatedPresentationData, completion: { [weak self] channel in
guard let self else {
return
}
var mappedChannel: TelegramPersonalChannel?
if let channel {
mappedChannel = TelegramPersonalChannel(peerId: channel.peer.id, subscriberCount: channel.subscriberCount.flatMap(Int32.init(clamping:)), topMessageId: nil)
}
let _ = self.context.engine.accountData.updatePersonalChannel(personalChannel: mappedChannel).startStandalone()
}))
})
}
private func editingOpenInviteLinksSetup() {

View File

@ -26,6 +26,8 @@ swift_library(
"//submodules/ComponentFlow",
"//submodules/Components/MultilineTextComponent",
"//submodules/Components/BalancedTextComponent",
"//submodules/ItemListPeerActionItem",
"//submodules/AccountContext",
],
visibility = [
"//visibility:public",

View File

@ -17,18 +17,19 @@ import ViewControllerComponent
import ComponentFlow
import BalancedTextComponent
import MultilineTextComponent
import ItemListPeerActionItem
final class PeerSelectionScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let initialData: PeerSelectionScreen.InitialData
let completion: (PeerSelectionScreen.ChannelInfo) -> Void
let completion: (PeerSelectionScreen.ChannelInfo?) -> Void
init(
context: AccountContext,
initialData: PeerSelectionScreen.InitialData,
completion: @escaping (PeerSelectionScreen.ChannelInfo) -> Void
completion: @escaping (PeerSelectionScreen.ChannelInfo?) -> Void
) {
self.context = context
self.initialData = initialData
@ -45,22 +46,30 @@ final class PeerSelectionScreenComponent: Component {
private enum ContentEntry: Comparable, Identifiable {
enum Id: Hashable {
case hide
case item(EnginePeer.Id)
}
var stableId: Id {
switch self {
case .hide:
return .hide
case let .item(peer, _, _):
return .item(peer.id)
}
}
case hide
case item(peer: EnginePeer, subscriberCount: Int?, sortIndex: Int)
static func <(lhs: ContentEntry, rhs: ContentEntry) -> Bool {
switch lhs {
case .hide:
return false
case let .item(lhsPeer, _, lhsSortIndex):
switch rhs {
case .hide:
return false
case let .item(rhsPeer, _, rhsSortIndex):
if lhsSortIndex != rhsSortIndex {
return lhsSortIndex < rhsSortIndex
@ -72,6 +81,26 @@ final class PeerSelectionScreenComponent: Component {
func item(listNode: ContentListNode) -> ListViewItem {
switch self {
case .hide:
return ItemListPeerActionItem(
presentationData: ItemListPresentationData(listNode.presentationData),
icon: PresentationResourcesItemList.hideIconImage(listNode.presentationData.theme),
iconSignal: nil,
title: "Hide Personal Channel",
additionalBadgeIcon: nil,
alwaysPlain: true,
hasSeparator: true,
sectionId: 0,
height: .generic,
color: .accent,
editing: false,
action: { [weak listNode] in
guard let listNode, let parentView = listNode.parentView else {
return
}
parentView.peerSelected(peer: nil)
}
)
case let .item(peer, subscriberCount, _):
//TODO:localize
let statusText: String
@ -213,14 +242,19 @@ final class PeerSelectionScreenComponent: Component {
return true
}
func peerSelected(peer: EnginePeer) {
func peerSelected(peer: EnginePeer?) {
guard let component = self.component, let environment = self.environment else {
return
}
guard let channel = self.channels.first(where: { $0.peer.id == peer.id }) else {
return
if let peer {
guard let channel = self.channels.first(where: { $0.peer.id == peer.id }) else {
return
}
component.completion(channel)
} else {
component.completion(nil)
}
component.completion(channel)
environment.controller()?.dismiss()
}
@ -526,6 +560,9 @@ final class PeerSelectionScreenComponent: Component {
contentListNode.update(size: availableSize, insets: UIEdgeInsets(top: navigationHeight, left: environment.safeInsets.left, bottom: listBottomInset, right: environment.safeInsets.right), transition: transition)
var entries: [ContentEntry] = []
if component.initialData.channelId != nil && self.searchQuery.isEmpty {
entries.append(.hide)
}
for channel in self.channels {
if !self.searchQuery.isEmpty {
var matches = false
@ -615,7 +652,10 @@ final class PeerSelectionScreenComponent: Component {
public final class PeerSelectionScreen: ViewControllerComponentContainer {
public final class InitialData {
init() {
fileprivate let channelId: EnginePeer.Id?
init(channelId: EnginePeer.Id?) {
self.channelId = channelId
}
}
@ -631,12 +671,12 @@ public final class PeerSelectionScreen: ViewControllerComponentContainer {
private let context: AccountContext
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, completion: @escaping (ChannelInfo) -> Void) {
public init(context: AccountContext, initialData: InitialData, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, completion: @escaping (ChannelInfo?) -> Void) {
self.context = context
super.init(context: context, component: PeerSelectionScreenComponent(
context: context,
initialData: InitialData(),
initialData: initialData,
completion: completion
), navigationBarAppearance: .none, theme: .default, updatedPresentationData: updatedPresentationData)
@ -663,6 +703,19 @@ public final class PeerSelectionScreen: ViewControllerComponentContainer {
deinit {
}
public static func initialData(context: AccountContext) -> Signal<InitialData, NoError> {
return context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.PersonalChannel(id: context.account.peerId)
)
|> map { personalChannel -> InitialData in
var channelId: EnginePeer.Id?
if case let .known(value) = personalChannel, let value {
channelId = value.peerId
}
return InitialData(channelId: channelId)
}
}
@objc private func cancelPressed() {
self.dismiss()
}