Profile preview

This commit is contained in:
Isaac 2024-04-02 16:28:01 +04:00
parent db60f2c82d
commit baca7c8661
19 changed files with 870 additions and 117 deletions

View File

@ -11884,3 +11884,6 @@ Sorry for the inconvenience.";
"BusinessLink.ErrorExpired" = "Link Expired";
"WebApp.AlertBiometryAccessText" = "Do you want to allow %@ to use Face ID?";
"StoryList.SubtitleArchived_1" = "1 archived post";
"StoryList.SubtitleArchived_any" = "%d archived posts";

View File

@ -19,6 +19,7 @@
@property (nonatomic, assign) bool displayEdges;
@property (nonatomic, assign) bool useLinesForPositions;
@property (nonatomic, assign) bool markPositions;
@property (nonatomic, readonly) bool knobStartedDragging;

View File

@ -42,6 +42,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
_value = _startValue;
_dotSize = 10.5f;
_minimumUndottedValue = -1;
_markPositions = true;
_lineSize = TGPhotoEditorSliderViewLineSize;
_knobPadding = TGPhotoEditorSliderViewInternalMargin;
@ -214,6 +215,12 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
{
for (NSInteger i = 0; i < self.positionsCount; i++)
{
if (!self.markPositions) {
if (i != 0 && i != self.positionsCount - 1) {
continue;
}
}
if (self.useLinesForPositions) {
CGSize lineSize = CGSizeMake(4.0, 12.0);
CGRect lineRect = CGRectMake(margin - lineSize.width / 2.0f + totalLength / (self.positionsCount - 1) * i, (sideLength - lineSize.height) / 2, lineSize.width, lineSize.height);

View File

@ -73,6 +73,7 @@ public struct PresentationResourcesSettings {
public static let stories = renderIcon(name: "Settings/Menu/Stories")
public static let premiumGift = renderIcon(name: "Settings/Menu/Gift")
public static let business = renderIcon(name: "Settings/Menu/Business", backgroundColors: [UIColor(rgb: 0xA95CE3), UIColor(rgb: 0xF16B80)])
public static let myProfile = renderIcon(name: "Settings/Menu/Profile")
public static let premium = generateImage(CGSize(width: 29.0, height: 29.0), contextGenerator: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)

View File

@ -15,6 +15,7 @@ public final class EmptyStateIndicatorComponent: Component {
public let title: String
public let text: String
public let actionTitle: String?
public let fitToHeight: Bool
public let action: () -> Void
public let additionalActionTitle: String?
public let additionalAction: () -> Void
@ -22,6 +23,7 @@ public final class EmptyStateIndicatorComponent: Component {
public init(
context: AccountContext,
theme: PresentationTheme,
fitToHeight: Bool,
animationName: String,
title: String,
text: String,
@ -32,6 +34,7 @@ public final class EmptyStateIndicatorComponent: Component {
) {
self.context = context
self.theme = theme
self.fitToHeight = fitToHeight
self.animationName = animationName
self.title = title
self.text = text
@ -48,6 +51,9 @@ public final class EmptyStateIndicatorComponent: Component {
if lhs.theme !== rhs.theme {
return false
}
if lhs.fitToHeight != rhs.fitToHeight {
return false
}
if lhs.animationName != rhs.animationName {
return false
}
@ -205,7 +211,12 @@ public final class EmptyStateIndicatorComponent: Component {
totalHeight += buttonSpacing + additionalButtonSize.height
}
var contentY = floor((availableSize.height - totalHeight) * 0.5)
var contentY: CGFloat
if component.fitToHeight {
contentY = 0.0
} else {
contentY = floor((availableSize.height - totalHeight) * 0.5)
}
if let animationView = self.animation.view {
if animationView.superview == nil {
@ -243,9 +254,13 @@ public final class EmptyStateIndicatorComponent: Component {
contentY += additionalButtonSize.height
}
if component.fitToHeight {
return CGSize(width: availableSize.width, height: totalHeight)
} else {
return availableSize
}
}
}
public func makeView() -> View {
return View(frame: CGRect())

View File

@ -11,18 +11,24 @@ import SliderComponent
public final class ListItemSliderSelectorComponent: Component {
public let theme: PresentationTheme
public let values: [String]
public let markPositions: Bool
public let selectedIndex: Int
public let title: String?
public let selectedIndexUpdated: (Int) -> Void
public init(
theme: PresentationTheme,
values: [String],
markPositions: Bool,
selectedIndex: Int,
title: String?,
selectedIndexUpdated: @escaping (Int) -> Void
) {
self.theme = theme
self.values = values
self.markPositions = markPositions
self.selectedIndex = selectedIndex
self.title = title
self.selectedIndexUpdated = selectedIndexUpdated
}
@ -33,14 +39,21 @@ public final class ListItemSliderSelectorComponent: Component {
if lhs.values != rhs.values {
return false
}
if lhs.markPositions != rhs.markPositions {
return false
}
if lhs.selectedIndex != rhs.selectedIndex {
return false
}
if lhs.title != rhs.title {
return false
}
return true
}
public final class View: UIView, ListSectionComponent.ChildView {
private var titles: [ComponentView<Empty>] = []
private var titles: [Int: ComponentView<Empty>] = [:]
private var mainTitle: ComponentView<Empty>?
private var slider = ComponentView<Empty>()
private var component: ListItemSliderSelectorComponent?
@ -67,15 +80,24 @@ public final class ListItemSliderSelectorComponent: Component {
let titleAreaWidth: CGFloat = availableSize.width - titleSideInset * 2.0
var validIds: [Int] = []
for i in 0 ..< component.values.count {
if component.title != nil {
if i != 0 && i != component.values.count - 1 {
continue
}
}
validIds.append(i)
var titleTransition = transition
let title: ComponentView<Empty>
if self.titles.count > i {
title = self.titles[i]
if let current = self.titles[i] {
title = current
} else {
titleTransition = titleTransition.withAnimation(.none)
title = ComponentView()
self.titles.append(title)
self.titles[i] = title
}
let titleSize = title.update(
transition: .immediate,
@ -103,11 +125,48 @@ public final class ListItemSliderSelectorComponent: Component {
titleTransition.setPosition(view: titleView, position: titleFrame.center)
}
}
if self.titles.count > component.values.count {
for i in component.values.count ..< self.titles.count {
self.titles[i].view?.removeFromSuperview()
var removeIds: [Int] = []
for (id, title) in self.titles {
if !validIds.contains(id) {
removeIds.append(id)
title.view?.removeFromSuperview()
}
}
for id in removeIds {
self.titles.removeValue(forKey: id)
}
if let title = component.title {
let mainTitle: ComponentView<Empty>
var mainTitleTransition = transition
if let current = self.mainTitle {
mainTitle = current
} else {
mainTitleTransition = mainTitleTransition.withAnimation(.none)
mainTitle = ComponentView()
self.mainTitle = mainTitle
}
let mainTitleSize = mainTitle.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: title, font: Font.regular(16.0), textColor: component.theme.list.itemPrimaryTextColor))
)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)
)
let mainTitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - mainTitleSize.width) * 0.5), y: 10.0), size: mainTitleSize)
if let mainTitleView = mainTitle.view {
if mainTitleView.superview == nil {
self.addSubview(mainTitleView)
}
mainTitleView.bounds = CGRect(origin: CGPoint(), size: mainTitleFrame.size)
mainTitleTransition.setPosition(view: mainTitleView, position: mainTitleFrame.center)
}
} else {
if let mainTitle = self.mainTitle {
self.mainTitle = nil
mainTitle.view?.removeFromSuperview()
}
self.titles.removeLast(self.titles.count - component.values.count)
}
let sliderSize = self.slider.update(
@ -115,6 +174,7 @@ public final class ListItemSliderSelectorComponent: Component {
component: AnyComponent(SliderComponent(
valueCount: component.values.count,
value: component.selectedIndex,
markPositions: component.markPositions,
trackBackgroundColor: component.theme.list.controlSecondaryColor,
trackForegroundColor: component.theme.list.itemAccentColor,
valueUpdated: { [weak self] value in

View File

@ -33,6 +33,8 @@ swift_library(
"//submodules/UndoUI",
"//submodules/TextFormat",
"//submodules/Components/HierarchyTrackingLayer",
"//submodules/TelegramUI/Components/ListSectionComponent",
"//submodules/TelegramUI/Components/ListItemSliderSelectorComponent",
],
visibility = [
"//visibility:public",

View File

@ -21,6 +21,8 @@ import AnimatedTextComponent
import TextFormat
import AudioToolbox
import PremiumLockButtonSubtitleComponent
import ListSectionComponent
import ListItemSliderSelectorComponent
final class PeerAllowedReactionsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -57,6 +59,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
private var reactionsTitleText: ComponentView<Empty>?
private var reactionsInfoText: ComponentView<Empty>?
private var reactionInput: ComponentView<Empty>?
private var reactionCountSection: ComponentView<Empty>?
private let actionButton = ComponentView<Empty>()
private var reactionSelectionControl: ComponentView<Empty>?
@ -82,6 +85,8 @@ final class PeerAllowedReactionsScreenComponent: Component {
private var displayInput: Bool = false
private var recenterOnCaret: Bool = false
private var allowedReactionCount: Int = 11
private var isApplyingSettings: Bool = false
private var applyDisposable: Disposable?
@ -775,6 +780,86 @@ final class PeerAllowedReactionsScreenComponent: Component {
}
contentHeight += reactionsInfoTextSize.height
contentHeight += 6.0
contentHeight += 32.0
let reactionCountSection: ComponentView<Empty>
if let current = self.reactionCountSection {
reactionCountSection = current
} else {
reactionCountSection = ComponentView()
self.reactionCountSection = reactionCountSection
}
let reactionCountValueList = (1 ... 11).map { i -> String in
return "\(i)"
}
//TODO:localize
let sliderTitle: String
if self.allowedReactionCount == 1 {
sliderTitle = "1 reaction"
} else {
sliderTitle = "\(self.allowedReactionCount) reactions"
}
let reactionCountSectionSize = reactionCountSection.update(
transition: transition,
component: AnyComponent(ListSectionComponent(
theme: environment.theme,
header: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: "MAXIMUM REACTIONS PER POST",
font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor
)),
maximumNumberOfLines: 0
)),
footer: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: "Limit the number of different reactions that can be added to a post, including already published posts.",
font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor
)),
maximumNumberOfLines: 0
)),
items: [
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
theme: environment.theme,
values: reactionCountValueList.map { item in
return item
},
markPositions: false,
selectedIndex: max(0, min(reactionCountValueList.count - 1, self.allowedReactionCount - 1)),
title: sliderTitle,
selectedIndexUpdated: { [weak self] index in
guard let self else {
return
}
let index = max(1, min(reactionCountValueList.count, index + 1))
self.allowedReactionCount = index
self.state?.updated(transition: .immediate)
}
)))
],
displaySeparators: false
)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
)
let reactionCountSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: reactionCountSectionSize)
if let reactionCountSectionView = reactionCountSection.view {
if reactionCountSectionView.superview == nil {
self.scrollView.addSubview(reactionCountSectionView)
}
if animateIn {
reactionCountSectionView.frame = reactionCountSectionFrame
if !transition.animation.isImmediate {
reactionCountSectionView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
} else {
transition.setFrame(view: reactionCountSectionView, frame: reactionCountSectionFrame)
}
}
contentHeight += reactionCountSectionSize.height
} else {
if let reactionsTitleText = self.reactionsTitleText {
self.reactionsTitleText = nil
@ -814,6 +899,19 @@ final class PeerAllowedReactionsScreenComponent: Component {
}
}
}
if let reactionCountSection = self.reactionCountSection {
self.reactionCountSection = nil
if let reactionCountSectionView = reactionCountSection.view {
if !transition.animation.isImmediate {
reactionCountSectionView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak reactionCountSectionView] _ in
reactionCountSectionView?.removeFromSuperview()
})
} else {
reactionCountSectionView.removeFromSuperview()
}
}
}
}
var buttonContents: [AnyComponentWithIdentity<Empty>] = []

