Update Ghostgram features

This commit is contained in:
ichmagmaus 812
2026-03-07 18:15:32 +01:00
parent 1a3303b059
commit 24a7ec39d9
902 changed files with 148295 additions and 62348 deletions
@@ -0,0 +1,29 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "AccountPeerContextItem",
module_name = "AccountPeerContextItem",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/Postbox",
"//submodules/TelegramCore",
"//submodules/PresentationDataUtils",
"//submodules/TelegramPresentationData",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/AvatarNode",
"//submodules/ContextUI",
"//submodules/AccountContext",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,174 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import ComponentFlow
import TelegramCore
import TelegramPresentationData
import PresentationDataUtils
import ContextUI
import AvatarNode
import EmojiStatusComponent
import AccountContext
public final class AccountPeerContextItem: ContextMenuCustomItem {
let context: AccountContext
let account: Account
let peer: EnginePeer
let action: (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void
public init(context: AccountContext, account: Account, peer: EnginePeer, action: @escaping (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void) {
self.context = context
self.account = account
self.peer = peer
self.action = action
}
public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
return AccountPeerContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected)
}
}
private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustomNode {
private let item: AccountPeerContextItem
private let presentationData: PresentationData
private let getController: () -> ContextControllerProtocol?
private let actionSelected: (ContextMenuActionResult) -> Void
private let buttonNode: HighlightTrackingButtonNode
private let textNode: ImmediateTextNode
private let avatarNode: AvatarNode
private let emojiStatusView: ComponentView<Empty>
init(presentationData: PresentationData, item: AccountPeerContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
self.item = item
self.presentationData = presentationData
self.getController = getController
self.actionSelected = actionSelected
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0)
self.textNode = ImmediateTextNode()
self.textNode.isAccessibilityElement = false
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = false
let peerTitle = item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
self.textNode.attributedText = NSAttributedString(string: peerTitle, font: textFont, textColor: presentationData.theme.contextMenu.primaryColor)
self.textNode.maximumNumberOfLines = 1
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0))
self.emojiStatusView = ComponentView<Empty>()
self.buttonNode = HighlightTrackingButtonNode()
self.buttonNode.isAccessibilityElement = true
self.buttonNode.accessibilityLabel = peerTitle
super.init()
self.addSubnode(self.textNode)
self.addSubnode(self.avatarNode)
self.addSubnode(self.buttonNode)
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 18.0
let iconSideInset: CGFloat = 20.0
let verticalInset: CGFloat = 11.0
let iconSize = CGSize(width: 28.0, height: 28.0)
let standardIconWidth: CGFloat = 32.0
var rightTextInset: CGFloat = sideInset
if !iconSize.width.isZero {
rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 12.0
}
self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer)
if self.item.peer.emojiStatus != nil {
rightTextInset += 32.0
}
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))
return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in
let verticalOrigin = floor((size.height - textSize.height) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
var iconContent: EmojiStatusComponent.Content?
if case let .user(user) = self.item.peer {
if let emojiStatus = user.emojiStatus {
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
} else if user.isPremium {
iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor)
}
} else if case let .channel(channel) = self.item.peer {
if let emojiStatus = channel.emojiStatus {
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
}
}
if let iconContent {
let emojiStatusSize = self.emojiStatusView.update(
transition: .immediate,
component: AnyComponent(EmojiStatusComponent(
context: self.item.context,
animationCache: self.item.context.animationCache,
animationRenderer: self.item.context.animationRenderer,
content: iconContent,
isVisibleForAnimations: true,
action: nil
)),
environment: {},
containerSize: CGSize(width: 24.0, height: 24.0)
)
if let view = self.emojiStatusView.view {
if view.superview == nil {
self.view.addSubview(view)
}
transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: textFrame.maxX + 2.0, y: textFrame.minY + floor((textFrame.height - emojiStatusSize.height) / 2.0)), size: emojiStatusSize))
}
}
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
})
}
func updateTheme(presentationData: PresentationData) {
if let attributedText = self.textNode.attributedText {
let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText)
updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length))
self.textNode.attributedText = updatedAttributedText
}
}
@objc private func buttonPressed() {
self.performAction()
}
func canBeHighlighted() -> Bool {
return true
}
func setIsHighlighted(_ value: Bool) {
}
func updateIsHighlighted(isHighlighted: Bool) {
self.setIsHighlighted(isHighlighted)
}
func performAction() {
guard let controller = self.getController() else {
return
}
self.item.action(controller, { [weak self] result in
self?.actionSelected(result)
})
}
}
@@ -504,7 +504,7 @@ private class GiftIconLayer: SimpleLayer {
file = gift.file
case let .unique(gift):
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
if case let .model(_, fileValue, _, _) = attribute {
file = fileValue
} else if case let .backdrop(_, _, innerColor, _, _, _, _) = attribute {
color = UIColor(rgb: UInt32(bitPattern: innerColor))
@@ -570,7 +570,7 @@ private class GiftIconLayer: SimpleLayer {
file = gift.file
case let .unique(gift):
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
if case let .model(_, fileValue, _, _) = attribute {
file = fileValue
} else if case let .backdrop(_, _, innerColor, _, _, _, _) = attribute {
color = UIColor(rgb: UInt32(bitPattern: innerColor))
@@ -1,5 +1,16 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
sgdeps = [
"//Swiftgram/SGSettingsUI:SGSettingsUI",
"//Swiftgram/SGStrings:SGStrings",
"//Swiftgram/SGSimpleSettings:SGSimpleSettings",
"//Swiftgram/SGRegDate:SGRegDate",
"//Swiftgram/SGRegDateScheme:SGRegDateScheme",
"//Swiftgram/SGDebugUI:SGDebugUI",
]
swift_library(
name = "PeerInfoScreen",
module_name = "PeerInfoScreen",
@@ -9,7 +20,7 @@ swift_library(
copts = [
"-warnings-as-errors",
],
deps = [
deps = sgdeps + [
"//submodules/AccountContext",
"//submodules/AccountUtils",
"//submodules/ActionSheetPeerItem",
@@ -173,6 +184,7 @@ swift_library(
"//submodules/TelegramUI/Components/AvatarComponent",
"//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent",
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
"//submodules/TelegramUI/Components/PeerInfo/AccountPeerContextItem",
],
visibility = [
"//visibility:public",
@@ -1,3 +1,5 @@
import SGRegDateScheme
import SGRegDate
import Foundation
import UIKit
import Postbox
@@ -383,6 +385,8 @@ final class PeerInfoPersonalChannelData: Equatable {
}
final class PeerInfoScreenData {
let regDate: RegDate?
let channelCreationTimestamp: Int32?
let peer: Peer?
let chatPeer: Peer?
let savedMessagesPeer: Peer?
@@ -437,6 +441,8 @@ final class PeerInfoScreenData {
}
init(
regDate: RegDate? = nil,
channelCreationTimestamp: Int32? = nil,
peer: Peer?,
chatPeer: Peer?,
savedMessagesPeer: Peer?,
@@ -480,6 +486,8 @@ final class PeerInfoScreenData {
savedMusicContext: ProfileSavedMusicContext?,
savedMusicState: ProfileSavedMusicContext.State?
) {
self.regDate = regDate
self.channelCreationTimestamp = channelCreationTimestamp
self.peer = peer
self.chatPeer = chatPeer
self.savedMessagesPeer = savedMessagesPeer
@@ -937,6 +945,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
combineLatest(notificationExceptions, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get()),
combineLatest(context.account.viewTracker.featuredStickerPacks(), archivedStickerPacks),
hasPassport,
(context.watchManager?.watchAppInstalled ?? .single(false)),
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]),
context.engine.notices.getServerProvidedSuggestions(),
context.engine.data.get(
@@ -955,7 +964,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
starsState,
tonState
)
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel, starsState, tonState -> PeerInfoScreenData in
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel, starsState, tonState -> PeerInfoScreenData in
let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications
let (featuredStickerPacks, archivedStickerPacks) = stickerPacks
@@ -968,6 +977,10 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
var enableQRLogin = false
let appConfiguration = accountPreferences.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self)
// MARK: Swiftgram
if let appConfiguration, appConfiguration.sgWebSettings.global.qrLogin {
enableQRLogin = true
}
if let appConfiguration, let data = appConfiguration.data, let enableQR = data["qr_login_camera"] as? Bool, enableQR {
enableQRLogin = true
}
@@ -998,7 +1011,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
userLimits: peer?.isPremium == true ? limits.1 : limits.0,
bots: bots,
hasPassport: hasPassport,
hasWatchApp: false,
hasWatchApp: hasWatchApp,
enableQRLogin: enableQRLogin
)
@@ -1443,6 +1456,7 @@ func peerInfoScreenData(
let savedMusicContext = ProfileSavedMusicContext(account: context.account, peerId: peerId)
return combineLatest(
Signal<RegDate?, NoError>.single(nil) |> then (getRegDate(context: context, peerId: peerId.id._internalGetInt64Value())),
context.account.viewTracker.peerView(peerId, updateData: true),
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: isMyProfile, chatLocationContextHolder: chatLocationContextHolder, sharedMediaFromForumTopic: sharedMediaFromForumTopic),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
@@ -1465,7 +1479,7 @@ func peerInfoScreenData(
webAppPermissions,
savedMusicContext.state
)
|> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, hasStoryArchive, recommendedBots, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, hasBotPreviewItems, personalChannel, privacySettings, starsRevenueContextAndState, revenueContextAndState, premiumGiftOptions, webAppPermissions, savedMusicState -> PeerInfoScreenData in
|> map { regDate, peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, hasStoryArchive, recommendedBots, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, hasBotPreviewItems, personalChannel, privacySettings, starsRevenueContextAndState, revenueContextAndState, premiumGiftOptions, webAppPermissions, savedMusicState -> PeerInfoScreenData in
var availablePanes = availablePanes
if isMyProfile {
availablePanes?.insert(.stories, at: 0)
@@ -1562,6 +1576,7 @@ func peerInfoScreenData(
}
return PeerInfoScreenData(
regDate: regDate,
peer: peer,
chatPeer: peerView.peers[peerId],
savedMessagesPeer: savedMessagesPeer?._asPeer(),
@@ -1715,6 +1730,7 @@ func peerInfoScreenData(
let personalChannel = peerInfoPersonalOrLinkedChannel(context: context, peerId: peerId, isSettings: false)
return combineLatest(
getFirstMessage(context: context, peerId: peerId),
context.account.viewTracker.peerView(peerId, updateData: true),
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder, sharedMediaFromForumTopic: sharedMediaFromForumTopic),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
@@ -1735,7 +1751,7 @@ func peerInfoScreenData(
profileGiftsContext.state,
personalChannel
)
|> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState, revenueContextAndState, profileGiftsState, personalChannel -> PeerInfoScreenData in
|> map { firstMessage, peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState, revenueContextAndState, profileGiftsState, personalChannel -> PeerInfoScreenData in
var availablePanes = availablePanes
if let hasStories {
if hasStories {
@@ -1807,6 +1823,7 @@ func peerInfoScreenData(
}
return PeerInfoScreenData(
channelCreationTimestamp: firstMessage?.timestamp,
peer: peerView.peers[peerId],
chatPeer: peerView.peers[peerId],
savedMessagesPeer: nil,
@@ -2049,6 +2066,7 @@ func peerInfoScreenData(
let isPremiumRequiredForStoryPosting: Signal<Bool, NoError> = isPremiumRequiredForStoryPosting(context: context)
return combineLatest(queue: .mainQueue(),
Signal<Message?, NoError>.single(nil) |> then (getFirstMessage(context: context, peerId: peerId)),
context.account.viewTracker.peerView(groupId, updateData: true),
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder, sharedMediaFromForumTopic: sharedMediaFromForumTopic),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
@@ -2068,7 +2086,7 @@ func peerInfoScreenData(
isPremiumRequiredForStoryPosting,
starsRevenueContextAndState
)
|> mapToSignal { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState -> Signal<PeerInfoScreenData, NoError> in
|> mapToSignal { firstMessage, peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, threadData, preferencesView, accountIsPremium, hasSavedMessages, hasSavedMessagesChats, hasSavedMessageTags, isPremiumRequiredForStoryPosting, starsRevenueContextAndState -> Signal<PeerInfoScreenData, NoError> in
var discussionPeer: Peer?
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
discussionPeer = peer
@@ -2142,7 +2160,24 @@ func peerInfoScreenData(
let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? .defaultValue
// MARK: Swiftgram
var channelCreationTimestamp = firstMessage?.timestamp
if groupId.namespace == Namespaces.Peer.CloudChannel, let firstMessage {
for media in firstMessage.media {
if let action = media as? TelegramMediaAction {
if case let .channelMigratedFromGroup(_, legacyGroupId) = action.action {
if let legacyGroup = firstMessage.peers[legacyGroupId] as? TelegramGroup {
if legacyGroup.creationDate != 0 {
channelCreationTimestamp = legacyGroup.creationDate
}
}
}
}
}
}
return .single(PeerInfoScreenData(
channelCreationTimestamp: channelCreationTimestamp,
peer: peerView.peers[groupId],
chatPeer: peerView.peers[groupId],
savedMessagesPeer: nil,
@@ -2425,8 +2460,8 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
result.append(.message)
}
result.append(.mute)
if case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), !channel.hasPermission(.manageDirect) {
} else if hasDiscussion {
/* /* MARK: Swiftgram */ if case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), !channel.hasPermission(.manageDirect) {
} else*/ if hasDiscussion {
result.append(.discussion)
}
result.append(.search)
@@ -2596,3 +2631,20 @@ private func isPremiumRequiredForStoryPosting(context: AccountContext) -> Signal
}
)
}
// MARK: Swiftgram
private func getFirstMessage(context: AccountContext, peerId: PeerId) -> Signal<Message?, NoError> {
return context.engine.messages.getMessagesLoadIfNecessary([MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: 1)])
|> `catch` { _ in
return .single(.result([]))
}
|> mapToSignal { result -> Signal<[Message], NoError> in
guard case let .result(result) = result else {
return .complete()
}
return .single(result)
}
|> map { $0.first }
}
@@ -28,6 +28,7 @@ import AnimationCache
import MultiAnimationRenderer
import ComponentDisplayAdapters
import ChatTitleView
import SGSimpleSettings
import AppBundle
import AvatarVideoNode
import PeerInfoVisualMediaPaneNode
@@ -207,7 +208,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private var currentStarRating: TelegramStarRating?
private var currentPendingStarRating: TelegramStarPendingRating?
init(context: AccountContext, controller: PeerInfoScreenImpl, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, isMyProfile: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) {
init(hidePhoneInSettings: Bool = false, context: AccountContext, controller: PeerInfoScreenImpl, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, isMyProfile: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) {
self.context = context
self.controller = controller
self.isAvatarExpanded = avatarInitiallyExpanded
@@ -1236,11 +1237,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
smallTitleAttributes = MultiScaleTextState.Attributes(font: Font.medium(28.0), color: .white, shadowColor: titleShadowColor)
if self.isSettings, let user = peer as? TelegramUser {
var subtitle = formatPhoneNumber(context: self.context, number: user.phone ?? "")
if let mainUsername = user.addressName, !mainUsername.isEmpty {
subtitle = "\(subtitle) • @\(mainUsername)"
let hidePhoneInSettings = SGSimpleSettings.shared.hidePhoneInSettings
var subtitleComponents: [String] = []
if !hidePhoneInSettings, let phone = user.phone, !phone.isEmpty {
subtitleComponents.append(formatPhoneNumber(context: self.context, number: phone))
}
if let mainUsername = user.addressName, !mainUsername.isEmpty {
subtitleComponents.append("@\(mainUsername)")
}
let subtitle = subtitleComponents.joined(separator: "")
subtitleStringText = subtitle
subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: .white)
smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor)
@@ -2859,4 +2864,3 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateAnchorPoint(layer: self.avatarListNode.maskNode.layer, anchorPoint: maskAnchorPoint)
}
}
@@ -9,6 +9,7 @@ import AccountContext
import StatisticsUI
final class PeerInfoInteraction {
let notifyTextCopied: () -> Void
let openChat: (EnginePeer.Id?) -> Void
let openUsername: (String, Bool, Promise<Bool>?) -> Void
let openPhone: (String, ASDisplayNode, ContextGesture?, Promise<Bool>?) -> Void
@@ -85,6 +86,7 @@ final class PeerInfoInteraction {
let getController: () -> ViewController?
init(
notifyTextCopied: @escaping () -> Void,
openUsername: @escaping (String, Bool, Promise<Bool>?) -> Void,
openPhone: @escaping (String, ASDisplayNode, ContextGesture?, Promise<Bool>?) -> Void,
editingOpenNotificationSettings: @escaping () -> Void,
@@ -160,6 +162,7 @@ final class PeerInfoInteraction {
displayAutoTranslateLocked: @escaping () -> Void,
getController: @escaping () -> ViewController?
) {
self.notifyTextCopied = notifyTextCopied
self.openUsername = openUsername
self.openPhone = openPhone
self.editingOpenNotificationSettings = editingOpenNotificationSettings
@@ -129,7 +129,7 @@ private final class GiftsTabItemComponent: Component {
file = gift.file
case let .unique(gift):
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
if case let .model(_, fileValue, _, _) = attribute {
file = fileValue
}
}
@@ -270,7 +270,7 @@ final class PeerInfoPaneTabsContainerPaneNode: ASDisplayNode {
file = gift.file
case let .unique(gift):
for attribute in gift.attributes {
if case let .model(_, fileValue, _) = attribute {
if case let .model(_, fileValue, _, _) = attribute {
file = fileValue
}
}
@@ -653,7 +653,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
private let initialPaneKey: PeerInfoPaneKey?
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?, initialGiftCollectionId: Int64?) {
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?, initialGiftCollectionId: Int64?, switchToMediaTarget: PeerInfoSwitchToMediaTarget? = nil) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
@@ -1,3 +1,8 @@
// MARK: Swiftgram
import SGSimpleSettings
import SGSettingsUI
import SGStrings
import CountrySelectionUI
import Foundation
import UIKit
import Display
@@ -19,9 +24,10 @@ import PeerNameColorItem
import BoostLevelIconComponent
private let enabledPublicBioEntities: EnabledEntityTypes = [.allUrl, .mention, .hashtag]
private let enabledPrivateBioEntities: EnabledEntityTypes = [.internalUrl, .mention, .hashtag]
private let enabledPrivateBioEntities: EnabledEntityTypes = [.allUrl, .mention, .hashtag] // MARK: Swiftgram
enum InfoSection: Int, CaseIterable {
case swiftgram
case groupLocation
case calls
case personalChannel
@@ -35,12 +41,19 @@ enum InfoSection: Int, CaseIterable {
case botAffiliateProgram
}
func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation, isOpenedFromChat: Bool, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] {
func infoItems(nearestChatParticipant: (String?, Int32?), showProfileId: Bool, data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation, isOpenedFromChat: Bool, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] {
guard let data = data else {
return []
}
var currentPeerInfoSection: InfoSection = .peerInfo
// MARK: Swiftgram
var sgItemId = 0
var idText = ""
var isMutualContact = false
// var isUser = false
// let lang = presentationData.strings.baseLanguageCode
var items: [InfoSection: [PeerInfoScreenItem]] = [:]
for section in InfoSection.allCases {
@@ -98,6 +111,11 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
let ItemBotAddToChatInfo = 9003
let ItemVerification = 9004
// MARK: Swiftgram
isMutualContact = user.flags.contains(.mutualContact)
idText = String(user.id.id._internalGetInt64Value())
// isUser = true
if !callMessages.isEmpty {
items[.calls]!.append(PeerInfoScreenCallListItem(id: ItemCallList, messages: callMessages))
}
@@ -122,7 +140,7 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
}))
}
if let phone = user.phone {
if let phone = user.phone, !(SGSimpleSettings.shared.hidePhoneInSettings && isMyProfile) {
let formattedPhone = formatPhoneNumber(context: context, number: phone)
let label: String
if formattedPhone.hasPrefix("+888 ") {
@@ -516,6 +534,10 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
}
}
} else if let channel = data.peer as? TelegramChannel {
// MARK: Swiftgram
idText = "-100" + String(channel.id.id._internalGetInt64Value())
let ItemSGRecentActions = 20
let ItemUsername = 1
let ItemUsernameInfo = 2
let ItemAbout = 3
@@ -681,7 +703,7 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
if case .broadcast = channel.info {
var canEditMembers = false
if channel.hasPermission(.banMembers) {
if channel.adminRights != nil || channel.flags.contains(.isCreator) { // MARK: Swiftgram
canEditMembers = true
}
if canEditMembers {
@@ -763,6 +785,14 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
items[section]!.append(PeerInfoScreenDisclosureItem(id: ItemEdit, label: .none, text: settingsTitle, icon: UIImage(bundleImageName: "Chat/Info/SettingsIcon"), action: {
interaction.openEditing()
}))
// MARK: Swiftgram
if channel.hasPermission(.banMembers) || channel.flags.contains(.isCreator) {
items[section]!.append(PeerInfoScreenDisclosureItem(id: ItemSGRecentActions, label: .none, text: presentationData.strings.Group_Info_AdminLog, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: {
interaction.openRecentActions()
}))
}
//
}
if channel.hasPermission(.manageDirect), let personalChannel = data.personalChannel {
@@ -782,6 +812,9 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
}
}
} else if let group = data.peer as? TelegramGroup {
// MARK: Swiftgram
idText = String(group.id.id._internalGetInt64Value())
if let cachedData = data.cachedData as? CachedGroupData {
let aboutText: String?
if group.isFake {
@@ -852,6 +885,139 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
}
}
// MARK: Swiftgram
if showProfileId {
items[.swiftgram]!.append(PeerInfoScreenLabeledValueItem(id: sgItemId, label: "id: \(idText)", text: "", textColor: .primary, action: nil, longTapAction: { sourceNode in
interaction.openPeerInfoContextMenu(.copy(idText), sourceNode, nil)
}, requestLayout: { _ in
interaction.requestLayout(false)
}))
sgItemId += 1
}
if SGSimpleSettings.shared.showDC {
var dcId: Int? = nil
// var dcLocation: String = ""
var phoneCountryText = ""
var dcLabel = ""
var dcText: String = ""
if let cachedData = data.cachedData as? CachedUserData, let phoneCountry = cachedData.peerStatusSettings?.phoneCountry {
var countryName = ""
let countriesConfiguration = context.currentCountriesConfiguration.with { $0 }
if let country = countriesConfiguration.countries.first(where: { $0.id == phoneCountry }) {
countryName = country.localizedName ?? country.name
} else if phoneCountry == "FT" {
countryName = presentationData.strings.Chat_NonContactUser_AnonymousNumber
} else if phoneCountry == "TS" {
countryName = "Test"
}
phoneCountryText = emojiFlagForISOCountryCode(phoneCountry) + " " + countryName
}
if let peer = data.peer, let smallProfileImage = peer.smallProfileImage, let cloudResource = smallProfileImage.resource as? CloudPeerPhotoSizeMediaResource {
dcId = cloudResource.datacenterId
// switch (dcId) {
// case 1:
// dcLocation = "Miami"
// case 2:
// dcLocation = "Amsterdam"
// case 3:
// dcLocation = "Miami"
// case 4:
// dcLocation = "Amsterdam"
// case 5:
// dcLocation = "Singapore"
// default:
// break
// }
}
if let dcId = dcId {
dcLabel = "dc: \(dcId)"
if phoneCountryText.isEmpty {
// if !dcLocation.isEmpty {
// dcLabel += " \(dcLocation)"
// }
} else {
dcText = "\(phoneCountryText)"
}
} else if !phoneCountryText.isEmpty {
dcLabel = "dc: ?"
dcText = phoneCountryText
}
if !dcText.isEmpty || !dcLabel.isEmpty {
items[.swiftgram]!.append(PeerInfoScreenLabeledValueItem(id: sgItemId, label: dcLabel, text: dcText, textColor: .primary, action: nil, longTapAction: { sourceNode in
interaction.openPeerInfoContextMenu(.aboutDC, sourceNode, nil)
}, requestLayout: { _ in
interaction.requestLayout(false)
}))
sgItemId += 1
}
}
if SGSimpleSettings.shared.showCreationDate {
if let channelCreationTimestamp = data.channelCreationTimestamp {
let creationDateString = stringForDate(timestamp: channelCreationTimestamp, strings: presentationData.strings)
items[.swiftgram]!.append(PeerInfoScreenLabeledValueItem(id: sgItemId, label: i18n("Chat.Created", presentationData.strings.baseLanguageCode, creationDateString), text: "", action: nil, longTapAction: { sourceNode in
interaction.openPeerInfoContextMenu(.copy(creationDateString), sourceNode, nil)
}, requestLayout: { _ in
interaction.requestLayout(false)
}))
sgItemId += 1
}
}
if let invitedAt = nearestChatParticipant.1 {
let joinedDateString = stringForDate(timestamp: invitedAt, strings: presentationData.strings)
items[.swiftgram]!.append(PeerInfoScreenLabeledValueItem(id: sgItemId, label: i18n("Chat.JoinedDateTitle", presentationData.strings.baseLanguageCode, nearestChatParticipant.0 ?? "chat") , text: joinedDateString, action: nil, longTapAction: { sourceNode in
interaction.openPeerInfoContextMenu(.copy(joinedDateString), sourceNode, nil)
}, requestLayout: { _ in
interaction.requestLayout(false)
}))
sgItemId += 1
}
if SGSimpleSettings.shared.showRegDate {
var regDateString = ""
if let cachedData = data.cachedData as? CachedUserData, let registrationDate = cachedData.peerStatusSettings?.registrationDate {
let components = registrationDate.components(separatedBy: ".")
if components.count == 2, let first = Int32(components[0]), let second = Int32(components[1]) {
let month = first - 1
let year = second - 1900
regDateString = stringForMonth(strings: presentationData.strings, month: month, ofYear: year)
}
}
if let regDate = data.regDate, regDateString.isEmpty {
let regTimestamp = Int32((regDate.from + regDate.to) / 2)
switch (context.currentAppConfiguration.with { $0 }.sgWebSettings.global.regdateFormat) {
case "year":
regDateString = stringForDateWithoutDayAndMonth(date: Date(timeIntervalSince1970: Double(regTimestamp)), strings: presentationData.strings)
case "month":
regDateString = stringForDateWithoutDay(date: Date(timeIntervalSince1970: Double(regTimestamp)), strings: presentationData.strings)
default:
regDateString = stringForDate(timestamp: regTimestamp, strings: presentationData.strings)
}
}
if !regDateString.isEmpty {
items[.swiftgram]!.append(PeerInfoScreenLabeledValueItem(id: sgItemId, label: i18n("Chat.RegDate", presentationData.strings.baseLanguageCode), text: regDateString, action: nil, longTapAction: { sourceNode in
interaction.openPeerInfoContextMenu(.copy(regDateString), sourceNode, nil)
}, requestLayout: { _ in
interaction.requestLayout(false)
}))
sgItemId += 1
}
}
if isMutualContact {
items[.swiftgram]!.append(PeerInfoScreenLabeledValueItem(id: sgItemId, label: i18n("MutualContact.Label", presentationData.strings.baseLanguageCode), text: "", action: nil, longTapAction: { _ in }, requestLayout: { _ in
interaction.requestLayout(false)
}))
sgItemId += 1
}
var result: [(AnyHashable, [PeerInfoScreenItem])] = []
for section in InfoSection.allCases {
if let sectionItems = items[section], !sectionItems.isEmpty {
@@ -1226,7 +1392,7 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s
}
var canEditMembers = false
if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) {
if /*channel.hasPermission(.banMembers) &&*/ (channel.adminRights != nil || channel.flags.contains(.isCreator)) { // MARK: Swiftgram
canEditMembers = true
}
if canEditMembers {
@@ -1,3 +1,9 @@
// MARK: Swiftgram
import SGDebugUI
import SGSimpleSettings
import SGSettingsUI
import SGStrings
import CountrySelectionUI
import Foundation
import UIKit
import Display
@@ -114,6 +120,7 @@ import GiftViewScreen
import PeerMessagesMediaPlaylist
import EdgeEffect
import Pasteboard
import AccountPeerContextItem
public enum PeerInfoAvatarEditingMode {
case generic
@@ -144,6 +151,8 @@ enum PeerInfoMemberAction {
}
enum PeerInfoContextSubject {
case copy(String)
case aboutDC
case bio
case phone(String)
case link(customLink: String?)
@@ -153,6 +162,9 @@ enum PeerInfoContextSubject {
}
enum PeerInfoSettingsSection {
case swiftgram
case swiftgramPro
case ghostgram
case avatar
case edit
case proxy
@@ -188,7 +200,6 @@ enum PeerInfoSettingsSection {
case premiumManagement
case stars
case ton
case ghostgram
}
enum PeerInfoReportType {
@@ -213,13 +224,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let chatLocation: ChatLocation
let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
let switchToStoryFolder: Int64?
let switchToMediaTarget: PeerInfoSwitchToMediaTarget?
let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?
let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
let isSettings: Bool
let isMyProfile: Bool
let isMediaOnly: Bool
let initialExpandPanes: Bool
var initialExpandPanes: Bool
private(set) var presentationData: PresentationData
@@ -229,6 +241,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let edgeEffectView: EdgeEffectView
let headerNode: PeerInfoHeaderNode
var underHeaderContentsAlpha: CGFloat = 1.0
var regularSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:]
var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:]
let paneContainerNode: PeerInfoPaneContainerNode
@@ -262,6 +275,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let enqueueMediaMessageDisposable = MetaDisposable()
private(set) var validLayout: (ContainerViewLayout, CGFloat)?
private(set) var nearestChatParticipant: (String?, Int32?) = (nil, nil)
private(set) var showProfileId: Bool = SGSimpleSettings.shared.showProfileId // MARK: Swiftgram
private(set) var data: PeerInfoScreenData?
var state = PeerInfoState(
@@ -311,7 +326,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let twoStepAuthData = Promise<TwoStepAuthData?>(nil)
let supportPeerDisposable = MetaDisposable()
let tipsPeerDisposable = MetaDisposable()
let cachedFaq = Promise<ResolvedUrl?>(nil)
var didSetCachedFaq = false
weak var copyProtectionTooltipController: TooltipController?
weak var emojiStatusSelectionController: ViewController?
@@ -343,7 +360,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
private var didSetReady = false
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?, switchToStoryFolder: Int64?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
init(hidePhoneInSettings: Bool /* MARK: Swiftgram */, controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?, switchToStoryFolder: Int64?, switchToMediaTarget: PeerInfoSwitchToMediaTarget?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
self.controller = controller
self.context = context
self.peerId = peerId
@@ -360,6 +377,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.isMediaOnly = context.account.peerId == peerId && !isSettings && !isMyProfile
self.initialExpandPanes = initialPaneKey != nil
self.switchToStoryFolder = switchToStoryFolder
self.switchToMediaTarget = switchToMediaTarget
self.switchToGiftsTarget = switchToGiftsTarget
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
@@ -373,7 +391,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if case let .replyThread(message) = chatLocation {
forumTopicThreadId = message.threadId
}
self.headerNode = PeerInfoHeaderNode(context: context, controller: controller, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, isMyProfile: isMyProfile, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
self.headerNode = PeerInfoHeaderNode(hidePhoneInSettings: hidePhoneInSettings, context: context, controller: controller, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, isMyProfile: isMyProfile, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
var switchToGiftCollection: Int64?
switch switchToGiftsTarget {
@@ -383,13 +401,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
break
}
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder, initialGiftCollectionId: switchToGiftCollection)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder, initialGiftCollectionId: switchToGiftCollection, switchToMediaTarget: switchToMediaTarget)
super.init()
self.paneContainerNode.parentController = controller
self._interaction = PeerInfoInteraction(
notifyTextCopied: { [weak self] in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
},
openUsername: { [weak self] value, isMainUsername, progress in
self?.openUsername(value: value, isMainUsername: isMainUsername, progress: progress)
},
@@ -851,7 +873,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
})))
}
let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: strongSelf.presentationData, source: .extracted(MessageContextExtractedContentSource(sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
strongSelf.controller?.window?.presentInGlobalOverlay(controller)
})
}, openMessageReactionContextMenu: { _, _, _, _ in
@@ -1007,7 +1029,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
switch previewData {
case let .gallery(gallery):
gallery.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node, sourceRect: rect)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: gallery, sourceNode: node, sourceRect: rect)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
case .instantPage:
break
@@ -1294,7 +1316,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}))
]
}
let contextController = ContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
controller.presentInGlobalOverlay(contextController)
}
@@ -1590,35 +1612,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
strongSelf.controller?.push(controller)
}
} else {
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.3, curve: .linear))
strongSelf.state = strongSelf.state.withIsEditing(true)
var updateOnCompletion = false
if strongSelf.headerNode.isAvatarExpanded {
updateOnCompletion = true
strongSelf.headerNode.skipCollapseCompletion = true
strongSelf.headerNode.avatarListNode.avatarContainerNode.canAttachVideo = false
strongSelf.headerNode.editingContentNode.avatarNode.canAttachVideo = false
strongSelf.headerNode.avatarListNode.listContainerNode.isCollapsing = true
strongSelf.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
strongSelf.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
}
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.scrollNode.view.setContentOffset(CGPoint(), animated: false)
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
UIView.transition(with: strongSelf.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
}, completion: { _ in
if updateOnCompletion {
strongSelf.headerNode.skipCollapseCompletion = false
strongSelf.headerNode.avatarListNode.listContainerNode.isCollapsing = false
strongSelf.headerNode.avatarListNode.avatarContainerNode.canAttachVideo = true
strongSelf.headerNode.editingContentNode.avatarNode.canAttachVideo = true
strongSelf.headerNode.editingContentNode.avatarNode.reset()
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
}
})
strongSelf.activateEdit()
}
case .done, .cancel:
strongSelf.view.endEditing(true)
@@ -2003,7 +1997,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
UIView.transition(with: strongSelf.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
}, completion: nil)
}
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear))
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(SGSimpleSettings.shared.hideTabBar ? true : false, transition: .animated(duration: 0.3, curve: .linear))
case .select:
strongSelf.state = strongSelf.state.withSelectedMessageIds(Set())
if let (layout, navigationHeight) = strongSelf.validLayout {
@@ -2060,6 +2054,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
guard let self else {
return
}
self.underHeaderContentsAlpha = alpha
if !self.state.isEditing {
for (_, section) in self.regularSections {
transition.updateAlpha(node: section, alpha: alpha)
@@ -2094,9 +2089,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|> map { data -> Bool in
return data?.hasSecretValues ?? false
}
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
screenData = peerInfoScreenSettingsData(context: context, peerId: peerId, accountsAndPeers: self.accountsAndPeers.get(), activeSessionsContextAndCount: self.activeSessionsContextAndCount.get(), notificationExceptions: self.notificationExceptions.get(), privacySettings: self.privacySettings.get(), archivedStickerPacks: self.archivedPacks.get(), hasPassport: hasPassport, starsContext: starsContext, tonContext: tonContext)
@@ -2326,7 +2319,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}, synchronousLoad: true)
galleryController.setHintWillBePresentedInPreviewingContext(true)
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
}
@@ -2456,14 +2449,29 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self?.updateNavigation(transition: .immediate, additive: true, animateHeader: true)
}
let nearestChatParticipantSignal = .single((nil, nil)) |> then(self.fetchNearestChatParticipant()) |> distinctUntilChanged { lhs, rhs in
if lhs.0 != rhs.0 {
return false
}
if lhs.1 != rhs.1 {
return false
}
return true
}
self.dataDisposable = combineLatest(
queue: Queue.mainQueue(),
nearestChatParticipantSignal,
screenData,
self.forceIsContactPromise.get()
).startStrict(next: { [weak self] data, forceIsContact in
).startStrict(next: { [weak self] nearestChatParticipant, data, forceIsContact in
guard let strongSelf = self else {
return
}
// MARK: Swiftgram
strongSelf.showProfileId = SGSimpleSettings.shared.showProfileId
//
strongSelf.nearestChatParticipant = nearestChatParticipant
if data.isContact && forceIsContact {
strongSelf.forceIsContactPromise.set(false)
} else {
@@ -2645,6 +2653,38 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
var canAttachVideo: Bool?
func activateEdit() {
(self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.3, curve: .linear))
self.state = self.state.withIsEditing(true)
var updateOnCompletion = false
if self.headerNode.isAvatarExpanded {
updateOnCompletion = true
self.headerNode.skipCollapseCompletion = true
self.headerNode.avatarListNode.avatarContainerNode.canAttachVideo = false
self.headerNode.editingContentNode.avatarNode.canAttachVideo = false
self.headerNode.avatarListNode.listContainerNode.isCollapsing = true
self.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
self.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
}
if let (layout, navigationHeight) = self.validLayout {
self.scrollNode.view.setContentOffset(CGPoint(), animated: false)
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
UIView.transition(with: self.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
}, completion: { _ in
if updateOnCompletion {
self.headerNode.skipCollapseCompletion = false
self.headerNode.avatarListNode.listContainerNode.isCollapsing = false
self.headerNode.avatarListNode.avatarContainerNode.canAttachVideo = true
self.headerNode.editingContentNode.avatarNode.canAttachVideo = true
self.headerNode.editingContentNode.avatarNode.reset()
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
}
})
}
private func updateData(_ data: PeerInfoScreenData) {
let previousData = self.data
var previousMemberCount: Int?
@@ -2820,6 +2860,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.ignoreScrolling = true
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: paneAreaExpansionFinalPoint), size: self.scrollNode.bounds.size))
self.ignoreScrolling = false
self.headerNode.headerEdgeEffectContainer.center = CGPoint(x: 0.0, y: self.scrollNode.view.contentOffset.y)
self.updateNavigation(transition: transition, additive: false, animateHeader: true)
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: transition, additive: true)
@@ -4111,7 +4152,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
}
private func openParticipantsSection(section: PeerInfoParticipantsSection) {
public func openParticipantsSection(section: PeerInfoParticipantsSection) { // MARK: Swiftgram
guard let data = self.data, let peer = data.peer else {
return
}
@@ -4660,7 +4701,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
if let rootController = self.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
let coordinator = rootController.openStoryCamera(customTarget: self.peerId == self.context.account.peerId ? nil : .peer(self.peerId), resumeLiveStream: false, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut())
let coordinator = rootController.openStoryCamera(mode: .photo, customTarget: self.peerId == self.context.account.peerId ? nil : .peer(self.peerId), resumeLiveStream: false, transitionIn: cameraTransitionIn, transitionedIn: {}, transitionOut: self.storyCameraTransitionOut())
coordinator?.animateIn()
}
case .channelBoostRequired:
@@ -4799,35 +4840,57 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.headerNode.navigationButtonContainer.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
if self.isSettings {
self.setupFaqIfNeeded()
if let settings = self.data?.globalSettings {
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Settings_Search, hasBackground: true, hasSeparator: true, contentNode: SettingsSearchContainerNode(context: self.context, openResult: { [weak self] result in
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
result.present(strongSelf.context, navigationController, { [weak self] mode, controller in
if let strongSelf = self {
switch mode {
case .push:
if let controller = controller {
strongSelf.controller?.push(controller)
self.searchDisplayController = SearchDisplayController(
presentationData: self.presentationData,
mode: .navigation,
placeholder: self.presentationData.strings.Settings_Search,
hasBackground: true,
hasSeparator: true,
contentNode: SettingsSearchContainerNode(
context: self.context,
openResult: { [weak self] result in
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
result.present(strongSelf.context, navigationController, { [weak self] mode, controller in
if let strongSelf = self {
switch mode {
case .push:
if let controller = controller {
strongSelf.controller?.push(controller)
}
case .modal:
if let controller = controller {
strongSelf.controller?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet, completion: { [weak self] in
self?.deactivateSearch()
}))
}
case .immediate:
if let controller = controller {
strongSelf.controller?.present(controller, in: .window(.root), with: nil)
}
case .dismiss:
strongSelf.deactivateSearch()
}
case .modal:
if let controller = controller {
strongSelf.controller?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet, completion: { [weak self] in
self?.deactivateSearch()
}))
}
case .immediate:
if let controller = controller {
strongSelf.controller?.present(controller, in: .window(.root), with: nil)
}
case .dismiss:
strongSelf.deactivateSearch()
}
}
})
}
})
}
}, resolvedFaqUrl: self.cachedFaq.get(), exceptionsList: .single(settings.notificationExceptions), archivedStickerPacks: .single(settings.archivedStickerPacks), privacySettings: .single(settings.privacySettings), hasTwoStepAuth: self.hasTwoStepAuth.get(), twoStepAuthData: self.twoStepAccessConfiguration.get(), activeSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.0 }, webSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.2 }), cancel: { [weak self] in
self?.deactivateSearch()
}, searchBarIsExternal: true)
},
resolvedFaqUrl: self.cachedFaq.get(),
exceptionsList: .single(settings.notificationExceptions),
archivedStickerPacks: .single(settings.archivedStickerPacks),
privacySettings: .single(settings.privacySettings),
hasTwoStepAuth: self.hasTwoStepAuth.get(),
twoStepAuthData: self.twoStepAccessConfiguration.get(),
activeSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.0 },
webSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.2 }
),
cancel: { [weak self] in
self?.deactivateSearch()
},
searchBarIsExternal: true
)
}
} else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .members = currentPaneKey {
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, hasSeparator: true, contentNode: ChannelMembersSearchContainerNode(context: self.context, forceTheme: nil, peerId: self.peerId, mode: .searchMembers, filters: [], searchContext: self.groupMembersSearchContext, openPeer: { [weak self] peer, participant in
@@ -4971,8 +5034,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.searchDisplayController = nil
searchDisplayController.deactivate(placeholder: nil)
controller.dismissAllTooltips()
if self.isSettings {
(self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.4, curve: .spring))
(self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(SGSimpleSettings.shared.hideTabBar ? true : false, transition: .animated(duration: 0.4, curve: .spring))
controller.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .animated(duration: 0.4, curve: .spring))
}
@@ -5076,7 +5141,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: strongSelf.peerId), subject: .message(id: .id(index.id), highlight: nil, timecode: nil, setupReply: false), botStart: nil, mode: .standard(.previewing), params: nil)
chatController.canReadHistory.set(false)
let contextController = ContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
strongSelf.controller?.presentInGlobalOverlay(contextController)
}
)
@@ -5268,7 +5333,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
insets.left += sectionInset
insets.right += sectionInset
let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, chatLocation: self.chatLocation, isOpenedFromChat: self.isOpenedFromChat, isMyProfile: self.isMyProfile)
let items = self.isSettings ? settingsItems(showProfileId: self.showProfileId, data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(nearestChatParticipant: self.nearestChatParticipant, showProfileId: self.showProfileId, data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, chatLocation: self.chatLocation, isOpenedFromChat: self.isOpenedFromChat, isMyProfile: self.isMyProfile)
contentHeight += headerHeight
if !((self.isSettings || self.isMyProfile) && self.state.isEditing) {
@@ -5294,7 +5359,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if wasAdded && transition.isAnimated && (self.isSettings || self.isMyProfile) && !self.state.isEditing {
sectionNode.alpha = 0.0
transition.updateAlpha(node: sectionNode, alpha: 1.0, delay: 0.1)
transition.updateAlpha(node: sectionNode, alpha: self.underHeaderContentsAlpha, delay: 0.1)
}
let sectionWidth = layout.size.width - insets.left - insets.right
@@ -5313,7 +5378,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if wasAdded && transition.isAnimated && (self.isSettings || self.isMyProfile) && !self.state.isEditing {
} else {
transition.updateAlpha(node: sectionNode, alpha: self.state.isEditing ? 0.0 : 1.0)
transition.updateAlpha(node: sectionNode, alpha: self.state.isEditing ? 0.0 : self.underHeaderContentsAlpha)
}
if !sectionHeight.isZero && !self.state.isEditing {
contentHeight += sectionHeight
@@ -5689,7 +5754,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let navigationBarHeight: CGFloat = !self.isSettings && layout.isModalOverlay ? 68.0 : 60.0
let paneContainerTopInset = navigationBarHeight + (layout.statusBarHeight ?? 0.0)
self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, topInset: paneContainerTopInset, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition)
self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, topInset: paneContainerTopInset, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: self.initialExpandPanes ? 1.0 : effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition)
transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight)))
var searchBarContainerY: CGFloat = layout.statusBarHeight ?? 0.0
@@ -5705,6 +5770,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} else {
if self.isSettings {
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
if SGSimpleSettings.shared.hideTabBar { leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .back, isForExpandedView: false)) }
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
} else if self.isMyProfile {
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
@@ -6159,6 +6225,21 @@ public enum PeerInfoSwitchToGiftsTarget {
case collection(Int64)
}
public struct PeerInfoSwitchToMediaTarget {
public enum Kind {
case photoVideo
case file
}
public let kind: Kind
public let messageIndex: EngineMessage.Index
public init(kind: Kind, messageIndex: EngineMessage.Index) {
self.kind = kind
self.messageIndex = messageIndex
}
}
public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder {
let context: AccountContext
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
@@ -6179,6 +6260,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
private let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?
private let switchToGroupsInCommon: Bool
private let switchToStoryFolder: Int64?
private let switchToMediaTarget: PeerInfoSwitchToMediaTarget?
private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
let chatLocation: ChatLocation
private let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
@@ -6199,6 +6281,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
var avatarPickerHolder: Any?
private let hidePhoneInSettings: Bool
var controllerNode: PeerInfoScreenNode {
return self.displayNode as! PeerInfoScreenNode
}
@@ -6214,6 +6298,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
return self.controllerNode.privacySettings
}
public var twoStepAuthData: Promise<TwoStepAuthData?> {
return self.controllerNode.twoStepAuthData
}
override public var customNavigationData: CustomViewControllerNavigationData? {
get {
if !self.isSettings {
@@ -6237,8 +6325,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
var didAppear: Bool = false
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
public init(
hidePhoneInSettings: Bool = SGSimpleSettings.defaultValues[SGSimpleSettings.Keys.hidePhoneInSettings.rawValue] as! Bool,
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
peerId: PeerId,
@@ -6258,8 +6347,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
switchToGiftsTarget: PeerInfoSwitchToGiftsTarget? = nil,
switchToGroupsInCommon: Bool = false,
switchToStoryFolder: Int64? = nil,
switchToMediaTarget: PeerInfoSwitchToMediaTarget? = nil,
) {
self.context = context
self.hidePhoneInSettings = hidePhoneInSettings
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
self.avatarInitiallyExpanded = avatarInitiallyExpanded
@@ -6276,6 +6367,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
self.switchToGiftsTarget = switchToGiftsTarget
self.switchToGroupsInCommon = switchToGroupsInCommon
self.switchToStoryFolder = switchToStoryFolder
self.switchToMediaTarget = switchToMediaTarget
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
if let forumTopicThread = forumTopicThread {
@@ -6621,8 +6713,15 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
initialPaneKey = .groupsInCommon
} else if self.switchToStoryFolder != nil {
initialPaneKey = .stories
} else if let switchToMediaTarget = self.switchToMediaTarget {
switch switchToMediaTarget.kind {
case .photoVideo:
initialPaneKey = .media
case .file:
initialPaneKey = .files
}
}
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToGiftsTarget: self.switchToGiftsTarget, switchToStoryFolder: self.switchToStoryFolder, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
self.displayNode = PeerInfoScreenNode(hidePhoneInSettings: self.hidePhoneInSettings, controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToGiftsTarget: self.switchToGiftsTarget, switchToStoryFolder: self.switchToStoryFolder, switchToMediaTarget: self.switchToMediaTarget, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())
@@ -6656,7 +6755,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
}
private func dismissAllTooltips() {
fileprivate func dismissAllTooltips() {
self.window?.forEachController({ controller in
if let controller = controller as? UndoOverlayController, !controller.keepOnParentDismissal {
controller.dismissWithCommitAction()
@@ -6687,6 +6786,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
}
public func activateEdit() {
self.controllerNode.activateEdit()
}
public func openAvatarSetup(completedWithUploadingImage: @escaping (UIImage, Signal<PeerInfoAvatarUploadStatus, NoError>) -> UIView?) {
let proceed = { [weak self] in
self?.openAvatarForEditing(completedWithUploadingImage: completedWithUploadingImage)
@@ -6828,7 +6931,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}))
})))
}
let contextController = ContextController(presentationData: presentationData, source: .reference(PeerInfoControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: presentationData, source: .reference(PeerInfoControllerContextReferenceContentSource(controller: parentController, sourceView: backButtonView, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
parentController.presentInGlobalOverlay(contextController)
})
}
@@ -6867,6 +6970,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
self.controllerNode.refreshHasPersonalChannelsIfNeeded()
self.controllerNode.initialExpandPanes = false
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
@@ -6886,6 +6990,22 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
let strings = self.presentationData.strings
var items: [ContextMenuItem] = []
// MARK: Swiftgram
#if DEBUG
items.append(.action(ContextMenuActionItem(text: "Swiftgram Debug", icon: { theme in
return generateTintedImage(image: nil, color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
guard let self = self else {
return
}
self.push(sgDebugController(context: self.context))
f(.dismissWithoutContent)
})))
#endif
//
items.append(.action(ContextMenuActionItem(text: strings.Settings_AddAccount, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
@@ -6929,7 +7049,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
})))*/
}
let controller = ContextController(presentationData: self.presentationData, source: .reference(SettingsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
let controller = makeContextController(presentationData: self.presentationData, source: .reference(SettingsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
}
@@ -6964,6 +7084,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
}
public func openEmojiStatusSetup() {
self.controllerNode.openSettings(section: .emojiStatus)
}
public func openBirthdaySetup() {
self.controllerNode.interaction.updateIsEditingBirthdate(true)
self.controllerNode.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
@@ -7044,7 +7168,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
})))
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
let contextController = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: sourceController, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
sourceController.presentInGlobalOverlay(contextController)
})
}
@@ -7178,15 +7302,20 @@ final class PeerInfoContextExtractedContentSource: ContextExtractedContentSource
final class PeerInfoContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceNode: ContextReferenceContentNode
private let sourceView: UIView
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
init(controller: ViewController, sourceNode: ASDisplayNode) {
self.controller = controller
self.sourceNode = sourceNode
self.sourceView = sourceNode.view
}
init(controller: ViewController, sourceView: UIView) {
self.controller = controller
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}
@@ -7259,3 +7388,93 @@ struct ClearPeerHistory {
}
}
}
// MARK: Swiftgram
extension PeerInfoScreenImpl {
public func tabBarItemContextActionRawUIView(sourceView: UIView, gesture: ContextGesture?) {
guard let (maybePrimary, other) = self.accountsAndPeersValue, let primary = maybePrimary else {
return
}
let strings = self.presentationData.strings
var items: [ContextMenuItem] = []
// MARK: Swiftgram
#if DEBUG
items.append(.action(ContextMenuActionItem(text: "Swiftgram Debug", icon: { theme in
return generateTintedImage(image: nil, color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
guard let self = self else {
return
}
self.push(sgDebugController(context: self.context))
f(.dismissWithoutContent)
})))
#endif
//
items.append(.action(ContextMenuActionItem(text: strings.Settings_AddAccount, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in
guard let strongSelf = self else {
return
}
strongSelf.controllerNode.openSettings(section: .addAccount)
f(.dismissWithoutContent)
})))
items.append(.custom(AccountPeerContextItem(context: self.context, account: self.context.account, peer: primary.1, action: { _, f in
f(.default)
}), true))
if !other.isEmpty {
items.append(.separator)
}
for account in other {
let id = account.0.account.id
items.append(.custom(AccountPeerContextItem(context: self.context, account: account.0.account, peer: account.1, action: { [weak self] _, f in
guard let strongSelf = self else {
return
}
strongSelf.controllerNode.switchToAccount(id: id)
f(.dismissWithoutContent)
}), true))
}
let controller = makeContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
}
}
extension PeerInfoScreenNode {
public func fetchNearestChatParticipant() -> Signal<(String?, Int32?), NoError> {
guard let navigationController = self.controller?.navigationController as? NavigationController else {
return .single((nil, nil))
}
for controller in navigationController.viewControllers.reversed() {
if let chatController = controller as? ChatController, let chatPeerId = chatController.chatLocation.peerId, [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(chatPeerId.namespace) {
return self.context.engine.peers.fetchChannelParticipant(peerId: chatPeerId, participantId: self.peerId)
|> mapToSignal { participant -> Signal<(String?, Int32?), NoError> in
if let participant = participant, case let .member(_, invitedAt, _, _, _, _) = participant {
return .single((chatController.overlayTitle, invitedAt))
} else {
return .single((nil, nil))
}
}
}
}
return .single((nil, nil))
}
}
@@ -463,7 +463,7 @@ extension PeerInfoScreenImpl {
case .accept:
(strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedPhotoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedPhotoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
if case .info = action {
self?.parentController?.openSettings()
self?.parentController?.openSettings(edit: false)
}
return false
}), in: .current)
@@ -662,7 +662,7 @@ extension PeerInfoScreenImpl {
case .accept:
(strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
if case .info = action {
self?.parentController?.openSettings()
self?.parentController?.openSettings(edit: false)
}
return false
}), in: .current)
@@ -874,7 +874,7 @@ extension PeerInfoScreenImpl {
case .accept:
(strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
if case .info = action {
self?.parentController?.openSettings()
self?.parentController?.openSettings(edit: false)
}
return false
}), in: .current)
@@ -18,6 +18,39 @@ extension PeerInfoScreenNode {
}
let context = self.context
switch subject {
case let .copy(text):
let contextMenuController = makeContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in
UIPasteboard.general.string = text
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
})])
controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in
if let controller = self?.controller, let sourceNode = sourceNode {
var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0)
if let sourceRect = sourceRect {
rect = sourceRect.insetBy(dx: 0.0, dy: 2.0)
}
return (sourceNode, rect, controller.displayNode, controller.view.bounds)
} else {
return nil
}
}))
case .aboutDC:
let contextMenuController = makeContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Passport_InfoLearnMore, accessibilityLabel: self.presentationData.strings.Passport_InfoLearnMore), action: { [weak self] in
self?.openUrl(url: "https://core.telegram.org/api/datacenter", concealed: false, external: false)
})])
controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in
if let controller = self?.controller, let sourceNode = sourceNode {
var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0)
if let sourceRect = sourceRect {
rect = sourceRect.insetBy(dx: 0.0, dy: 2.0)
}
return (sourceNode, rect, controller.displayNode, controller.view.bounds)
} else {
return nil
}
}))
case .birthday:
if let cachedData = data.cachedData as? CachedUserData, let birthday = cachedData.birthday {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@@ -750,7 +750,7 @@ extension PeerInfoScreenNode {
guard let self else {
return
}
self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in
self.context.sharedContext.openResolvedUrl(.settings(.legacy(.autoremoveMessages)), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in
guard let self else {
return
}
@@ -966,7 +966,7 @@ extension PeerInfoScreenNode {
guard let self else {
return
}
self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in
self.context.sharedContext.openResolvedUrl(.settings(.legacy(.autoremoveMessages)), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in
guard let self else {
return
}
@@ -1096,7 +1096,7 @@ extension PeerInfoScreenNode {
guard let self else {
return
}
self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in
self.context.sharedContext.openResolvedUrl(.settings(.legacy(.autoremoveMessages)), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in
guard let self else {
return
}
@@ -1,3 +1,5 @@
import SGStrings
import SGSettingsUI
import Foundation
import UIKit
import Display
@@ -15,6 +17,7 @@ import PremiumUI
import TelegramPresentationData
import PresentationDataUtils
import PasswordSetupUI
import InstantPageCache
extension PeerInfoScreenNode {
func openSettings(section: PeerInfoSettingsSection) {
@@ -44,6 +47,20 @@ extension PeerInfoScreenNode {
}
}
switch section {
case .swiftgram:
self.controller?.push(sgSettingsController(context: self.context))
case .swiftgramPro:
if self.context.sharedContext.immediateSGStatus.status > 1 {
self.controller?.push(self.context.sharedContext.makeSGProController(context: self.context))
} else {
if let payWallController = self.context.sharedContext.makeSGPayWallController(context: self.context) {
self.controller?.present(payWallController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} else {
self.controller?.present(self.context.sharedContext.makeSGUpdateIOSController(), animated: true)
}
}
case .ghostgram:
push(ghostgramSettingsController(context: self.context))
case .avatar:
self.controller?.openAvatarForEditing()
case .edit:
@@ -218,15 +235,15 @@ extension PeerInfoScreenNode {
guard let strongSelf = self else {
return
}
var maximumAvailableAccounts: Int = 3
var maximumAvailableAccounts: Int = maximumSwiftgramNumberOfAccounts
if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment {
maximumAvailableAccounts = 4
maximumAvailableAccounts = maximumSwiftgramNumberOfAccounts
}
var count: Int = 1
for (accountContext, peer, _) in accountsAndPeers {
if !accountContext.account.testingEnvironment {
if peer.isPremium {
maximumAvailableAccounts = 4
maximumAvailableAccounts = maximumSwiftgramNumberOfAccounts
}
count += 1
}
@@ -246,7 +263,23 @@ extension PeerInfoScreenNode {
navigationController.pushViewController(controller)
}
} else {
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
// MARK: Swiftgram
if count + 1 > maximumSafeNumberOfAccounts {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let alertController = textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: i18n("Auth.AccountBackupReminder", presentationData.strings.baseLanguageCode), actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
})
])
if let controller = strongSelf.controller {
controller.present(alertController, in: .window(.root))
} else {
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
}
} else {
strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment)
}
//
}
})
case .logout:
@@ -295,12 +328,19 @@ extension PeerInfoScreenNode {
if let tonContext = self.controller?.tonContext {
push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: tonContext))
}
case .ghostgram:
push(ghostgramSettingsController(context: self.context))
}
}
func setupFaqIfNeeded() {
if !self.didSetCachedFaq {
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
self.didSetCachedFaq = true
}
}
func openFaq(anchor: String? = nil) {
self.setupFaqIfNeeded()
let presentationData = self.presentationData
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
@@ -316,6 +356,7 @@ extension PeerInfoScreenNode {
let progressDisposable = progressSignal.start()
let _ = (self.cachedFaq.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] resolvedUrl in
progressDisposable.dispose()
@@ -55,10 +55,10 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, blockMessageAuthor: { _, _ in
}, deleteMessages: { _, _, f in
f(.default)
}, forwardSelectedMessages: {
}, forwardSelectedMessages: { _ in
forwardMessages()
}, forwardCurrentForwardMessages: {
}, forwardMessages: { _ in
}, forwardMessages: { _, _ in
}, updateForwardOptionsState: { _ in
}, presentForwardOptions: { _ in
}, presentReplyOptions: { _ in
@@ -193,7 +193,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, preferredGlassType: .default, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height, maxOverlayHeight: layout.size.height, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics, isMediaInputExpanded: false)
transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight)))
@@ -13,6 +13,25 @@ import ItemListPeerItem
import DeviceAccess
import TelegramStringFormatting
import PeerNameColorItem
import SGSimpleSettings
private func ghostgramSettingsMenuIcon() -> UIImage? {
let bundle = Bundle.main
let candidates: [(String, String)] = [
("GhostgramIcon@2x", "png"),
("GhostgramIcon@3x", "png"),
("GhostIcon@60x60", "png"),
("GhostIcon@58x58", "png")
]
for (name, ext) in candidates {
if let path = bundle.path(forResource: name, ofType: ext), let image = UIImage(contentsOfFile: path) {
return generateImage(CGSize(width: 29.0, height: 29.0), contextGenerator: { size, _ in
image.draw(in: CGRect(origin: .zero, size: size))
})
}
}
return PresentationResourcesSettings.ghostgram
}
enum SettingsSection: Int, CaseIterable {
case edit
@@ -20,6 +39,8 @@ enum SettingsSection: Int, CaseIterable {
case accounts
case myProfile
case proxy
case swiftgram
case swiftgramPro
case apps
case shortcuts
case advanced
@@ -28,7 +49,7 @@ enum SettingsSection: Int, CaseIterable {
case support
}
func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isExpanded: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] {
func settingsItems(showProfileId: Bool, data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isExpanded: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] {
guard let data = data else {
return []
}
@@ -80,6 +101,28 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
}))
}
// MARK: Swiftgram
if showProfileId {
var idText = ""
if let user = data.peer as? TelegramUser {
idText = String(user.id.id._internalGetInt64Value())
}
items[.edit]!.append(
PeerInfoScreenActionItem(
id: 100,
text: "ID: \(idText)",
color: .accent,
action: {
UIPasteboard.general.string = idText
interaction.notifyTextCopied()
}
)
)
}
if let settings = data.globalSettings {
if settings.premiumGracePeriod {
items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: "Your access to Telegram Premium will expire soon!", text: .markdown("Unfortunately, your latest payment didn't come through. To keep your access to exclusive features, please renew the subscription."), isWarning: true, linkAction: nil))
@@ -144,15 +187,19 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
}))
}
items[.accounts]!.append(PeerInfoScreenActionItem(id: 100, text: presentationData.strings.Settings_AddAccount, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), action: {
interaction.openSettings(.addAccount)
}))
// items[.accounts]!.append(PeerInfoScreenActionItem(id: 100, text: presentationData.strings.Settings_AddAccount, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), action: {
// interaction.openSettings(.addAccount)
// }))
}
// MARK: Swiftgram
items[.accounts]!.append(PeerInfoScreenActionItem(id: 1000, text: presentationData.strings.Settings_AddAccount, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), action: {
interaction.openSettings(.addAccount)
}))
items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_MyProfile, icon: PresentationResourcesSettings.myProfile, action: {
interaction.openSettings(.profile)
}))
items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 1001, text: "Ghostgram Settings", icon: UIImage(bundleImageName: "Settings/Menu/GhostgramSettings"), action: {
items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 1001, text: "Ghostgram Settings", icon: ghostgramSettingsMenuIcon(), action: {
interaction.openSettings(.ghostgram)
}))
@@ -174,6 +221,39 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
}
}
// let locale = presentationData.strings.baseLanguageCode
// MARK: Swiftgram
let hasNewSGFeatures = {
return false
}
let swiftgramLabel: PeerInfoScreenDisclosureItem.Label
if hasNewSGFeatures() {
swiftgramLabel = .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor)
} else {
swiftgramLabel = .none
}
let hasNewSGProFeatures = {
return false
}
let swiftgramProLabel: PeerInfoScreenDisclosureItem.Label
if hasNewSGProFeatures() {
swiftgramProLabel = .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor)
} else {
swiftgramProLabel = .none
}
let sgWebSettings = context.currentAppConfiguration.with({ $0 }).sgWebSettings
if sgWebSettings.global.paymentsEnabled || context.sharedContext.immediateSGStatus.status > 1 {
items[.swiftgram]!.append(PeerInfoScreenDisclosureItem(id: 0, label: swiftgramProLabel, text: "Swiftgram Pro", icon: nil, action: {
interaction.openSettings(.swiftgramPro)
}))
}
items[.swiftgram]!.append(PeerInfoScreenDisclosureItem(id: 1, label: swiftgramLabel, text: "Swiftgram", icon: nil, action: {
interaction.openSettings(.swiftgram)
}))
var appIndex = 1000
if let settings = data.globalSettings {
for bot in settings.bots {
@@ -298,8 +378,8 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
}))
}
if let starsState = data.starsState {
if !isPremiumDisabled || starsState.balance > StarsAmount.zero {
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 105, label: .text(""), text: presentationData.strings.Settings_SendGift, icon: PresentationResourcesSettings.premiumGift, action: {
if (!isPremiumDisabled || starsState.balance > StarsAmount.zero) && sgWebSettings.global.canGrant {
items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 105, label: .text(""), text: "Telegram Gifts", icon: PresentationResourcesSettings.premiumGift, action: {
interaction.openSettings(.premiumGift)
}))
}
@@ -422,7 +502,7 @@ func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoState, conte
interaction.openBirthdatePrivacy()
}))
if let user = data.peer as? TelegramUser {
if let user = data.peer as? TelegramUser, !SGSimpleSettings.shared.hidePhoneInSettings {
items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPhoneNumber, label: .text(user.phone.flatMap({ formatPhoneNumber(context: context, number: $0) }) ?? ""), text: presentationData.strings.Settings_PhoneNumber, action: {
interaction.openSettings(.phoneNumber)
}))
@@ -310,7 +310,7 @@ final class PeerInfoStoryGridScreenComponent: Component {
return
}
if let rootController = component.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface {
let coordinator = rootController.openStoryCamera(customTarget: nil, resumeLiveStream: false, transitionIn: nil, transitionedIn: {}, transitionOut: { _, _ in return nil })
let coordinator = rootController.openStoryCamera(mode: .photo, customTarget: nil, resumeLiveStream: false, transitionIn: nil, transitionedIn: {}, transitionOut: { _, _ in return nil })
coordinator?.animateIn()
}
}
@@ -988,7 +988,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
var items: [ContextMenuItem] = []
if canManage {
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_AddToCollection, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/AddToCollection"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
let addToCollectionItem: ContextMenuActionItem = ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_AddToCollection, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/AddToCollection"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
var subItems: [ContextMenuItem] = []
subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, textColor: .primary, icon: { theme in
@@ -1054,7 +1054,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
case let .unique(uniqueGift):
giftTitle = uniqueGift.title + " #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: currentParams.presentationData.dateTimeFormat))"
for attribute in uniqueGift.attributes {
if case let .model(_, file, _) = attribute {
if case let .model(_, file, _, _) = attribute {
giftFile = file
}
}
@@ -1089,7 +1089,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
c?.pushItems(items: .single(ContextController.Items(content: .list(subItems))))
})))
})
items.append(.action(addToCollectionItem))
items.append(.separator)
}
@@ -1274,7 +1275,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
if canManage {
items.append(.action(ContextMenuActionItem(text: gift.savedToProfile ? strings.PeerInfo_Gifts_Context_Hide : strings.PeerInfo_Gifts_Context_Show, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: gift.savedToProfile ? "Peer Info/HideIcon" : "Peer Info/ShowIcon"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
let toggleVisibilityItem: ContextMenuActionItem = ContextMenuActionItem(text: gift.savedToProfile ? strings.PeerInfo_Gifts_Context_Hide : strings.PeerInfo_Gifts_Context_Show, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: gift.savedToProfile ? "Peer Info/HideIcon" : "Peer Info/ShowIcon"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
c?.dismiss(completion: { [weak self] in
guard let self else {
return
@@ -1289,7 +1290,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
animationFile = gift.file
case let .unique(gift):
for attribute in gift.attributes {
if case let .model(_, file, _) = attribute {
if case let .model(_, file, _, _) = attribute {
animationFile = file
break
}
@@ -1316,7 +1317,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
}
})
})))
})
items.append(.action(toggleVisibilityItem))
if case let .unique(uniqueGift) = gift.gift {
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_Transfer, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/TransferIcon"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
@@ -1374,7 +1376,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
if canManage, case let .collection(id) = self.currentCollection {
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_RemoveFromCollection, textColor: .destructive, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/RemoveFromCollection"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in
let removeFromCollectionItem: ContextMenuActionItem = ContextMenuActionItem(text: strings.PeerInfo_Gifts_Context_RemoveFromCollection, textColor: .destructive, textLayout: .twoLinesMax, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/RemoveFromCollection"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in
f(.default)
guard let self else {
@@ -1393,7 +1395,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
case let .unique(uniqueGift):
giftTitle = uniqueGift.title + " #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: currentParams.presentationData.dateTimeFormat))"
for attribute in uniqueGift.attributes {
if case let .model(_, file, _) = attribute {
if case let .model(_, file, _, _) = attribute {
giftFile = file
}
}
@@ -1415,7 +1417,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
)
self.parentController?.present(undoController, in: .current)
}
})))
})
items.append(.action(removeFromCollectionItem))
}
guard !items.isEmpty else {