mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Profile preview
This commit is contained in:
parent
db60f2c82d
commit
baca7c8661
@ -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";
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
@property (nonatomic, assign) bool displayEdges;
|
||||
@property (nonatomic, assign) bool useLinesForPositions;
|
||||
@property (nonatomic, assign) bool markPositions;
|
||||
|
||||
@property (nonatomic, readonly) bool knobStartedDragging;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -33,6 +33,8 @@ swift_library(
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/Components/HierarchyTrackingLayer",
|
||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||
"//submodules/TelegramUI/Components/ListItemSliderSelectorComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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>] = []
|
||||
|
@ -8,6 +8,7 @@ import TelegramPresentationData
|
||||
public enum PeerInfoPaneKey: Int32 {
|
||||
case members
|
||||
case stories
|
||||
case storyArchive
|
||||
case media
|
||||
case savedMessagesChats
|
||||
case savedMessages
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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:
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/Profile.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/Profile.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "profile.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
231
submodules/TelegramUI/Images.xcassets/Settings/Menu/Profile.imageset/profile.pdf
vendored
Normal file
231
submodules/TelegramUI/Images.xcassets/Settings/Menu/Profile.imageset/profile.pdf
vendored
Normal 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
|
Loading…
x
Reference in New Issue
Block a user