View File

@ -8,6 +8,7 @@ import TelegramPresentationData
public enum PeerInfoPaneKey: Int32 {
case members
case stories
case storyArchive
case media
case savedMessagesChats
case savedMessages

View File

@ -305,6 +305,7 @@ final class PeerInfoScreenData {
let linkedDiscussionPeer: Peer?
let members: PeerInfoMembersData?
let storyListContext: PeerStoryListContext?
let storyArchiveListContext: PeerStoryListContext?
let encryptionKeyFingerprint: SecretChatKeyFingerprint?
let globalSettings: TelegramGlobalSettings?
let invitations: PeerExportedInvitationsState?
@ -344,6 +345,7 @@ final class PeerInfoScreenData {
linkedDiscussionPeer: Peer?,
members: PeerInfoMembersData?,
storyListContext: PeerStoryListContext?,
storyArchiveListContext: PeerStoryListContext?,
encryptionKeyFingerprint: SecretChatKeyFingerprint?,
globalSettings: TelegramGlobalSettings?,
invitations: PeerExportedInvitationsState?,
@ -371,6 +373,7 @@ final class PeerInfoScreenData {
self.linkedDiscussionPeer = linkedDiscussionPeer
self.members = members
self.storyListContext = storyListContext
self.storyArchiveListContext = storyArchiveListContext
self.encryptionKeyFingerprint = encryptionKeyFingerprint
self.globalSettings = globalSettings
self.invitations = invitations
@ -403,7 +406,7 @@ private enum PeerInfoScreenInputData: Equatable {
public func hasAvailablePeerInfoMediaPanes(context: AccountContext, peerId: PeerId) -> Signal<Bool, NoError> {
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let mediaPanes = peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: .peer(id: peerId), chatLocationContextHolder: chatLocationContextHolder)
let mediaPanes = peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: .peer(id: peerId), isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder)
|> map { panes -> Bool in
if let panes {
return !panes.isEmpty
@ -426,8 +429,11 @@ public func hasAvailablePeerInfoMediaPanes(context: AccountContext, peerId: Peer
}
}
private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<[PeerInfoPaneKey]?, NoError> {
let tags: [(MessageTags, PeerInfoPaneKey)] = [
private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, isMyProfile: Bool, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<[PeerInfoPaneKey]?, NoError> {
var tags: [(MessageTags, PeerInfoPaneKey)] = []
if !isMyProfile {
tags = [
(.photoOrVideo, .media),
(.file, .files),
(.music, .music),
@ -435,6 +441,7 @@ private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId
(.webPage, .links),
(.gif, .gifs)
]
}
enum PaneState {
case loading
case empty
@ -545,7 +552,7 @@ public func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId, c
case .user, .channel, .group:
var signals: [Signal<Never, NoError>] = []
signals.append(context.peerChannelMemberCategoriesContextsManager.profileData(postbox: context.account.postbox, network: context.account.network, peerId: peerId, customData: peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder) |> ignoreValues) |> ignoreValues)
signals.append(context.peerChannelMemberCategoriesContextsManager.profileData(postbox: context.account.postbox, network: context.account.network, peerId: peerId, customData: peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder) |> ignoreValues) |> ignoreValues)
signals.append(context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: context.account.postbox, network: context.account.network, peerId: peerId, fetch: peerInfoProfilePhotos(context: context, peerId: peerId)) |> ignoreValues)
if case .user = inputData {
@ -843,6 +850,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
linkedDiscussionPeer: nil,
members: nil,
storyListContext: hasStories == true ? storyListContext : nil,
storyArchiveListContext: nil,
encryptionKeyFingerprint: nil,
globalSettings: globalSettings,
invitations: nil,
@ -859,7 +867,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
}
}
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<PeerInfoScreenData, NoError> {
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<PeerInfoScreenData, NoError> {
return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: isSettings)
|> mapToSignal { inputData -> Signal<PeerInfoScreenData, NoError> in
let wasUpgradedGroup = Atomic<Bool?>(value: nil)
@ -881,6 +889,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
linkedDiscussionPeer: nil,
members: nil,
storyListContext: nil,
storyArchiveListContext: nil,
encryptionKeyFingerprint: nil,
globalSettings: nil,
invitations: nil,
@ -896,7 +905,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
))
case let .user(userPeerId, secretChatId, kind):
let groupsInCommon: GroupsInCommonContext?
if [.user, .bot].contains(kind) {
if isMyProfile {
groupsInCommon = nil
} else if [.user, .bot].contains(kind) {
groupsInCommon = GroupsInCommonContext(account: context.account, peerId: userPeerId, hintGroupInCommon: hintGroupInCommon)
} else {
groupsInCommon = nil
@ -1008,6 +1019,23 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
}
|> distinctUntilChanged
let hasStoryArchive: Signal<Bool?, NoError>
var storyArchiveListContext: PeerStoryListContext?
if isMyProfile {
let storyArchiveListContextValue = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: true)
storyArchiveListContext = storyArchiveListContextValue
hasStoryArchive = storyArchiveListContextValue.state
|> map { state -> Bool? in
if !state.hasCache {
return nil
}
return !state.items.isEmpty
}
|> distinctUntilChanged
} else {
hasStoryArchive = .single(false)
}
let accountIsPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|> map { peer -> Bool in
return peer?.isPremium ?? false
@ -1092,11 +1120,12 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
return combineLatest(
context.account.viewTracker.peerView(peerId, updateData: true),
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: isMyProfile, chatLocationContextHolder: chatLocationContextHolder),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
secretChatKeyFingerprint,
status,
hasStories,
hasStoryArchive,
accountIsPremium,
savedMessagesPeer,
hasSavedMessagesChats,
@ -1104,10 +1133,15 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
hasSavedMessageTags,
peerInfoPersonalChannel(context: context, peerId: peerId, isSettings: false)
)
|> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, personalChannel -> PeerInfoScreenData in
|> map { peerView, availablePanes, globalNotificationSettings, encryptionKeyFingerprint, status, hasStories, hasStoryArchive, accountIsPremium, savedMessagesPeer, hasSavedMessagesChats, hasSavedMessages, hasSavedMessageTags, personalChannel -> PeerInfoScreenData in
var availablePanes = availablePanes
if let hasStories {
if isMyProfile {
availablePanes?.insert(.stories, at: 0)
if let hasStoryArchive, hasStoryArchive {
availablePanes?.insert(.storyArchive, at: 1)
}
} else if let hasStories {
if hasStories, peerView.peers[peerView.peerId] is TelegramUser, peerView.peerId != context.account.peerId {
availablePanes?.insert(.stories, at: 0)
}
@ -1155,6 +1189,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
linkedDiscussionPeer: nil,
members: nil,
storyListContext: storyListContext,
storyArchiveListContext: storyArchiveListContext,
encryptionKeyFingerprint: encryptionKeyFingerprint,
globalSettings: nil,
invitations: nil,
@ -1244,7 +1279,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
return combineLatest(
context.account.viewTracker.peerView(peerId, updateData: true),
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
status,
invitationsContextPromise.get(),
@ -1325,6 +1360,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
linkedDiscussionPeer: discussionPeer,
members: nil,
storyListContext: storyListContext,
storyArchiveListContext: nil,
encryptionKeyFingerprint: nil,
globalSettings: nil,
invitations: invitations,
@ -1517,7 +1553,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
return combineLatest(queue: .mainQueue(),
context.account.viewTracker.peerView(groupId, updateData: true),
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, isMyProfile: false, chatLocationContextHolder: chatLocationContextHolder),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
status,
membersData,
@ -1618,6 +1654,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
linkedDiscussionPeer: discussionPeer,
members: membersData,
storyListContext: storyListContext,
storyArchiveListContext: nil,
encryptionKeyFingerprint: nil,
globalSettings: nil,
invitations: invitations,

View File

@ -88,6 +88,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private let isOpenedFromChat: Bool
private let isSettings: Bool
private let isMyProfile: Bool
private let videoCallsEnabled: Bool
private let forumTopicThreadId: Int64?
private let chatLocation: ChatLocation
@ -179,12 +180,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
private var validLayout: (width: CGFloat, deviceMetrics: DeviceMetrics)?
init(context: AccountContext, controller: PeerInfoScreenImpl, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?, chatLocation: ChatLocation) {
init(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
self.isOpenedFromChat = isOpenedFromChat
self.isSettings = isSettings
self.isMyProfile = isMyProfile
self.videoCallsEnabled = true
self.forumTopicThreadId = forumTopicThreadId
self.chatLocation = chatLocation
@ -522,7 +524,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let credibilityIcon: CredibilityIcon
var verifiedIcon: CredibilityIcon = .none
if let peer = peer {
if peer.id == self.context.account.peerId && !self.isSettings {
if peer.id == self.context.account.peerId && !self.isSettings && !self.isMyProfile {
credibilityIcon = .none
} else if peer.isFake {
credibilityIcon = .fake
@ -535,7 +537,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
credibilityIcon = .emojiStatus(emojiStatus)
} else if peer.isVerified {
credibilityIcon = .verified
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings) {
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings || self.isMyProfile) {
credibilityIcon = .premium
} else {
credibilityIcon = .none
@ -554,7 +556,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, chatLocation: self.chatLocation, cachedData: cachedData, isContact: isContact, isSettings: isSettings, presentationData: presentationData, transition: transition)
let editingContentHeight = self.editingContentNode.update(width: width, safeInset: containerInset, statusBarHeight: statusBarHeight, navigationHeight: navigationHeight, isModalOverlay: isModalOverlay, peer: state.isEditing ? peer : nil, threadData: threadData, chatLocation: self.chatLocation, cachedData: cachedData, isContact: isContact, isSettings: isSettings || isMyProfile, presentationData: presentationData, transition: transition)
transition.updateFrame(node: self.editingContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -contentOffset), size: CGSize(width: width, height: editingContentHeight)))
let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self)
@ -694,7 +696,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else {
let backgroundTransitionStepDistance: CGFloat = 50.0
var backgroundTransitionDistance: CGFloat = navigationHeight + panelWithAvatarHeight - backgroundTransitionStepDistance
if self.isSettings {
if self.isSettings || self.isMyProfile {
backgroundTransitionDistance -= 100.0
}
if isMediaOnly {
@ -978,15 +980,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.navigationBackgroundBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
self.navigationSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
let navigationSeparatorAlpha: CGFloat
if isMediaOnly {
navigationSeparatorAlpha = 0.0
} else if state.isEditing && self.isSettings {
//navigationSeparatorAlpha = min(1.0, contentOffset / (navigationHeight * 0.5))
navigationSeparatorAlpha = 0.0
} else {
navigationSeparatorAlpha = 0.0
}
let navigationSeparatorAlpha: CGFloat = 0.0
transition.updateAlpha(node: self.navigationBackgroundBackgroundNode, alpha: 1.0 - navigationSeparatorAlpha)
transition.updateAlpha(node: self.navigationSeparatorNode, alpha: navigationSeparatorAlpha)
@ -994,7 +988,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let expandedAvatarControlsHeight: CGFloat = 61.0
var expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
if self.isSettings {
if self.isSettings || self.isMyProfile {
expandedAvatarListHeight = expandedAvatarListHeight + 60.0
} else {
expandedAvatarListHeight = expandedAvatarListHeight + 98.0
@ -1002,8 +996,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
let actionButtonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact)
let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info)
let actionButtonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact)
let buttonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info)
var isPremium = false
var isVerified = false
@ -1029,7 +1023,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if let peer = peer {
var title: String
if peer.id == self.context.account.peerId && !self.isSettings {
if peer.id == self.context.account.peerId && !self.isSettings && !self.isMyProfile {
if case .replyThread = self.chatLocation {
title = presentationData.strings.Conversation_MyNotes
} else {
@ -1069,6 +1063,26 @@ final class PeerInfoHeaderNode: ASDisplayNode {
smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor)
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white))
} else if self.isMyProfile {
let subtitleColor: UIColor
subtitleColor = .white
subtitleStringText = presentationData.strings.Presence_online
subtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor)
smallSubtitleAttributes = MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white, shadowColor: titleShadowColor)
usernameString = ("", MultiScaleTextState.Attributes(font: Font.regular(16.0), color: .white))
let (maybePanelStatusData, _, _) = panelStatusData
if let panelStatusData = maybePanelStatusData {
let subtitleColor: UIColor
if panelStatusData.isActivity {
subtitleColor = UIColor.white
} else {
subtitleColor = UIColor.white
}
panelSubtitleString = (panelStatusData.text, MultiScaleTextState.Attributes(font: Font.regular(17.0), color: subtitleColor))
}
} else if let _ = threadData {
let subtitleColor: UIColor
subtitleColor = UIColor.white
@ -1341,14 +1355,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let expandedTitleScale: CGFloat = 0.8
var bottomShadowHeight: CGFloat = 88.0
if !self.isSettings {
if !self.isSettings && !self.isMyProfile {
bottomShadowHeight += 100.0
}
let bottomShadowFrame = CGRect(origin: CGPoint(x: 0.0, y: expandedAvatarHeight - bottomShadowHeight), size: CGSize(width: width, height: bottomShadowHeight))
transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: bottomShadowFrame, beginWithCurrentState: true)
self.avatarListNode.listContainerNode.bottomShadowNode.update(size: bottomShadowFrame.size, transition: transition)
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
let singleTitleLockOffset: CGFloat = ((peer?.id == self.context.account.peerId && !self.isMyProfile) || subtitleSize.height.isZero) ? 8.0 : 0.0
let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
let titleMaxLockOffset: CGFloat = 7.0
@ -1358,14 +1372,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if self.isAvatarExpanded {
let minTitleSize = CGSize(width: titleSize.width * expandedTitleScale, height: titleSize.height * expandedTitleScale)
var minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize)
if !self.isSettings {
if !self.isSettings && !self.isMyProfile {
minTitleFrame.origin.y -= 83.0
}
titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize)
var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset
if case .regular = metrics.widthClass, !isSettings {
if case .regular = metrics.widthClass, !isSettings, !isMyProfile {
titleCollapseOffset -= 7.0
}
titleOffset = -min(titleCollapseOffset, contentOffset)
@ -1377,7 +1391,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 9.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize)
var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset
if case .regular = metrics.widthClass, !isSettings {
if case .regular = metrics.widthClass, !isSettings, !isMyProfile {
titleCollapseOffset -= 7.0
}
titleOffset = -min(titleCollapseOffset, contentOffset)
@ -1408,7 +1422,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let effectiveAreaExpansionFraction: CGFloat
if state.isEditing {
effectiveAreaExpansionFraction = 0.0
} else if isSettings {
} else if isSettings || isMyProfile {
var paneAreaExpansionDelta = (self.frame.maxY - navigationHeight) - contentOffset
paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance))
effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance
@ -1716,12 +1730,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else {
rawHeight = navigationHeight + panelWithAvatarHeight
var expandablePart: CGFloat = panelWithAvatarHeight - contentOffset
if self.isSettings {
if self.isSettings || self.isMyProfile {
expandablePart += 20.0
} else {
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId {
expandablePart = 0.0
} else if peer?.id == self.context.account.peerId {
} else if peer?.id == self.context.account.peerId && !self.isMyProfile {
expandablePart = 0.0
} else {
expandablePart += 99.0
@ -2140,7 +2154,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
if !state.isEditing {
if !isSettings {
if !isSettings && !isMyProfile {
if self.isAvatarExpanded {
resolvedHeight -= 21.0
} else {

View File

@ -365,6 +365,7 @@ private final class PeerInfoPendingPane {
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
parentController: ViewController?,
openMediaCalendar: @escaping () -> Void,
openAddStory: @escaping () -> Void,
paneDidScroll: @escaping () -> Void,
ensureRectVisible: @escaping (UIView, CGRect) -> Void,
externalDataUpdated: @escaping (ContainedViewLayoutTransition) -> Void
@ -372,8 +373,8 @@ private final class PeerInfoPendingPane {
let captureProtected = data.peer?.isCopyProtectionEnabled ?? false
let paneNode: PeerInfoPaneNode
switch key {
case .stories:
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: false, navigationController: chatControllerInteraction.navigationController, listContext: data.storyListContext)
case .stories, .storyArchive:
let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isSaved: false, isArchive: key == .storyArchive, isProfileEmbedded: true, navigationController: chatControllerInteraction.navigationController, listContext: key == .storyArchive ? data.storyArchiveListContext : data.storyListContext)
paneNode = visualPaneNode
visualPaneNode.openCurrentDate = {
openMediaCalendar()
@ -384,6 +385,9 @@ private final class PeerInfoPendingPane {
visualPaneNode.ensureRectVisible = { sourceView, rect in
ensureRectVisible(sourceView, rect)
}
visualPaneNode.emptyAction = {
openAddStory()
}
case .media:
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .photoOrVideo, captureProtected: captureProtected)
paneNode = visualPaneNode
@ -500,6 +504,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
var openMediaCalendar: (() -> Void)?
var openAddStory: (() -> Void)?
var paneDidScroll: (() -> Void)?
var ensurePaneRectVisible: ((UIView, CGRect) -> Void)?
@ -848,6 +853,9 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
openMediaCalendar: { [weak self] in
self?.openMediaCalendar?()
},
openAddStory: { [weak self] in
self?.openAddStory?()
},
paneDidScroll: { [weak self] in
self?.paneDidScroll?()
},
@ -1010,6 +1018,9 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
switch key {
case .stories:
title = presentationData.strings.PeerInfo_PaneStories
case .storyArchive:
//TODO:localize
title = "Archived Posts"
case .media:
title = presentationData.strings.PeerInfo_PaneMedia
case .files:

View File

@ -518,6 +518,7 @@ private enum PeerInfoSettingsSection {
case emojiStatus
case powerSaving
case businessSetup
case profile
}
private enum PeerInfoReportType {
@ -725,6 +726,7 @@ private enum SettingsSection: Int, CaseIterable {
case edit
case phone
case accounts
case myProfile
case proxy
case apps
case shortcuts
@ -850,6 +852,11 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
}))
}
//TODO:localize
items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 0, text: "My Profile", icon: PresentationResourcesSettings.myProfile, action: {
interaction.openSettings(.profile)
}))
if !settings.proxySettings.servers.isEmpty {
let proxyType: String
if settings.proxySettings.enabled, let activeServer = settings.proxySettings.activeServer {
@ -892,10 +899,6 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
}
}
items[.apps]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_MyStories, icon: PresentationResourcesSettings.stories, action: {
interaction.openSettings(.stories)
}))
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: {
interaction.openSettings(.savedMessages)
}))
@ -994,7 +997,7 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
return result
}
private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoState, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction) -> [(AnyHashable, [PeerInfoScreenItem])] {
private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoState, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] {
guard let data = data else {
return []
}
@ -1028,7 +1031,9 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
let ItemBirthdayHelp = 12
let ItemPeerPersonalChannel = 13
if !isMyProfile {
items[.help]!.append(PeerInfoScreenCommentItem(id: ItemNameHelp, text: presentationData.strings.EditProfile_NameAndPhotoOrVideoHelp))
}
if let cachedData = data.cachedData as? CachedUserData {
items[.bio]!.append(PeerInfoScreenMultilineInputItem(id: ItemBio, text: state.updatingBio ?? (cachedData.about ?? ""), placeholder: presentationData.strings.UserInfo_About_Placeholder, textUpdated: { updatedText in
@ -1056,6 +1061,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
birthDateString = presentationData.strings.Settings_Birthday_Add
}
if !isMyProfile {
let isEditingBirthDate = state.isEditingBirthDate
items[.birthday]!.append(PeerInfoScreenDisclosureItem(id: ItemBirthday, label: .coloredText(birthDateString, isEditingBirthDate ? .accent : .generic), text: presentationData.strings.Settings_Birthday, icon: nil, hasArrow: false, action: {
interaction.updateIsEditingBirthdate(!isEditingBirthDate)
@ -1070,6 +1076,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
}))
}
var birthdayIsForContactsOnly = false
if let birthdayPrivacy = data.globalSettings?.privacySettings?.birthday, case .enableContacts = birthdayPrivacy {
birthdayIsForContactsOnly = true
@ -1077,6 +1084,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
items[.birthday]!.append(PeerInfoScreenCommentItem(id: ItemBirthdayHelp, text: birthdayIsForContactsOnly ? presentationData.strings.Settings_Birthday_ContactsHelp : presentationData.strings.Settings_Birthday_Help, linkAction: { _ in
interaction.openBirthdatePrivacy()
}))
}
if let user = data.peer as? TelegramUser {
items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPhoneNumber, label: .text(user.phone.flatMap({ formatPhoneNumber(context: context, number: $0) }) ?? ""), text: presentationData.strings.Settings_PhoneNumber, action: {
@ -1091,7 +1099,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
interaction.openSettings(.username)
}))
if let peer = data.peer as? TelegramUser {
if !isMyProfile, let peer = data.peer as? TelegramUser {
var colors: [PeerNameColors.Colors] = []
if let nameColor = peer.nameColor.flatMap({ context.peerNameColors.get($0, dark: presentationData.theme.overallDarkAppearance) }) {
colors.append(nameColor)
@ -1123,9 +1131,11 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
}
}
if !isMyProfile {
items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: {
interaction.openSettings(.addAccount)
}))
}
var hasPremiumAccounts = false
if data.peer?.isPremium == true && !context.account.testingEnvironment {
@ -1142,11 +1152,13 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
}
}
if !isMyProfile {
items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: hasPremiumAccounts ? presentationData.strings.Settings_AddAnotherAccount_PremiumHelp : presentationData.strings.Settings_AddAnotherAccount_Help))
items[.logout]!.append(PeerInfoScreenActionItem(id: ItemLogout, text: presentationData.strings.Settings_Logout, color: .destructive, alignment: .center, action: {
interaction.openSettings(.logout)
}))
}
var result: [(AnyHashable, [PeerInfoScreenItem])] = []
for section in Section.allCases {
@ -2371,6 +2383,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
let isSettings: Bool
let isMyProfile: Bool
private let isMediaOnly: Bool
let initialExpandPanes: Bool
@ -2488,7 +2501,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, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, initialPaneKey: PeerInfoPaneKey?) {
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?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, initialPaneKey: PeerInfoPaneKey?) {
self.controller = controller
self.context = context
self.peerId = peerId
@ -2499,9 +2512,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.reactionSourceMessageId = reactionSourceMessageId
self.callMessages = callMessages
self.isSettings = isSettings
self.isMyProfile = isMyProfile
self.chatLocation = chatLocation
self.chatLocationContextHolder = chatLocationContextHolder
self.isMediaOnly = context.account.peerId == peerId && !isSettings
self.isMediaOnly = context.account.peerId == peerId && !isSettings && !isMyProfile
self.initialExpandPanes = initialPaneKey != nil
self.scrollNode = ASScrollNode()
@ -2512,7 +2526,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, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
self.headerNode = PeerInfoHeaderNode(context: context, controller: controller, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, isMyProfile: isMyProfile, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey)
super.init()
@ -3437,6 +3451,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
strongSelf.openMediaCalendar()
}
self.paneContainerNode.openAddStory = { [weak self] in
guard let self else {
return
}
self.headerNode.navigationButtonContainer.performAction?(.postStory, nil, nil)
}
self.paneContainerNode.paneDidScroll = { [weak self] in
guard let strongSelf = self else {
return
@ -4180,7 +4201,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
strongSelf.controller?.present(emojiStatusSelectionController, in: .window(.root))
}
} else {
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder)
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder)
var previousTimestamp: Double?
self.headerNode.displayPremiumIntro = { [weak self] sourceView, peerStatus, emojiStatusFileAndPack, white in
@ -9186,6 +9207,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
case .proxy:
self.controller?.push(proxySettingsController(context: self.context))
case .profile:
self.controller?.push(PeerInfoScreenImpl(
context: self.context,
updatedPresentationData: self.controller?.updatedPresentationData,
peerId: self.context.account.peerId,
avatarInitiallyExpanded: false,
isOpenedFromChat: false,
nearbyPeerDistance: nil,
reactionSourceMessageId: nil,
callMessages: [],
isMyProfile: true
))
case .stories:
push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved))
case .savedMessages:
@ -10503,7 +10536,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
var validEditingSections: [AnyHashable] = []
let editItems = self.isSettings ? settingsEditingItems(data: self.data, state: self.state, context: self.context, presentationData: self.presentationData, interaction: self.interaction) : editingItems(data: self.data, state: self.state, chatLocation: self.chatLocation, context: self.context, presentationData: self.presentationData, interaction: self.interaction)
let editItems = (self.isSettings || self.isMyProfile) ? settingsEditingItems(data: self.data, state: self.state, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isMyProfile: self.isMyProfile) : editingItems(data: self.data, state: self.state, chatLocation: self.chatLocation, context: self.context, presentationData: self.presentationData, interaction: self.interaction)
for (sectionId, sectionItems) in editItems {
var insets = UIEdgeInsets()
@ -10829,6 +10862,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}
if let data = self.data, !data.isPremiumRequiredForStoryPosting || data.accountIsPremium, let channel = data.peer as? TelegramChannel, channel.hasPermission(.postStories) {
rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0)
} else if self.isMyProfile {
rightNavigationButtons.insert(PeerInfoHeaderNavigationButtonSpec(key: .postStory, isForExpandedView: false), at: 0)
}
if self.state.selectedMessageIds == nil {
@ -11182,6 +11217,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
private let reactionSourceMessageId: MessageId?
private let callMessages: [Message]
private let isSettings: Bool
private let isMyProfile: Bool
private let hintGroupInCommon: PeerId?
private weak var requestsContext: PeerInvitationImportersContext?
private let switchToRecommendedChannels: Bool
@ -11241,7 +11277,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil, forumTopicThread: ChatReplyThreadMessage? = nil, switchToRecommendedChannels: Bool = false) {
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, isMyProfile: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil, forumTopicThread: ChatReplyThreadMessage? = nil, switchToRecommendedChannels: Bool = false) {
self.context = context
self.updatedPresentationData = updatedPresentationData
self.peerId = peerId
@ -11251,6 +11287,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
self.reactionSourceMessageId = reactionSourceMessageId
self.callMessages = callMessages
self.isSettings = isSettings
self.isMyProfile = isMyProfile
self.hintGroupInCommon = hintGroupInCommon
self.requestsContext = requestsContext
self.switchToRecommendedChannels = switchToRecommendedChannels
@ -11587,7 +11624,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
override public func loadDisplayNode() {
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, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, initialPaneKey: self.switchToRecommendedChannels ? .recommended : nil)
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, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, initialPaneKey: self.switchToRecommendedChannels ? .recommended : nil)
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())

View File

@ -461,6 +461,7 @@ final class PeerInfoStoryGridScreenComponent: Component {
captureProtected: false,
isSaved: true,
isArchive: component.scope == .archive,
isProfileEmbedded: false,
navigationController: { [weak self] in
guard let self else {
return nil

View File

@ -189,6 +189,62 @@ private let viewCountImage: UIImage = {
return image!
}()
private let privacyTypeImageScaleFactor: CGFloat = {
return 0.9
}()
private let privacyTypeEveryoneImage: UIImage = {
let baseImage = UIImage(bundleImageName: "Stories/PrivacyEveryone")!
let imageSize = CGSize(width: floor(baseImage.size.width * privacyTypeImageScaleFactor), height: floor(baseImage.size.width * privacyTypeImageScaleFactor))
let image = generateImage(imageSize, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
baseImage.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
})
return image!
}()
private let privacyTypeContactsImage: UIImage = {
let baseImage = UIImage(bundleImageName: "Stories/PrivacyContacts")!
let imageSize = CGSize(width: floor(baseImage.size.width * privacyTypeImageScaleFactor), height: floor(baseImage.size.width * privacyTypeImageScaleFactor))
let image = generateImage(imageSize, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
baseImage.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
})
return image!
}()
private let privacyTypeCloseFriendsImage: UIImage = {
let baseImage = UIImage(bundleImageName: "Stories/PrivacyCloseFriends")!
let imageSize = CGSize(width: floor(baseImage.size.width * privacyTypeImageScaleFactor), height: floor(baseImage.size.width * privacyTypeImageScaleFactor))
let image = generateImage(imageSize, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
baseImage.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
})
return image!
}()
private let privacyTypeSelectedImage: UIImage = {
let baseImage = UIImage(bundleImageName: "Stories/PrivacySelectedContacts")!
let imageSize = CGSize(width: floor(baseImage.size.width * privacyTypeImageScaleFactor), height: floor(baseImage.size.width * privacyTypeImageScaleFactor))
let image = generateImage(imageSize, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
baseImage.draw(in: CGRect(origin: CGPoint(), size: size))
UIGraphicsPopContext()
})
return image!
}()
private final class DurationLayer: CALayer {
override init() {
super.init()
@ -264,6 +320,41 @@ private final class DurationLayer: CALayer {
self.contents = image?.cgImage
}
}
func update(privacyType: Stories.Item.Privacy.Base, isMin: Bool) {
if isMin {
self.contents = nil
} else {
let iconImage: UIImage
switch privacyType {
case .everyone:
iconImage = privacyTypeEveryoneImage
case .contacts:
iconImage = privacyTypeContactsImage
case .closeFriends:
iconImage = privacyTypeCloseFriendsImage
case .nobody:
iconImage = privacyTypeSelectedImage
}
let sideInset: CGFloat = 0.0
let verticalInset: CGFloat = 0.0
let image = generateImage(CGSize(width: iconImage.size.width + sideInset * 2.0, height: iconImage.size.height + verticalInset * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.normal)
context.setShadow(offset: CGSize(width: 0.0, height: 0.0), blur: 2.5, color: UIColor(rgb: 0x000000, alpha: 0.22).cgColor)
UIGraphicsPushContext(context)
iconImage.draw(in: CGRect(origin: CGPoint(x: (size.width - iconImage.size.width) * 0.5, y: (size.height - iconImage.size.height) * 0.5), size: iconImage.size))
UIGraphicsPopContext()
})
self.contents = image?.cgImage
}
}
}
private protocol ItemLayer: SparseItemGridLayer {
@ -276,7 +367,7 @@ private protocol ItemLayer: SparseItemGridLayer {
var hasContents: Bool { get set }
func setSpoilerContents(_ contents: Any?)
func updateDuration(viewCount: Int32?, duration: Int32?, isMin: Bool, minFactor: CGFloat)
func updateDuration(viewCount: Int32?, duration: Int32?, privacy: Stories.Item.Privacy.Base?, isMin: Bool, minFactor: CGFloat)
func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool)
func updateHasSpoiler(hasSpoiler: Bool)
@ -288,8 +379,10 @@ private final class GenericItemLayer: CALayer, ItemLayer {
var item: VisualMediaItem?
var viewCountLayer: DurationLayer?
var durationLayer: DurationLayer?
var privacyTypeLayer: DurationLayer?
var leftShadowLayer: SimpleLayer?
var rightShadowLayer: SimpleLayer?
var topRightShadowLayer: SimpleLayer?
var minFactor: CGFloat = 1.0
var selectionLayer: GridMessageSelectionLayer?
var dustLayer: MediaDustLayer?
@ -335,7 +428,7 @@ private final class GenericItemLayer: CALayer, ItemLayer {
self.item = item
}
func updateDuration(viewCount: Int32?, duration: Int32?, isMin: Bool, minFactor: CGFloat) {
func updateDuration(viewCount: Int32?, duration: Int32?, privacy: Stories.Item.Privacy.Base?, isMin: Bool, minFactor: CGFloat) {
self.minFactor = minFactor
if let viewCount {
@ -371,6 +464,23 @@ private final class GenericItemLayer: CALayer, ItemLayer {
durationLayer.removeFromSuperlayer()
}
if let privacy {
if let privacyTypeLayer = self.privacyTypeLayer {
privacyTypeLayer.update(privacyType: privacy, isMin: isMin)
} else {
let privacyTypeLayer = DurationLayer()
privacyTypeLayer.contentsGravity = .bottomRight
privacyTypeLayer.update(privacyType: privacy, isMin: isMin)
self.addSublayer(privacyTypeLayer)
privacyTypeLayer.frame = CGRect(origin: CGPoint(x: self.bounds.width - 2.0, y: 3.0), size: CGSize())
privacyTypeLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0)
self.privacyTypeLayer = privacyTypeLayer
}
} else if let privacyTypeLayer = self.privacyTypeLayer {
self.privacyTypeLayer = nil
privacyTypeLayer.removeFromSuperlayer()
}
let size = self.bounds.size
if self.viewCountLayer != nil {
@ -404,6 +514,22 @@ private final class GenericItemLayer: CALayer, ItemLayer {
rightShadowLayer.removeFromSuperlayer()
}
}
if self.privacyTypeLayer != nil {
if self.topRightShadowLayer == nil {
let topRightShadowLayer = SimpleLayer()
self.topRightShadowLayer = topRightShadowLayer
self.insertSublayer(topRightShadowLayer, at: 0)
topRightShadowLayer.contents = rightShadowImage.cgImage
let shadowSize = CGSize(width: min(size.width, rightShadowImage.size.width), height: min(size.height, rightShadowImage.size.height))
topRightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: size.height - shadowSize.height), size: shadowSize)
}
} else {
if let topRightShadowLayer = self.topRightShadowLayer {
self.topRightShadowLayer = nil
topRightShadowLayer.removeFromSuperlayer()
}
}
}
func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool) {
@ -468,6 +594,9 @@ private final class GenericItemLayer: CALayer, ItemLayer {
if let durationLayer = self.durationLayer {
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 4.0), size: CGSize())
}
if let privacyTypeLayer = self.privacyTypeLayer {
privacyTypeLayer.frame = CGRect(origin: CGPoint(x: size.width - 2.0, y: 3.0), size: CGSize())
}
if let leftShadowLayer = self.leftShadowLayer {
let shadowSize = CGSize(width: min(size.width, leftShadowImage.size.width), height: min(size.height, leftShadowImage.size.height))
@ -479,6 +608,11 @@ private final class GenericItemLayer: CALayer, ItemLayer {
rightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: size.height - shadowSize.height), size: shadowSize)
}
if let topRightShadowLayer = self.topRightShadowLayer {
let shadowSize = CGSize(width: min(size.width, rightShadowImage.size.width), height: min(size.height, rightShadowImage.size.height))
topRightShadowLayer.frame = CGRect(origin: CGPoint(x: size.width - shadowSize.width, y: 0.0), size: shadowSize)
}
if let binding = binding as? SparseItemGridBindingImpl, let item = item as? VisualMediaItem, let previousItem = self.item, previousItem.story.media.id != item.story.media.id {
binding.bindLayers(items: [item], layers: [displayItem], size: size, insets: insets, synchronous: .none)
}
@ -489,11 +623,14 @@ private final class ItemTransitionView: UIView {
private weak var itemLayer: CALayer?
private var copyDurationLayer: SimpleLayer?
private var copyViewCountLayer: SimpleLayer?
private var copyPrivacyTypeLayer: SimpleLayer?
private var copyLeftShadowLayer: SimpleLayer?
private var copyRightShadowLayer: SimpleLayer?
private var copyTopRightShadowLayer: SimpleLayer?
private var viewCountLayerBottomLeftPosition: CGPoint?
private var durationLayerBottomLeftPosition: CGPoint?
private var privacyTypeLayerTopRightPosition: CGPoint?
init(itemLayer: CALayer?) {
self.itemLayer = itemLayer
@ -505,13 +642,17 @@ private final class ItemTransitionView: UIView {
var viewCountLayer: CALayer?
var durationLayer: CALayer?
var privacyTypeLayer: CALayer?
var leftShadowLayer: CALayer?
var rightShadowLayer: CALayer?
var topRightShadowLayer: CALayer?
if let itemLayer = itemLayer as? GenericItemLayer {
viewCountLayer = itemLayer.viewCountLayer
durationLayer = itemLayer.durationLayer
privacyTypeLayer = itemLayer.privacyTypeLayer
leftShadowLayer = itemLayer.leftShadowLayer
rightShadowLayer = itemLayer.rightShadowLayer
topRightShadowLayer = itemLayer.topRightShadowLayer
self.layer.contents = itemLayer.contents
}
@ -537,6 +678,17 @@ private final class ItemTransitionView: UIView {
self.copyRightShadowLayer = copyLayer
}
if let topRightShadowLayer {
let copyLayer = SimpleLayer()
copyLayer.contents = topRightShadowLayer.contents
copyLayer.contentsRect = topRightShadowLayer.contentsRect
copyLayer.contentsGravity = topRightShadowLayer.contentsGravity
copyLayer.contentsScale = topRightShadowLayer.contentsScale
copyLayer.frame = topRightShadowLayer.frame
self.layer.addSublayer(copyLayer)
self.copyTopRightShadowLayer = copyLayer
}
if let viewCountLayer {
let copyViewCountLayer = SimpleLayer()
copyViewCountLayer.contents = viewCountLayer.contents
@ -550,6 +702,19 @@ private final class ItemTransitionView: UIView {
self.viewCountLayerBottomLeftPosition = CGPoint(x: viewCountLayer.frame.minX, y: itemLayer.bounds.height - viewCountLayer.frame.maxY)
}
if let privacyTypeLayer {
let copyPrivacyTypeLayer = SimpleLayer()
copyPrivacyTypeLayer.contents = privacyTypeLayer.contents
copyPrivacyTypeLayer.contentsRect = privacyTypeLayer.contentsRect
copyPrivacyTypeLayer.contentsGravity = privacyTypeLayer.contentsGravity
copyPrivacyTypeLayer.contentsScale = privacyTypeLayer.contentsScale
copyPrivacyTypeLayer.frame = privacyTypeLayer.frame
self.layer.addSublayer(copyPrivacyTypeLayer)
self.copyPrivacyTypeLayer = copyPrivacyTypeLayer
self.privacyTypeLayerTopRightPosition = CGPoint(x: itemLayer.bounds.width - privacyTypeLayer.frame.maxX, y: privacyTypeLayer.frame.minY)
}
if let durationLayer {
let copyDurationLayer = SimpleLayer()
copyDurationLayer.contents = durationLayer.contents
@ -576,8 +741,12 @@ private final class ItemTransitionView: UIView {
transition.setFrame(layer: copyDurationLayer, frame: CGRect(origin: CGPoint(x: size.width - durationLayerBottomLeftPosition.x - copyDurationLayer.bounds.width, y: size.height - durationLayerBottomLeftPosition.y - copyDurationLayer.bounds.height), size: copyDurationLayer.bounds.size))
}
if let copyViewCountLayer = self.copyViewCountLayer, let viewcountLayerBottomLeftPosition = self.viewCountLayerBottomLeftPosition {
transition.setFrame(layer: copyViewCountLayer, frame: CGRect(origin: CGPoint(x: viewcountLayerBottomLeftPosition.x, y: size.height - viewcountLayerBottomLeftPosition.y - copyViewCountLayer.bounds.height), size: copyViewCountLayer.bounds.size))
if let copyViewCountLayer = self.copyViewCountLayer, let viewCountLayerBottomLeftPosition = self.viewCountLayerBottomLeftPosition {
transition.setFrame(layer: copyViewCountLayer, frame: CGRect(origin: CGPoint(x: viewCountLayerBottomLeftPosition.x, y: size.height - viewCountLayerBottomLeftPosition.y - copyViewCountLayer.bounds.height), size: copyViewCountLayer.bounds.size))
}
if let privacyTypeLayer = self.copyPrivacyTypeLayer, let privacyTypeLayerTopRightPosition = self.privacyTypeLayerTopRightPosition {
transition.setFrame(layer: privacyTypeLayer, frame: CGRect(origin: CGPoint(x: size.width - privacyTypeLayerTopRightPosition.x, y: privacyTypeLayerTopRightPosition.y), size: privacyTypeLayer.bounds.size))
}
if let copyLeftShadowLayer = self.copyLeftShadowLayer {
@ -587,6 +756,10 @@ private final class ItemTransitionView: UIView {
if let copyRightShadowLayer = self.copyRightShadowLayer {
transition.setFrame(layer: copyRightShadowLayer, frame: CGRect(origin: CGPoint(x: size.width - copyRightShadowLayer.bounds.width, y: size.height - copyRightShadowLayer.bounds.height), size: copyRightShadowLayer.bounds.size))
}
if let copyTopRightShadowLayer = self.copyTopRightShadowLayer {
transition.setFrame(layer: copyTopRightShadowLayer, frame: CGRect(origin: CGPoint(x: size.width - copyTopRightShadowLayer.bounds.width, y: 0.0), size: copyTopRightShadowLayer.bounds.size))
}
}
}
@ -595,6 +768,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
let chatLocation: ChatLocation
let directMediaImageCache: DirectMediaImageCache
let captureProtected: Bool
let displayPrivacy: Bool
var strings: PresentationStrings
var chatPresentationData: ChatPresentationData
var checkNodeTheme: CheckNodeTheme
@ -613,11 +787,12 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
private var shimmerImages: [CGFloat: UIImage] = [:]
init(context: AccountContext, chatLocation: ChatLocation, directMediaImageCache: DirectMediaImageCache, captureProtected: Bool) {
init(context: AccountContext, chatLocation: ChatLocation, directMediaImageCache: DirectMediaImageCache, captureProtected: Bool, displayPrivacy: Bool) {
self.context = context
self.chatLocation = chatLocation
self.directMediaImageCache = directMediaImageCache
self.captureProtected = false
self.displayPrivacy = displayPrivacy
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
self.strings = presentationData.strings
@ -794,6 +969,11 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
viewCount = Int32(value)
}
var privacyType: EngineStoryPrivacy.Base?
if self.displayPrivacy, let value = story.privacy {
privacyType = value.base
}
var duration: Int32?
var isMin: Bool = false
if let file = selectedMedia as? TelegramMediaFile, !file.isAnimated {
@ -802,7 +982,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
}
isMin = layer.bounds.width < 80.0
}
layer.updateDuration(viewCount: viewCount, duration: duration, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0))
layer.updateDuration(viewCount: viewCount, duration: duration, privacy: privacyType, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0))
}
var isSelected: Bool?
@ -895,6 +1075,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private let chatLocation: ChatLocation
private let isSaved: Bool
private let isArchive: Bool
private let isProfileEmbedded: Bool
public private(set) var contentType: ContentType
private var contentTypePromise: ValuePromise<ContentType>
@ -1002,7 +1183,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private var emptyStateView: ComponentView<Empty>?
public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, navigationController: @escaping () -> NavigationController?, listContext: PeerStoryListContext?) {
public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, isProfileEmbedded: Bool, navigationController: @escaping () -> NavigationController?, listContext: PeerStoryListContext?) {
self.context = context
self.peerId = peerId
self.chatLocation = chatLocation
@ -1011,6 +1192,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.navigationController = navigationController
self.isSaved = isSaved
self.isArchive = isArchive
self.isProfileEmbedded = isProfileEmbedded
self.isSelectionModeActive = isArchive
@ -1024,7 +1206,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
context: context,
chatLocation: .peer(id: peerId),
directMediaImageCache: self.directMediaImageCache,
captureProtected: captureProtected
captureProtected: captureProtected,
displayPrivacy: isProfileEmbedded && !self.isArchive
)
self.listSource = listContext ?? PeerStoryListContext(account: context.account, peerId: peerId, isArchived: self.isArchive)
@ -1421,7 +1604,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
strongSelf.itemGrid.cancelGestures()
}
self.statusPromise.set(.single(PeerInfoStatusData(text: "", isActivity: false, key: .stories)))
self.statusPromise.set(.single(PeerInfoStatusData(text: "", isActivity: false, key: self.isArchive ? .storyArchive : .stories)))
/*self.storedStateDisposable = (visualMediaStoredState(engine: context.engine, peerId: peerId, messageTag: self.stateTag)
|> deliverOnMainQueue).start(next: { [weak self] value in
@ -1671,11 +1854,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
} else {
if self.isSaved {
title = self.presentationData.strings.StoryList_SubtitleSaved(Int32(state.totalCount))
} else if self.isArchive {
title = self.presentationData.strings.StoryList_SubtitleArchived(Int32(state.totalCount))
} else {
title = self.presentationData.strings.StoryList_SubtitleCount(Int32(state.totalCount))
}
}
self.statusPromise.set(.single(PeerInfoStatusData(text: title, isActivity: false, key: .stories)))
self.statusPromise.set(.single(PeerInfoStatusData(text: title, isActivity: false, key: self.isArchive ? .storyArchive : .stories)))
let timezoneOffset = Int32(TimeZone.current.secondsFromGMT())
@ -1952,12 +2137,19 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: self.itemInteraction.selectedIds?.contains(item.story.id), animated: animated)
}
let isSelecting = self._itemInteraction?.selectedIds != nil
var isSelecting = false
if let selectedIds = self._itemInteraction?.selectedIds, !selectedIds.isEmpty {
isSelecting = true
}
self.itemGrid.pinchEnabled = !isSelecting
var enableDismissGesture = true
if let items = self.items, items.items.isEmpty {
} else if isSelecting {
if self.isProfileEmbedded {
enableDismissGesture = true
} else if let items = self.items, items.items.isEmpty {
}
if isSelecting {
enableDismissGesture = false
}
self.view.disablesInteractiveTransitionGestureRecognizer = !enableDismissGesture
@ -2030,6 +2222,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
component: AnyComponent(EmptyStateIndicatorComponent(
context: self.context,
theme: presentationData.theme,
fitToHeight: self.isProfileEmbedded,
animationName: "StoryListEmpty",
title: self.isArchive ? presentationData.strings.StoryList_ArchivedEmptyState_Title : presentationData.strings.StoryList_SavedEmptyPosts_Title,
text: self.isArchive ? presentationData.strings.StoryList_ArchivedEmptyState_Text : presentationData.strings.StoryList_SavedEmptyPosts_Text,
@ -2040,7 +2233,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
self.emptyAction?()
},
additionalActionTitle: self.isArchive ? nil : presentationData.strings.StoryList_SavedEmptyAction,
additionalActionTitle: (self.isArchive || self.isProfileEmbedded) ? nil : presentationData.strings.StoryList_SavedEmptyAction,
additionalAction: { [weak self] in
guard let self else {
return
@ -2051,6 +2244,14 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
environment: {},
containerSize: CGSize(width: size.width, height: size.height - topInset - bottomInset)
)
let emptyStateFrame: CGRect
if self.isProfileEmbedded {
emptyStateFrame = CGRect(origin: CGPoint(x: floor((size.width - emptyStateSize.width) * 0.5), y: max(topInset, floor((visibleHeight - topInset - bottomInset - emptyStateSize.height) * 0.5))), size: emptyStateSize)
} else {
emptyStateFrame = CGRect(origin: CGPoint(x: floor((size.width - emptyStateSize.width) * 0.5), y: topInset), size: emptyStateSize)
}
if let emptyStateComponentView = emptyStateView.view {
if emptyStateComponentView.superview == nil {
self.view.addSubview(emptyStateComponentView)
@ -2058,12 +2259,20 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
emptyStateComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
emptyStateTransition.setFrame(view: emptyStateComponentView, frame: CGRect(origin: CGPoint(x: floor((size.width - emptyStateSize.width) * 0.5), y: topInset), size: emptyStateSize))
emptyStateTransition.setFrame(view: emptyStateComponentView, frame: emptyStateFrame)
}
if self.didUpdateItemsOnce {
Transition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
let backgroundColor: UIColor
if self.isProfileEmbedded {
backgroundColor = presentationData.theme.list.plainBackgroundColor
} else {
self.view.backgroundColor = presentationData.theme.list.blocksBackgroundColor
backgroundColor = presentationData.theme.list.blocksBackgroundColor
}
if self.didUpdateItemsOnce {
Transition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: backgroundColor)
} else {
self.view.backgroundColor = backgroundColor
}
} else {
if let emptyStateView = self.emptyStateView {
@ -2076,7 +2285,11 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
})
}
if self.isProfileEmbedded {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.plainBackgroundColor)
} else {
subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor)
}
} else {
self.view.backgroundColor = .clear
}

View File

@ -1463,7 +1463,9 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component {
values: valueList.map { item in
return environment.strings.MessageTimer_Days(Int32(item))
},
markPositions: true,
selectedIndex: selectedInactivityIndex,
title: nil,
selectedIndexUpdated: { [weak self] index in
guard let self else {
return

View File

@ -9,6 +9,7 @@ import ComponentFlow
public final class SliderComponent: Component {
public let valueCount: Int
public let value: Int
public let markPositions: Bool
public let trackBackgroundColor: UIColor
public let trackForegroundColor: UIColor
public let valueUpdated: (Int) -> Void
@ -17,6 +18,7 @@ public final class SliderComponent: Component {
public init(
valueCount: Int,
value: Int,
markPositions: Bool,
trackBackgroundColor: UIColor,
trackForegroundColor: UIColor,
valueUpdated: @escaping (Int) -> Void,
@ -24,6 +26,7 @@ public final class SliderComponent: Component {
) {
self.valueCount = valueCount
self.value = value
self.markPositions = markPositions
self.trackBackgroundColor = trackBackgroundColor
self.trackForegroundColor = trackForegroundColor
self.valueUpdated = valueUpdated
@ -37,6 +40,9 @@ public final class SliderComponent: Component {
if lhs.value != rhs.value {
return false
}
if lhs.markPositions != rhs.markPositions {
return false
}
if lhs.trackBackgroundColor != rhs.trackBackgroundColor {
return false
}
@ -97,6 +103,7 @@ public final class SliderComponent: Component {
sliderView.maximumValue = CGFloat(component.valueCount - 1)
sliderView.positionsCount = component.valueCount
sliderView.useLinesForPositions = true
sliderView.markPositions = component.markPositions
sliderView.backgroundColor = nil
sliderView.isOpaque = false

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "profile.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,231 @@
%PDF-1.7
1 0 obj
<< /Type /XObject
/Length 2 0 R
/Group << /Type /Group
/S /Transparency
>>
/Subtype /Form
/Resources << >>
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
>>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
1.000000 0.176471 0.333333 scn
0.000000 18.799999 m
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
18.799999 30.000000 l
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
30.000000 11.200001 l
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
11.200000 0.000000 l
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
0.000000 18.799999 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm
1.000000 1.000000 1.000000 scn
17.165209 3.024336 m
18.919451 4.825943 20.000000 7.286784 20.000000 10.000000 c
20.000000 15.522848 15.522847 20.000000 10.000000 20.000000 c
4.477152 20.000000 0.000000 15.522848 0.000000 10.000000 c
0.000000 7.286821 1.080519 4.826013 2.834717 3.024412 c
2.877964 3.183798 2.941688 3.345104 3.027704 3.506973 c
3.983309 5.305289 5.950467 7.156250 9.999956 7.156250 c
14.049442 7.156250 16.016598 5.305290 16.972202 3.506973 c
17.058231 3.345078 17.121962 3.183746 17.165209 3.024336 c
h
11.770506 0.156250 m
8.229494 0.156250 l
8.804153 0.053581 9.395820 0.000000 10.000000 0.000000 c
10.604180 0.000000 11.195847 0.053581 11.770506 0.156250 c
h
13.499953 12.843750 m
13.499953 10.910753 11.932950 9.343750 9.999953 9.343750 c
8.066957 9.343750 6.499953 10.910753 6.499953 12.843750 c
6.499953 14.776747 8.066957 16.343750 9.999953 16.343750 c
11.932950 16.343750 13.499953 14.776747 13.499953 12.843750 c
h
f*
n
Q
q
25.000000 15.000000 m
25.000000 9.477154 20.522846 5.000000 15.000000 5.000000 c
9.477152 5.000000 5.000000 9.477154 5.000000 15.000000 c
5.000000 20.522848 9.477152 25.000000 15.000000 25.000000 c
20.522846 25.000000 25.000000 20.522848 25.000000 15.000000 c
h
W*
n
q
1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm
1.000000 1.000000 1.000000 scn
18.670000 10.000000 m
18.670000 5.211691 14.788309 1.330000 10.000000 1.330000 c
10.000000 -1.330000 l
16.257387 -1.330000 21.330000 3.742613 21.330000 10.000000 c
18.670000 10.000000 l
h
10.000000 1.330000 m
5.211691 1.330000 1.330000 5.211691 1.330000 10.000000 c
-1.330000 10.000000 l
-1.330000 3.742613 3.742614 -1.330000 10.000000 -1.330000 c
10.000000 1.330000 l
h
1.330000 10.000000 m
1.330000 14.788309 5.211691 18.670000 10.000000 18.670000 c
10.000000 21.330000 l
3.742614 21.330000 -1.330000 16.257385 -1.330000 10.000000 c
1.330000 10.000000 l
h
10.000000 18.670000 m
14.788309 18.670000 18.670000 14.788309 18.670000 10.000000 c
21.330000 10.000000 l
21.330000 16.257385 16.257387 21.330000 10.000000 21.330000 c
10.000000 18.670000 l
h
f
n
Q
Q
endstream
endobj
2 0 obj
3071
endobj
3 0 obj
<< /Type /XObject
/Length 4 0 R
/Group << /Type /Group
/S /Transparency
>>
/Subtype /Form
/Resources << >>
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
>>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
0.000000 0.000000 0.000000 scn
0.000000 18.799999 m
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
18.799999 30.000000 l
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
30.000000 11.200001 l
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
11.200000 0.000000 l
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
0.000000 18.799999 l
h
f
n
Q
endstream
endobj
4 0 obj
944
endobj
5 0 obj
<< /XObject << /X1 1 0 R >>
/ExtGState << /E1 << /SMask << /Type /Mask
/G 3 0 R
/S /Alpha
>>
/Type /ExtGState
>> >>
>>
endobj
6 0 obj
<< /Length 7 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
/E1 gs
/X1 Do
Q
endstream
endobj
7 0 obj
46
endobj
8 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 5 0 R
/Contents 6 0 R
/Parent 9 0 R
>>
endobj
9 0 obj
<< /Kids [ 8 0 R ]
/Count 1
/Type /Pages
>>
endobj
10 0 obj
<< /Pages 9 0 R
/Type /Catalog
>>
endobj
xref
0 11
0000000000 65535 f
0000000010 00000 n
0000003329 00000 n
0000003352 00000 n
0000004544 00000 n
0000004566 00000 n
0000004864 00000 n
0000004966 00000 n
0000004987 00000 n
0000005160 00000 n
0000005234 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 10 0 R
/Size 11
>>
startxref
5294
%%EOF