mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +00:00
Merge branch 'master' into experimental-2
This commit is contained in:
commit
43c2d875a6
@ -5679,6 +5679,8 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"Settings.EditAccount" = "Edit Account";
|
||||
"Settings.EditPhoto" = "Edit Photo";
|
||||
"Settings.EditVideo" = "Edit Video";
|
||||
"Settings.CancelUpload" = "Cancel Upload";
|
||||
|
||||
"Settings.FrequentlyAskedQuestions" = "Frequently Asked Questions";
|
||||
|
||||
@ -5690,3 +5692,5 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Conversation.Dice.u26BD" = "Send a football emoji to do a free kick.";
|
||||
|
||||
"SettingsSearch_Synonyms_ChatFolders" = "";
|
||||
|
||||
"EditProfile.NameAndPhotoOrVideoHelp" = "Enter your name and add an optional profile photo or video.";
|
||||
|
||||
@ -532,7 +532,7 @@ public protocol SharedAccountContext: class {
|
||||
func makeProxySettingsController(context: AccountContext) -> ViewController
|
||||
func makeLocalizationListController(context: AccountContext) -> ViewController
|
||||
func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController
|
||||
func makeChatRecentActionsController(context: AccountContext, peer: Peer) -> ViewController
|
||||
func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController
|
||||
func makePrivacyAndSecurityController(context: AccountContext) -> ViewController
|
||||
func navigateToChatController(_ params: NavigateToChatControllerParams)
|
||||
func openExternalUrl(context: AccountContext, urlContext: OpenURLContext, url: String, forceExternal: Bool, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void)
|
||||
|
||||
@ -911,7 +911,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var chatListText: (String, String)?
|
||||
var chatListSearchResult: CachedChatListSearchResult?
|
||||
|
||||
let contentImageSize = CGSize(width: 18.0, height: 18.0)
|
||||
let contentImageSide: CGFloat = max(10.0, min(20.0, floor(item.presentationData.fontSize.baseDisplaySize * 18.0 / 17.0)))
|
||||
let contentImageSize = CGSize(width: contentImageSide, height: contentImageSide)
|
||||
let contentImageSpacing: CGFloat = 2.0
|
||||
let contentImageTrailingSpace: CGFloat = 5.0
|
||||
var contentImageSpecs: [(message: Message, media: Media, size: CGSize)] = []
|
||||
@ -1338,7 +1339,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
animateContent = true
|
||||
}
|
||||
|
||||
let measureString = NSAttributedString(string: "A", font: titleFont, textColor: .black)
|
||||
let (measureLayout, measureApply) = makeMeasureLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let titleSpacing: CGFloat = -1.0
|
||||
@ -1633,7 +1633,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
inputActivitiesApply?()
|
||||
|
||||
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: 2.0)
|
||||
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: floor((measureLayout.size.height - contentImageSize.height) / 2.0))
|
||||
var validMediaIds: [MediaId] = []
|
||||
for (message, media, mediaSize) in contentImageSpecs {
|
||||
guard let mediaId = media.id else {
|
||||
@ -1824,16 +1824,16 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
textFrame.origin.x = contentRect.origin.x
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
var mediaPreviewOffset = textFrame.origin.offsetBy(dx: 1.0, dy: 2.0)
|
||||
var mediaPreviewOffsetX = textFrame.origin.x + 1.0
|
||||
let contentImageSpacing: CGFloat = 2.0
|
||||
for (_, media, mediaSize) in self.currentMediaPreviewSpecs {
|
||||
guard let mediaId = media.id else {
|
||||
continue
|
||||
}
|
||||
if let previewNode = self.mediaPreviewNodes[mediaId] {
|
||||
transition.updateFrame(node: previewNode, frame: CGRect(origin: mediaPreviewOffset, size: mediaSize))
|
||||
transition.updateFrame(node: previewNode, frame: CGRect(origin: CGPoint(x: mediaPreviewOffsetX, y: previewNode.frame.minY), size: mediaSize))
|
||||
}
|
||||
mediaPreviewOffset.x += mediaSize.width + contentImageSpacing
|
||||
mediaPreviewOffsetX += mediaSize.width + contentImageSpacing
|
||||
}
|
||||
|
||||
let dateFrame = self.dateNode.frame
|
||||
|
||||
@ -3,6 +3,7 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
|
||||
public enum ContainedViewLayoutTransitionCurve {
|
||||
case linear
|
||||
case easeInOut
|
||||
case spring
|
||||
case custom(Float, Float, Float, Float)
|
||||
@ -15,6 +16,8 @@ public enum ContainedViewLayoutTransitionCurve {
|
||||
public extension ContainedViewLayoutTransitionCurve {
|
||||
var timingFunction: String {
|
||||
switch self {
|
||||
case .linear:
|
||||
return CAMediaTimingFunctionName.linear.rawValue
|
||||
case .easeInOut:
|
||||
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
||||
case .spring:
|
||||
@ -26,6 +29,8 @@ public extension ContainedViewLayoutTransitionCurve {
|
||||
|
||||
var mediaTimingFunction: CAMediaTimingFunction? {
|
||||
switch self {
|
||||
case .linear:
|
||||
return nil
|
||||
case .easeInOut:
|
||||
return nil
|
||||
case .spring:
|
||||
@ -38,6 +43,8 @@ public extension ContainedViewLayoutTransitionCurve {
|
||||
#if os(iOS)
|
||||
var viewAnimationOptions: UIView.AnimationOptions {
|
||||
switch self {
|
||||
case .linear:
|
||||
return [.curveLinear]
|
||||
case .easeInOut:
|
||||
return [.curveEaseInOut]
|
||||
case .spring:
|
||||
|
||||
@ -3231,6 +3231,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
offset = -offset
|
||||
}
|
||||
switch curve {
|
||||
case .linear:
|
||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear))
|
||||
case .spring:
|
||||
transition.0.animateOffsetAdditive(node: headerNode, offset: offset)
|
||||
case let .custom(p1, p2, p3, p4):
|
||||
|
||||
@ -187,7 +187,7 @@ public func listViewAnimationDurationAndCurve(transition: ContainedViewLayoutTra
|
||||
case let .animated(animationDuration, animationCurve):
|
||||
duration = animationDuration
|
||||
switch animationCurve {
|
||||
case .easeInOut, .custom:
|
||||
case .linear, .easeInOut, .custom:
|
||||
break
|
||||
case .spring:
|
||||
curve = 7
|
||||
|
||||
@ -948,7 +948,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params) || !item.noInsets
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
@ -305,7 +305,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noInsets
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
@ -2743,9 +2743,9 @@
|
||||
[self setPlayButtonHidden:true animated:false];
|
||||
}
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
|
||||
{
|
||||
if (startValue > _dotPosition) {
|
||||
if (startValue > _dotPosition || videoScrubber.trimEndValue < _dotPosition) {
|
||||
_resetDotPosition = true;
|
||||
[self resetDotImage];
|
||||
}
|
||||
@ -2754,7 +2754,7 @@
|
||||
|
||||
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingEndValueDidChange:(NSTimeInterval)endValue
|
||||
{
|
||||
if (endValue < _dotPosition) {
|
||||
if (endValue < _dotPosition || videoScrubber.trimStartValue > _dotPosition) {
|
||||
_resetDotPosition = true;
|
||||
[self resetDotImage];
|
||||
}
|
||||
|
||||
@ -96,6 +96,22 @@ public final class AvatarGalleryControllerPresentationArguments {
|
||||
}
|
||||
}
|
||||
|
||||
public func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryEntry] {
|
||||
var updatedEntries: [AvatarGalleryEntry] = []
|
||||
let count: Int32 = Int32(entries.count)
|
||||
var index: Int32 = 0
|
||||
for entry in entries {
|
||||
let indexData = GalleryItemIndexData(position: index, totalCount: count)
|
||||
if case let .topImage(representations, _, immediateThumbnailData) = entry {
|
||||
updatedEntries.append(.topImage(representations, indexData, immediateThumbnailData))
|
||||
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData) = entry {
|
||||
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData))
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
return updatedEntries
|
||||
}
|
||||
|
||||
public func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry] {
|
||||
var initialEntries: [AvatarGalleryEntry] = []
|
||||
if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) {
|
||||
@ -540,22 +556,6 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
}
|
||||
}
|
||||
|
||||
private func normalizeEntries(_ entries: [AvatarGalleryEntry]) -> [AvatarGalleryEntry] {
|
||||
var updatedEntries: [AvatarGalleryEntry] = []
|
||||
let count: Int32 = Int32(entries.count)
|
||||
var index: Int32 = 0
|
||||
for entry in entries {
|
||||
let indexData = GalleryItemIndexData(position: index, totalCount: count)
|
||||
if case let .topImage(representations, _, immediateThumbnailData) = entry {
|
||||
updatedEntries.append(.topImage(representations, indexData, immediateThumbnailData))
|
||||
} else if case let .image(id, reference, representations, videoRepresentations, peer, date, _, messageId, immediateThumbnailData) = entry {
|
||||
updatedEntries.append(.image(id, reference, representations, videoRepresentations, peer, date, indexData, messageId, immediateThumbnailData))
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
return updatedEntries
|
||||
}
|
||||
|
||||
private func setMainEntry(_ rawEntry: AvatarGalleryEntry) {
|
||||
var entry = rawEntry
|
||||
if case .topImage = entry, !self.entries.isEmpty {
|
||||
@ -600,7 +600,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
canDelete = false
|
||||
}
|
||||
|
||||
entries = self.normalizeEntries(entries)
|
||||
entries = normalizeEntries(entries)
|
||||
|
||||
self.galleryNode.pager.replaceItems(entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: peer, presentationData: presentationData, entry: entry, sourceHasRoundCorners: self.sourceHasRoundCorners, delete: canDelete ? { [weak self] in
|
||||
self?.deleteEntry(entry)
|
||||
@ -707,7 +707,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
})
|
||||
}))
|
||||
|
||||
if let position = rawEntry.indexData?.position, position > 0 {
|
||||
if self.peer.id == self.context.account.peerId, let position = rawEntry.indexData?.position, position > 0 {
|
||||
let title: String
|
||||
if let _ = rawEntry.videoRepresentations.last {
|
||||
title = self.presentationData.strings.ProfilePhoto_SetMainVideo
|
||||
|
||||
@ -555,7 +555,7 @@ public func channelAdminsController(context: AccountContext, peerId initialPeerI
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if peer is TelegramGroup {
|
||||
} else {
|
||||
pushControllerImpl?(context.sharedContext.makeChatRecentActionsController(context: context, peer: peer))
|
||||
pushControllerImpl?(context.sharedContext.makeChatRecentActionsController(context: context, peer: peer, adminPeerId: nil))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -19,6 +19,7 @@ import ItemListPeerItem
|
||||
import ItemListPeerActionItem
|
||||
|
||||
private let maxUsersDisplayedLimit: Int32 = 10
|
||||
private let maxUsersDisplayedHighLimit: Int32 = 12
|
||||
|
||||
private final class GroupStatsControllerArguments {
|
||||
let context: AccountContext
|
||||
@ -94,15 +95,15 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
case topWeekdaysGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
|
||||
|
||||
case topPostersTitle(PresentationTheme, String, String)
|
||||
case topPoster(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopPoster, Bool)
|
||||
case topPoster(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopPoster, Bool, Bool)
|
||||
case topPostersExpand(PresentationTheme, String)
|
||||
|
||||
case topAdminsTitle(PresentationTheme, String, String)
|
||||
case topAdmin(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopAdmin, Bool)
|
||||
case topAdmin(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopAdmin, Bool, Bool)
|
||||
case topAdminsExpand(PresentationTheme, String)
|
||||
|
||||
case topInvitersTitle(PresentationTheme, String, String)
|
||||
case topInviter(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopInviter, Bool)
|
||||
case topInviter(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, GroupStatsTopInviter, Bool, Bool)
|
||||
case topInvitersExpand(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
@ -174,19 +175,19 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
return 17
|
||||
case .topPostersTitle:
|
||||
return 1000
|
||||
case let .topPoster(index, _, _, _, _, _, _):
|
||||
case let .topPoster(index, _, _, _, _, _, _, _):
|
||||
return 1001 + index
|
||||
case .topPostersExpand:
|
||||
return 1999
|
||||
case .topAdminsTitle:
|
||||
return 2000
|
||||
case let .topAdmin(index, _, _, _, _, _, _):
|
||||
case let .topAdmin(index, _, _, _, _, _, _, _):
|
||||
return 2001 + index
|
||||
case .topAdminsExpand:
|
||||
return 2999
|
||||
case .topInvitersTitle:
|
||||
return 3000
|
||||
case let .topInviter(index, _, _, _, _, _, _):
|
||||
case let .topInviter(index, _, _, _, _, _, _, _):
|
||||
return 3001 + index
|
||||
case .topInvitersExpand:
|
||||
return 3999
|
||||
@ -309,8 +310,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .topPoster(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopPoster, lhsRevealed):
|
||||
if case let .topPoster(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopPoster, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopPoster == rhsTopPoster, lhsRevealed == rhsRevealed {
|
||||
case let .topPoster(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopPoster, lhsRevealed, lhsCanPromote):
|
||||
if case let .topPoster(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopPoster, rhsRevealed, rhsCanPromote) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopPoster == rhsTopPoster, lhsRevealed == rhsRevealed, lhsCanPromote == rhsCanPromote {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -327,8 +328,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .topAdmin(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopAdmin, lhsRevealed):
|
||||
if case let .topAdmin(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopAdmin, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopAdmin == rhsTopAdmin, lhsRevealed == rhsRevealed {
|
||||
case let .topAdmin(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopAdmin, lhsRevealed, lhsCanPromote):
|
||||
if case let .topAdmin(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopAdmin, rhsRevealed, rhsCanPromote) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopAdmin == rhsTopAdmin, lhsRevealed == rhsRevealed, lhsCanPromote == rhsCanPromote {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -345,8 +346,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .topInviter(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopInviter, lhsRevealed):
|
||||
if case let .topInviter(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopInviter, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopInviter == rhsTopInviter, lhsRevealed == rhsRevealed {
|
||||
case let .topInviter(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsTopInviter, lhsRevealed, lhsCanPromote):
|
||||
if case let .topInviter(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsTopInviter, rhsRevealed, rhsCanPromote) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, arePeersEqual(lhsPeer, rhsPeer), lhsTopInviter == rhsTopInviter, lhsRevealed == rhsRevealed, lhsCanPromote == rhsCanPromote {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -393,7 +394,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
let .topHoursGraph(_, _, _, graph, type),
|
||||
let .topWeekdaysGraph(_, _, _, graph, type):
|
||||
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
|
||||
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster, revealed):
|
||||
case let .topPoster(_, _, strings, dateTimeFormat, peer, topPoster, revealed, canPromote):
|
||||
var textComponents: [String] = []
|
||||
if topPoster.messageCount > 0 {
|
||||
textComponents.append(strings.Stats_GroupTopPosterMessages(topPoster.messageCount))
|
||||
@ -402,13 +403,17 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
var options: [ItemListPeerItemRevealOption] = []
|
||||
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopPoster_History, action: {
|
||||
arguments.openPeerHistory(peer.id)
|
||||
}))
|
||||
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopPoster_Promote, action: {
|
||||
arguments.promotePeer(peer.id)
|
||||
}))
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
|
||||
if !peer.isDeleted {
|
||||
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopPoster_History, action: {
|
||||
arguments.openPeerHistory(peer.id)
|
||||
}))
|
||||
if canPromote && arguments.context.account.peerId != peer.id {
|
||||
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopPoster_Promote, action: {
|
||||
arguments.promotePeer(peer.id)
|
||||
}))
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
@ -417,7 +422,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
|
||||
arguments.expandTopPosters()
|
||||
})
|
||||
case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin, revealed):
|
||||
case let .topAdmin(_, _, strings, dateTimeFormat, peer, topAdmin, revealed, canPromote):
|
||||
var textComponents: [String] = []
|
||||
if topAdmin.deletedCount > 0 {
|
||||
textComponents.append(strings.Stats_GroupTopAdminDeletions(topAdmin.deletedCount))
|
||||
@ -429,13 +434,17 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
textComponents.append(strings.Stats_GroupTopAdminBans(topAdmin.bannedCount))
|
||||
}
|
||||
var options: [ItemListPeerItemRevealOption] = []
|
||||
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopAdmin_Actions, action: {
|
||||
arguments.openPeerAdminActions(peer.id)
|
||||
}))
|
||||
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopAdmin_Promote, action: {
|
||||
arguments.promotePeer(peer.id)
|
||||
}))
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
|
||||
if !peer.isDeleted {
|
||||
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopAdmin_Actions, action: {
|
||||
arguments.openPeerAdminActions(peer.id)
|
||||
}))
|
||||
if canPromote && arguments.context.account.peerId != peer.id {
|
||||
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopAdmin_Promote, action: {
|
||||
arguments.promotePeer(peer.id)
|
||||
}))
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
@ -444,17 +453,21 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
|
||||
arguments.expandTopAdmins()
|
||||
})
|
||||
case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter, revealed):
|
||||
case let .topInviter(_, _, strings, dateTimeFormat, peer, topInviter, revealed, canPromote):
|
||||
var textComponents: [String] = []
|
||||
textComponents.append(strings.Stats_GroupTopInviterInvites(topInviter.inviteCount))
|
||||
var options: [ItemListPeerItemRevealOption] = []
|
||||
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopPoster_History, action: {
|
||||
arguments.openPeerHistory(peer.id)
|
||||
}))
|
||||
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopPoster_Promote, action: {
|
||||
arguments.promotePeer(peer.id)
|
||||
}))
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
|
||||
if !peer.isDeleted {
|
||||
options.append(ItemListPeerItemRevealOption(type: .accent, title: strings.Stats_GroupTopPoster_History, action: {
|
||||
arguments.openPeerHistory(peer.id)
|
||||
}))
|
||||
if canPromote && arguments.context.account.peerId != peer.id {
|
||||
options.append(ItemListPeerItemRevealOption(type: .neutral, title: strings.Stats_GroupTopPoster_Promote, action: {
|
||||
arguments.promotePeer(peer.id)
|
||||
}))
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", ")), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
@ -467,7 +480,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
|
||||
private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStats?, peers: [PeerId: Peer]?, presentationData: PresentationData) -> [StatsEntry] {
|
||||
private func groupStatsControllerEntries(accountPeerId: PeerId, state: GroupStatsState, data: GroupStats?, channelPeer: Peer, peers: [PeerId: Peer]?, presentationData: PresentationData) -> [StatsEntry] {
|
||||
var entries: [StatsEntry] = []
|
||||
|
||||
if let data = data {
|
||||
@ -517,15 +530,16 @@ private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStat
|
||||
entries.append(.topWeekdaysTitle(presentationData.theme, presentationData.strings.Stats_GroupTopWeekdaysTitle))
|
||||
entries.append(.topWeekdaysGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.topWeekdaysGraph, .pie))
|
||||
}
|
||||
|
||||
|
||||
if let peers = peers {
|
||||
let canPromote = canEditAdminRights(accountPeerId: accountPeerId, channelPeer: channelPeer, initialParticipant: nil)
|
||||
if !data.topPosters.isEmpty {
|
||||
entries.append(.topPostersTitle(presentationData.theme, presentationData.strings.Stats_GroupTopPostersTitle, dates))
|
||||
var index: Int32 = 0
|
||||
|
||||
var topPosters = data.topPosters
|
||||
var effectiveExpanded = state.topPostersExpanded
|
||||
if topPosters.count > maxUsersDisplayedLimit && !effectiveExpanded {
|
||||
if topPosters.count > maxUsersDisplayedHighLimit && !effectiveExpanded {
|
||||
topPosters = Array(topPosters.prefix(Int(maxUsersDisplayedLimit)))
|
||||
} else {
|
||||
effectiveExpanded = true
|
||||
@ -533,7 +547,7 @@ private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStat
|
||||
|
||||
for topPoster in topPosters {
|
||||
if let peer = peers[topPoster.peerId], topPoster.messageCount > 0 {
|
||||
entries.append(.topPoster(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topPoster, topPoster.peerId == state.posterPeerIdWithRevealedOptions))
|
||||
entries.append(.topPoster(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topPoster, topPoster.peerId == state.posterPeerIdWithRevealedOptions, canPromote))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -548,7 +562,7 @@ private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStat
|
||||
|
||||
var topAdmins = data.topAdmins
|
||||
var effectiveExpanded = state.topAdminsExpanded
|
||||
if topAdmins.count > maxUsersDisplayedLimit && !effectiveExpanded {
|
||||
if topAdmins.count > maxUsersDisplayedHighLimit && !effectiveExpanded {
|
||||
topAdmins = Array(topAdmins.prefix(Int(maxUsersDisplayedLimit)))
|
||||
} else {
|
||||
effectiveExpanded = true
|
||||
@ -556,7 +570,7 @@ private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStat
|
||||
|
||||
for topAdmin in data.topAdmins {
|
||||
if let peer = peers[topAdmin.peerId], (topAdmin.deletedCount + topAdmin.kickedCount + topAdmin.bannedCount) > 0 {
|
||||
entries.append(.topAdmin(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topAdmin, topAdmin.peerId == state.adminPeerIdWithRevealedOptions))
|
||||
entries.append(.topAdmin(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topAdmin, topAdmin.peerId == state.adminPeerIdWithRevealedOptions, canPromote))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -571,7 +585,7 @@ private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStat
|
||||
|
||||
var topInviters = data.topInviters
|
||||
var effectiveExpanded = state.topInvitersExpanded
|
||||
if topInviters.count > maxUsersDisplayedLimit && !effectiveExpanded {
|
||||
if topInviters.count > maxUsersDisplayedHighLimit && !effectiveExpanded {
|
||||
topInviters = Array(topInviters.prefix(Int(maxUsersDisplayedLimit)))
|
||||
} else {
|
||||
effectiveExpanded = true
|
||||
@ -579,7 +593,7 @@ private func groupStatsControllerEntries(state: GroupStatsState, data: GroupStat
|
||||
|
||||
for topInviter in data.topInviters {
|
||||
if let peer = peers[topInviter.peerId], topInviter.inviteCount > 0 {
|
||||
entries.append(.topInviter(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topInviter, topInviter.peerId == state.inviterPeerIdWithRevealedOptions))
|
||||
entries.append(.topInviter(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, topInviter, topInviter.peerId == state.inviterPeerIdWithRevealedOptions, canPromote))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -667,6 +681,35 @@ private struct GroupStatsState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func canEditAdminRights(accountPeerId: PeerId, channelPeer: Peer, initialParticipant: ChannelParticipant?) -> Bool {
|
||||
if let channel = channelPeer as? TelegramChannel {
|
||||
if channel.flags.contains(.isCreator) {
|
||||
return true
|
||||
} else if let initialParticipant = initialParticipant {
|
||||
switch initialParticipant {
|
||||
case .creator:
|
||||
return false
|
||||
case let .member(_, _, adminInfo, _, _):
|
||||
if let adminInfo = adminInfo {
|
||||
return adminInfo.canBeEditedByAccountPeer || adminInfo.promotedBy == accountPeerId
|
||||
} else {
|
||||
return channel.hasPermission(.addAdmins)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return channel.hasPermission(.addAdmins)
|
||||
}
|
||||
} else if let group = channelPeer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func groupStatsController(context: AccountContext, peerId: PeerId, cachedPeerData: CachedPeerData) -> ViewController {
|
||||
let statePromise = ValuePromise(GroupStatsState())
|
||||
let stateValue = Atomic(value: GroupStatsState())
|
||||
@ -792,9 +835,10 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
|
||||
|
||||
let previousData = Atomic<GroupStats?>(value: nil)
|
||||
|
||||
let signal = combineLatest(statePromise.get(), context.sharedContext.presentationData, dataPromise.get(), peersPromise.get(), longLoadingSignal)
|
||||
|
||||
let signal = combineLatest(statePromise.get(), context.sharedContext.presentationData, dataPromise.get(), context.account.postbox.loadedPeerWithId(peerId), peersPromise.get(), longLoadingSignal)
|
||||
|> deliverOnMainQueue
|
||||
|> map { state, presentationData, data, peers, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|> map { state, presentationData, data, channelPeer, peers, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let previous = previousData.swap(data)
|
||||
var emptyStateItem: ItemListControllerEmptyStateItem?
|
||||
if data == nil {
|
||||
@ -804,9 +848,9 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
|
||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelInfo_Stats), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: groupStatsControllerEntries(state: state, data: data, peers: peers, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: groupStatsControllerEntries(accountPeerId: context.account.peerId, state: state, data: data, channelPeer: channelPeer, peers: peers, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
@ -851,7 +895,7 @@ public func groupStatsController(context: AccountContext, peerId: PeerId, cached
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
let controller = context.sharedContext.makeChatRecentActionsController(context: context, peer: peer)
|
||||
let controller = context.sharedContext.makeChatRecentActionsController(context: context, peer: peer, adminPeerId: participantPeerId)
|
||||
navigationController.pushViewController(controller)
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -170,9 +170,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.item = item
|
||||
|
||||
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: imageSize))
|
||||
let shape = CAShapeLayer()
|
||||
shape.path = maskPath.cgPath
|
||||
|
||||
|
||||
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: labelLayout.size.height + 10 + 2), size: imageSize)
|
||||
if let image = image {
|
||||
let imageNode: TransformImageNode
|
||||
@ -180,6 +178,8 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
imageNode = current
|
||||
} else {
|
||||
imageNode = TransformImageNode()
|
||||
let shape = CAShapeLayer()
|
||||
shape.path = maskPath.cgPath
|
||||
imageNode.layer.mask = shape
|
||||
strongSelf.imageNode = imageNode
|
||||
strongSelf.insertSubnode(imageNode, at: 0)
|
||||
@ -221,7 +221,8 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
videoNode.updateLayout(size: imageSize, transition: .immediate)
|
||||
videoNode.frame = imageFrame
|
||||
|
||||
|
||||
let shape = CAShapeLayer()
|
||||
shape.path = maskPath.cgPath
|
||||
videoNode.layer.mask = shape
|
||||
|
||||
strongSelf.addSubnode(videoNode)
|
||||
|
||||
@ -18,6 +18,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
|
||||
private let context: AccountContext
|
||||
private let peer: Peer
|
||||
private let initialAdminPeerId: PeerId?
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
@ -26,9 +27,10 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
|
||||
private let titleView: ChatRecentActionsTitleView
|
||||
|
||||
init(context: AccountContext, peer: Peer) {
|
||||
init(context: AccountContext, peer: Peer, adminPeerId: PeerId?) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.initialAdminPeerId = adminPeerId
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -174,6 +176,11 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
return self?.navigationController as? NavigationController
|
||||
})
|
||||
|
||||
if let adminPeerId = self.initialAdminPeerId {
|
||||
self.controllerNode.updateFilter(events: .all, adminPeerIds: [adminPeerId])
|
||||
self.updateTitle()
|
||||
}
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
|
||||
@ -78,14 +78,17 @@ final class MultiScaleTextNode: ASDisplayNode {
|
||||
return result
|
||||
}
|
||||
|
||||
func update(stateFractions: [AnyHashable: CGFloat], transition: ContainedViewLayoutTransition) {
|
||||
func update(stateFractions: [AnyHashable: CGFloat], alpha: CGFloat = 1.0, transition: ContainedViewLayoutTransition) {
|
||||
var fractionSum: CGFloat = 0.0
|
||||
for (_, fraction) in stateFractions {
|
||||
fractionSum += fraction
|
||||
}
|
||||
for (key, fraction) in stateFractions {
|
||||
if let node = self.stateNodes[key], let nodeLayout = node.currentLayout {
|
||||
transition.updateAlpha(node: node, alpha: fraction / fractionSum)
|
||||
if let node = self.stateNodes[key], let _ = node.currentLayout {
|
||||
if !transition.isAnimated {
|
||||
node.layer.removeAllAnimations()
|
||||
}
|
||||
transition.updateAlpha(node: node, alpha: fraction / fractionSum * alpha)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,6 +81,7 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
let leftInset = (item.icon == nil ? sideInset : sideInset + 29.0 + 16.0)
|
||||
let rightInset = sideInset
|
||||
let separatorInset = item.icon == nil ? sideInset : leftInset - 1.0
|
||||
let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize)
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
@ -93,7 +94,7 @@ private final class PeerInfoScreenActionItemNode: PeerInfoScreenItemNode {
|
||||
}
|
||||
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: textColorValue)
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
|
||||
|
||||
@ -114,9 +114,10 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
let leftInset = (item.icon == nil ? sideInset : sideInset + 29.0 + 16.0)
|
||||
let rightInset = sideInset + 18.0
|
||||
let separatorInset = item.icon == nil ? sideInset : leftInset - 1.0
|
||||
let titleFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize)
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
|
||||
let textColorValue: UIColor = presentationData.theme.list.itemPrimaryTextColor
|
||||
let labelColorValue: UIColor
|
||||
let labelFont: UIFont
|
||||
@ -125,12 +126,12 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
labelFont = Font.regular(15.0)
|
||||
} else {
|
||||
labelColorValue = presentationData.theme.list.itemSecondaryTextColor
|
||||
labelFont = Font.regular(17.0)
|
||||
labelFont = titleFont
|
||||
}
|
||||
self.labelNode.attributedText = NSAttributedString(string: item.label.text, font: labelFont, textColor: labelColorValue)
|
||||
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: textColorValue)
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
let labelSize = self.labelNode.updateLayout(CGSize(width: width - textSize.width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
|
||||
@ -738,7 +738,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.galleryEntries = entries
|
||||
self.galleryEntries = normalizeEntries(entries)
|
||||
self.items = items
|
||||
self.itemsUpdated?(items)
|
||||
self.currentIndex = 0
|
||||
@ -767,7 +767,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.galleryEntries = entries
|
||||
self.galleryEntries = normalizeEntries(entries)
|
||||
self.items = items
|
||||
self.itemsUpdated?(items)
|
||||
self.currentIndex = max(0, previousIndex - 1)
|
||||
@ -1019,6 +1019,11 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
if case .immediate = transition, fraction == 1.0 {
|
||||
return
|
||||
}
|
||||
if fraction > 0.0 {
|
||||
self.videoNode?.pause()
|
||||
} else {
|
||||
self.videoNode?.play()
|
||||
}
|
||||
transition.updateAlpha(node: videoNode, alpha: 1.0 - fraction)
|
||||
}
|
||||
}
|
||||
@ -1199,8 +1204,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
private var videoContent: NativeVideoContent?
|
||||
private var videoStartTimestamp: Double?
|
||||
|
||||
var tapped: (() -> Void)?
|
||||
var cancel: (() -> Void)?
|
||||
var tapped: ((Bool) -> Void)?
|
||||
|
||||
var canAttachVideo = true
|
||||
|
||||
@ -1219,7 +1223,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.tapped?()
|
||||
self.tapped?(false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1911,7 +1915,8 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
private let requestUpdateLayout: () -> Void
|
||||
|
||||
let avatarNode: PeerInfoEditingAvatarNode
|
||||
let avatarTextNode: HighlightableButtonNode
|
||||
let avatarTextNode: ImmediateTextNode
|
||||
let avatarButtonNode: HighlightableButtonNode
|
||||
|
||||
var itemNodes: [PeerInfoHeaderTextFieldNodeKey: PeerInfoHeaderTextFieldNode] = [:]
|
||||
|
||||
@ -1921,17 +1926,19 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
|
||||
self.avatarNode = PeerInfoEditingAvatarNode(context: context)
|
||||
|
||||
self.avatarTextNode = HighlightableButtonNode()
|
||||
self.avatarTextNode = ImmediateTextNode()
|
||||
self.avatarButtonNode = HighlightableButtonNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.avatarButtonNode.addSubnode(self.avatarTextNode)
|
||||
|
||||
self.avatarTextNode.addTarget(self, action: #selector(textPressed), forControlEvents: .touchUpInside)
|
||||
self.avatarButtonNode.addTarget(self, action: #selector(textPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func textPressed() {
|
||||
self.avatarNode.tapped?()
|
||||
self.avatarNode.tapped?(true)
|
||||
}
|
||||
|
||||
func editingTextForKey(_ key: PeerInfoHeaderTextFieldNodeKey) -> String? {
|
||||
@ -1950,14 +1957,14 @@ final class PeerInfoHeaderEditingContentNode: ASDisplayNode {
|
||||
var contentHeight: CGFloat = statusBarHeight + 10.0 + avatarSize + 20.0
|
||||
|
||||
if canEditPeerInfo(context: self.context, peer: peer) {
|
||||
if self.avatarTextNode.supernode == nil {
|
||||
self.addSubnode(self.avatarTextNode)
|
||||
if self.avatarButtonNode.supernode == nil {
|
||||
self.addSubnode(self.avatarButtonNode)
|
||||
}
|
||||
self.avatarTextNode.attributedText = NSAttributedString(string: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
|
||||
self.avatarTextNode.setAttributedTitle(NSAttributedString(string: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor), for: [])
|
||||
|
||||
let avatarTextSize = self.avatarTextNode.measure(CGSize(width: width, height: 32.0))
|
||||
transition.updateFrame(node: self.avatarTextNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - avatarTextSize.width) / 2.0), y: contentHeight - 1.0), size: avatarTextSize))
|
||||
let avatarTextSize = self.avatarTextNode.updateLayout(CGSize(width: width, height: 32.0))
|
||||
transition.updateFrame(node: self.avatarTextNode, frame: CGRect(origin: CGPoint(), size: avatarTextSize))
|
||||
transition.updateFrame(node: self.avatarButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((width - avatarTextSize.width) / 2.0), y: contentHeight - 1.0), size: avatarTextSize))
|
||||
contentHeight += 32.0
|
||||
}
|
||||
|
||||
@ -2068,6 +2075,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
private let videoCallsEnabled: Bool
|
||||
|
||||
private(set) var isAvatarExpanded: Bool
|
||||
private(set) var twoLineInfo = false
|
||||
|
||||
let avatarListNode: PeerInfoAvatarListNode
|
||||
|
||||
@ -2093,7 +2101,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
var performButtonAction: ((PeerInfoHeaderButtonKey) -> Void)?
|
||||
var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)?
|
||||
var requestOpenAvatarForEditing: (() -> Void)?
|
||||
var requestOpenAvatarForEditing: ((Bool) -> Void)?
|
||||
var requestUpdateLayout: (() -> Void)?
|
||||
|
||||
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
||||
@ -2126,11 +2134,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.subtitleNodeRawContainer = ASDisplayNode()
|
||||
self.subtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
|
||||
self.subtitleNode.displaysAsynchronously = false
|
||||
self.subtitleNode.allowsGroupOpacity = true
|
||||
|
||||
self.usernameNodeContainer = ASDisplayNode()
|
||||
self.usernameNodeRawContainer = ASDisplayNode()
|
||||
self.usernameNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
|
||||
self.usernameNode.displaysAsynchronously = false
|
||||
self.usernameNode.allowsGroupOpacity = true
|
||||
|
||||
self.regularContentNode = PeerInfoHeaderRegularContentNode()
|
||||
var requestUpdateLayoutImpl: (() -> Void)?
|
||||
@ -2176,11 +2186,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.avatarListNode.avatarContainerNode.tapped = { [weak self] in
|
||||
self?.initiateAvatarExpansion()
|
||||
}
|
||||
self.editingContentNode.avatarNode.tapped = { [weak self] in
|
||||
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.requestOpenAvatarForEditing?()
|
||||
strongSelf.requestOpenAvatarForEditing?(confirm)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2365,22 +2375,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
TitleNodeStateRegular: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: titleConstrainedSize.width, height: titleConstrainedSize.height)),
|
||||
TitleNodeStateExpanded: MultiScaleTextState(attributedText: usernameString, constrainedSize: CGSize(width: width - titleNodeLayout[TitleNodeStateExpanded]!.size.width - 8.0, height: titleConstrainedSize.height))
|
||||
], mainState: TitleNodeStateRegular)
|
||||
|
||||
self.titleNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], transition: transition)
|
||||
|
||||
self.subtitleNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], transition: transition)
|
||||
|
||||
self.usernameNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], transition: transition)
|
||||
|
||||
|
||||
let avatarSize: CGFloat = isModalOverlay ? 200.0 : 100.0
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 10.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
let avatarCenter = CGPoint(x: (1.0 - transitionFraction) * avatarFrame.midX + transitionFraction * transitionSourceAvatarFrame.midX, y: (1.0 - transitionFraction) * avatarFrame.midY + transitionFraction * transitionSourceAvatarFrame.midY)
|
||||
@ -2402,6 +2397,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let subtitleFrame: CGRect
|
||||
let usernameFrame: CGRect
|
||||
let usernameSpacing: CGFloat = 4.0
|
||||
var twoLineInfo = false
|
||||
if self.isAvatarExpanded {
|
||||
let minTitleSize = CGSize(width: titleSize.width * 0.7, height: titleSize.height * 0.7)
|
||||
let minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - expandedAvatarControlsHeight + 9.0 + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize)
|
||||
@ -2412,14 +2408,17 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 10.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize)
|
||||
|
||||
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||
if usernameSize.width == 0.0 || totalSubtitleWidth > width - textSideInset * 2.0 {
|
||||
twoLineInfo = totalSubtitleWidth > width - textSideInset * 2.0
|
||||
if usernameSize.width == 0.0 || twoLineInfo {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||
|
||||
} else {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
||||
}
|
||||
}
|
||||
self.twoLineInfo = twoLineInfo
|
||||
|
||||
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
|
||||
|
||||
@ -2435,6 +2434,22 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
let apparentTitleLockOffset = (1.0 - titleCollapseFraction) * 0.0 + titleCollapseFraction * titleMaxLockOffset
|
||||
|
||||
self.titleNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], transition: transition)
|
||||
|
||||
let subtitleAlpha: CGFloat = self.isSettings ? 1.0 - titleCollapseFraction : 1.0
|
||||
self.subtitleNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], alpha: subtitleAlpha, transition: transition)
|
||||
|
||||
self.usernameNode.update(stateFractions: [
|
||||
TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0,
|
||||
TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0
|
||||
], alpha: subtitleAlpha, transition: transition)
|
||||
|
||||
let avatarScale: CGFloat
|
||||
let avatarOffset: CGFloat
|
||||
if self.navigationTransition != nil {
|
||||
@ -2544,7 +2559,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, isExpanded: self.isAvatarExpanded, transition: transition)
|
||||
|
||||
let panelWithAvatarHeight: CGFloat = (self.isSettings ? 40.0 : 112.0) + avatarSize
|
||||
var panelWithAvatarHeight: CGFloat = (self.isSettings ? 40.0 : 112.0) + avatarSize
|
||||
if twoLineInfo {
|
||||
panelWithAvatarHeight += 17.0
|
||||
}
|
||||
let buttonsCollapseStart = titleCollapseOffset
|
||||
let buttonsCollapseEnd = panelWithAvatarHeight - (navigationHeight - statusBarHeight) + 10.0
|
||||
|
||||
@ -2589,10 +2607,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale)
|
||||
transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale)
|
||||
transition.updateSublayerTransformScale(node: self.usernameNodeContainer, scale: subtitleScale)
|
||||
if self.isSettings {
|
||||
transition.updateAlpha(node: self.subtitleNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
transition.updateAlpha(node: self.usernameNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
} else {
|
||||
let titleScale: CGFloat
|
||||
let subtitleScale: CGFloat
|
||||
@ -2617,18 +2632,20 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
transition.updateFrameAdditive(node: self.usernameNodeContainer, frame: CGRect(origin: rawUsernameFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
} else {
|
||||
transition.updateFrameAdditiveToCenter(node: self.titleNodeContainer, frame: CGRect(origin: rawTitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset + apparentTitleLockOffset))
|
||||
transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: rawSubtitleFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: rawUsernameFrame.center, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
|
||||
var subtitleCenter = rawSubtitleFrame.center
|
||||
subtitleCenter.x = rawTitleFrame.center.x + (subtitleCenter.x - rawTitleFrame.center.x) * subtitleScale
|
||||
transition.updateFrameAdditiveToCenter(node: self.subtitleNodeContainer, frame: CGRect(origin: subtitleCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
|
||||
var usernameCenter = rawUsernameFrame.center
|
||||
usernameCenter.x = rawTitleFrame.center.x + (usernameCenter.x - rawTitleFrame.center.x) * subtitleScale
|
||||
transition.updateFrameAdditiveToCenter(node: self.usernameNodeContainer, frame: CGRect(origin: usernameCenter, size: CGSize()).offsetBy(dx: 0.0, dy: titleOffset))
|
||||
}
|
||||
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
transition.updateFrame(node: self.usernameNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale)
|
||||
transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale)
|
||||
transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale)
|
||||
if self.isSettings {
|
||||
transition.updateAlpha(node: self.subtitleNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
transition.updateAlpha(node: self.usernameNode, alpha: 1.0 - titleCollapseFraction, beginWithCurrentState: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2810,7 +2827,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
if self.isAvatarExpanded {
|
||||
resolvedRegularHeight = expandedAvatarListSize.height + expandedAvatarControlsHeight
|
||||
} else {
|
||||
resolvedRegularHeight = (self.isSettings ? 40.0 : 112.0) + avatarSize + navigationHeight
|
||||
resolvedRegularHeight = panelWithAvatarHeight + navigationHeight
|
||||
}
|
||||
|
||||
let backgroundFrame: CGRect
|
||||
|
||||
@ -819,7 +819,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
|
||||
let ItemAddAccountHelp = 6
|
||||
let ItemLogout = 7
|
||||
|
||||
items[.help]!.append(PeerInfoScreenCommentItem(id: ItemNameHelp, text: presentationData.strings.EditProfile_NameAndPhotoHelp))
|
||||
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
|
||||
@ -837,10 +837,12 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
|
||||
interaction.openSettings(.username)
|
||||
}))
|
||||
|
||||
items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: {
|
||||
interaction.openSettings(.addAccount)
|
||||
}))
|
||||
items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: presentationData.strings.Settings_AddAnotherAccount_Help))
|
||||
if let settings = data.globalSettings, settings.accountsAndPeers.count + 1 < maximumNumberOfAccounts {
|
||||
items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: {
|
||||
interaction.openSettings(.addAccount)
|
||||
}))
|
||||
items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: presentationData.strings.Settings_AddAnotherAccount_Help))
|
||||
}
|
||||
|
||||
items[.logout]!.append(PeerInfoScreenActionItem(id: ItemLogout, text: presentationData.strings.Settings_Logout, color: .destructive, alignment: .center, action: {
|
||||
interaction.openSettings(.logout)
|
||||
@ -2110,15 +2112,36 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}))
|
||||
}
|
||||
|
||||
self.headerNode.requestOpenAvatarForEditing = { [weak self] in
|
||||
self.headerNode.requestOpenAvatarForEditing = { [weak self] confirm in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.state.updatingAvatar != nil {
|
||||
strongSelf.updateAvatarDisposable.set(nil)
|
||||
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
||||
let proceed = {
|
||||
strongSelf.updateAvatarDisposable.set(nil)
|
||||
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
||||
}
|
||||
}
|
||||
if confirm {
|
||||
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Settings_CancelUpload, color: .destructive, action: { [weak self] in
|
||||
dismissAction()
|
||||
proceed()
|
||||
}))
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: items),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
strongSelf.controller?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
} else {
|
||||
proceed()
|
||||
}
|
||||
} else {
|
||||
strongSelf.openAvatarForEditing()
|
||||
@ -2140,7 +2163,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
switch key {
|
||||
case .edit:
|
||||
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.3, curve: .linear))
|
||||
strongSelf.state = strongSelf.state.withIsEditing(true)
|
||||
var updateOnCompletion = false
|
||||
if strongSelf.headerNode.isAvatarExpanded {
|
||||
@ -2166,7 +2189,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
})
|
||||
strongSelf.controller?.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, style: .plain, target: strongSelf, action: #selector(strongSelf.editingCancelPressed)), animated: true)
|
||||
case .done, .cancel:
|
||||
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear))
|
||||
strongSelf.view.endEditing(true)
|
||||
if case .done = key {
|
||||
guard let data = strongSelf.data else {
|
||||
@ -3880,13 +3903,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
|
||||
if let item = item, case let .image(image) = item {
|
||||
if index > 0 {
|
||||
let title: String
|
||||
let setMainTitle: String
|
||||
if image.2.isEmpty {
|
||||
title = self.presentationData.strings.ProfilePhoto_SetMainPhoto
|
||||
setMainTitle = self.presentationData.strings.ProfilePhoto_SetMainPhoto
|
||||
} else {
|
||||
title = self.presentationData.strings.ProfilePhoto_SetMainVideo
|
||||
setMainTitle = self.presentationData.strings.ProfilePhoto_SetMainVideo
|
||||
}
|
||||
items.append(ActionSheetButtonItem(title: title, color: .accent, action: { [weak self] in
|
||||
items.append(ActionSheetButtonItem(title: setMainTitle, color: .accent, action: { [weak self] in
|
||||
dismissAction()
|
||||
self?.setMainAvatar(item)
|
||||
}))
|
||||
@ -3897,7 +3920,14 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
self?.editAvatarItem(item)
|
||||
}))
|
||||
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.GroupInfo_SetGroupPhotoDelete, color: .destructive, action: { [weak self] in
|
||||
|
||||
let deleteTitle: String
|
||||
if image.2.isEmpty {
|
||||
deleteTitle = self.presentationData.strings.GroupInfo_SetGroupPhotoDelete
|
||||
} else {
|
||||
deleteTitle = self.presentationData.strings.Settings_RemoveVideo
|
||||
}
|
||||
items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak self] in
|
||||
dismissAction()
|
||||
self?.deleteAvatar(item)
|
||||
}))
|
||||
@ -4116,7 +4146,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
assetsController?.dismiss()
|
||||
self?.updateProfilePhoto(result)
|
||||
}))
|
||||
strongSelf.controller?.present(controller, in: .window(.root))
|
||||
controller.navigationPresentation = .modal
|
||||
strongSelf.controller?.push(controller)
|
||||
}
|
||||
mixin.didFinishWithImage = { [weak self] image in
|
||||
if let image = image {
|
||||
@ -5344,7 +5375,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
guard let (_, navigationHeight) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
let height: CGFloat = self.isSettings ? 140.0 : 212.0
|
||||
var height: CGFloat = self.isSettings ? 140.0 : 212.0
|
||||
if self.headerNode.twoLineInfo {
|
||||
height += 17.0
|
||||
}
|
||||
if !self.state.isEditing {
|
||||
if targetContentOffset.pointee.y < height {
|
||||
if targetContentOffset.pointee.y < height / 2.0 {
|
||||
|
||||
@ -1096,8 +1096,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return peerSharedMediaControllerImpl(context: context, peerId: peerId)
|
||||
}
|
||||
|
||||
public func makeChatRecentActionsController(context: AccountContext, peer: Peer) -> ViewController {
|
||||
return ChatRecentActionsController(context: context, peer: peer)
|
||||
public func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController {
|
||||
return ChatRecentActionsController(context: context, peer: peer, adminPeerId: adminPeerId)
|
||||
}
|
||||
|
||||
public func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user