mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-04 18:41:00 +00:00
Merge commit '187e2603747bd07e4429443ff4e85a8e99242ccd' into beta
This commit is contained in:
commit
16d63eadf6
@ -217,7 +217,7 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry
|
||||
public class AvatarGalleryController: ViewController, StandalonePresentableController {
|
||||
public enum SourceCorners {
|
||||
case none
|
||||
case round
|
||||
case round(Bool)
|
||||
case roundRect(CGFloat)
|
||||
}
|
||||
|
||||
@ -256,6 +256,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
public var avatarPhotoEditCompletion: ((UIImage) -> Void)?
|
||||
public var avatarVideoEditCompletion: ((UIImage, URL, TGVideoEditAdjustments?) -> Void)?
|
||||
|
||||
public var removedEntry: ((AvatarGalleryEntry) -> Void)?
|
||||
|
||||
private let _hiddenMedia = Promise<AvatarGalleryEntry?>(nil)
|
||||
public var hiddenMedia: Signal<AvatarGalleryEntry?, NoError> {
|
||||
return self._hiddenMedia.get()
|
||||
@ -265,7 +267,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
|
||||
private let editDisposable = MetaDisposable ()
|
||||
|
||||
public init(context: AccountContext, peer: Peer, sourceCorners: SourceCorners = .round, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, skipInitial: Bool = false, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
|
||||
public init(context: AccountContext, peer: Peer, sourceCorners: SourceCorners = .round(true), remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, skipInitial: Bool = false, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.sourceCorners = sourceCorners
|
||||
@ -318,23 +320,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
strongSelf.centralEntryIndex = 0
|
||||
}
|
||||
if strongSelf.isViewLoaded {
|
||||
let canDelete: Bool
|
||||
if strongSelf.peer.id == strongSelf.context.account.peerId {
|
||||
canDelete = true
|
||||
} else if let group = strongSelf.peer as? TelegramGroup {
|
||||
switch group.role {
|
||||
case .creator, .admin:
|
||||
canDelete = true
|
||||
case .member:
|
||||
canDelete = false
|
||||
}
|
||||
} else if let channel = strongSelf.peer as? TelegramChannel {
|
||||
canDelete = channel.hasPermission(.changeInfo)
|
||||
} else {
|
||||
canDelete = false
|
||||
}
|
||||
|
||||
strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ entry in PeerAvatarImageGalleryItem(context: context, peer: peer, presentationData: presentationData, entry: entry, sourceCorners: sourceCorners, delete: canDelete ? {
|
||||
strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ entry in PeerAvatarImageGalleryItem(context: context, peer: peer, presentationData: presentationData, entry: entry, sourceCorners: sourceCorners, delete: strongSelf.canDelete ? {
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
@ -495,24 +481,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
let canDelete: Bool
|
||||
if self.peer.id == self.context.account.peerId {
|
||||
canDelete = true
|
||||
} else if let group = self.peer as? TelegramGroup {
|
||||
switch group.role {
|
||||
case .creator, .admin:
|
||||
canDelete = true
|
||||
case .member:
|
||||
canDelete = false
|
||||
}
|
||||
} else if let channel = self.peer as? TelegramChannel {
|
||||
canDelete = channel.hasPermission(.changeInfo)
|
||||
} else {
|
||||
canDelete = false
|
||||
}
|
||||
|
||||
let presentationData = self.presentationData
|
||||
self.galleryNode.pager.replaceItems(self.entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: canDelete ? { [weak self] in
|
||||
self.galleryNode.pager.replaceItems(self.entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: self.canDelete ? { [weak self] in
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
@ -610,6 +580,25 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
}
|
||||
}
|
||||
|
||||
private var canDelete: Bool {
|
||||
let canDelete: Bool
|
||||
if self.peer.id == self.context.account.peerId {
|
||||
canDelete = true
|
||||
} else if let group = self.peer as? TelegramGroup {
|
||||
switch group.role {
|
||||
case .creator, .admin:
|
||||
canDelete = true
|
||||
case .member:
|
||||
canDelete = false
|
||||
}
|
||||
} else if let channel = self.peer as? TelegramChannel {
|
||||
canDelete = channel.hasPermission(.changeInfo)
|
||||
} else {
|
||||
canDelete = false
|
||||
}
|
||||
return canDelete
|
||||
}
|
||||
|
||||
private func setMainEntry(_ rawEntry: AvatarGalleryEntry) {
|
||||
var entry = rawEntry
|
||||
if case .topImage = entry, !self.entries.isEmpty {
|
||||
@ -638,25 +627,10 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
entries.insert(previousFirstEntry, at: index)
|
||||
}
|
||||
|
||||
let canDelete: Bool
|
||||
if self.peer.id == self.context.account.peerId {
|
||||
canDelete = true
|
||||
} else if let group = self.peer as? TelegramGroup {
|
||||
switch group.role {
|
||||
case .creator, .admin:
|
||||
canDelete = true
|
||||
case .member:
|
||||
canDelete = false
|
||||
}
|
||||
} else if let channel = self.peer as? TelegramChannel {
|
||||
canDelete = channel.hasPermission(.changeInfo)
|
||||
} else {
|
||||
canDelete = false
|
||||
}
|
||||
|
||||
|
||||
entries = normalizeEntries(entries)
|
||||
|
||||
self.galleryNode.pager.replaceItems(entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: canDelete ? { [weak self] in
|
||||
self.galleryNode.pager.replaceItems(entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: self.peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: self.canDelete ? { [weak self] in
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
@ -798,12 +772,19 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
}
|
||||
|
||||
private func deleteEntry(_ rawEntry: AvatarGalleryEntry) {
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let proceed = {
|
||||
var entry = rawEntry
|
||||
if case .topImage = entry, !self.entries.isEmpty {
|
||||
entry = self.entries[0]
|
||||
}
|
||||
|
||||
self.removedEntry?(rawEntry)
|
||||
|
||||
var focusOnItem: Int?
|
||||
var updatedEntries = self.entries
|
||||
var replaceItems = false
|
||||
|
||||
switch entry {
|
||||
case .topImage:
|
||||
if self.peer.id == self.context.account.peerId {
|
||||
@ -827,8 +808,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self.dismiss(forceAway: true)
|
||||
} else {
|
||||
if let index = self.entries.firstIndex(of: entry) {
|
||||
self.entries.remove(at: index)
|
||||
self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
|
||||
replaceItems = true
|
||||
updatedEntries.remove(at: index)
|
||||
focusOnItem = index - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -841,14 +823,26 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
||||
self.dismiss(forceAway: true)
|
||||
} else {
|
||||
if let index = self.entries.firstIndex(of: entry) {
|
||||
self.entries.remove(at: index)
|
||||
self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
|
||||
replaceItems = true
|
||||
updatedEntries.remove(at: index)
|
||||
focusOnItem = index - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if replaceItems {
|
||||
updatedEntries = normalizeEntries(updatedEntries)
|
||||
self.galleryNode.pager.replaceItems(updatedEntries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: self.peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: self.canDelete ? { [weak self] in
|
||||
self?.deleteEntry(entry)
|
||||
} : nil, setMain: { [weak self] in
|
||||
self?.setMainEntry(entry)
|
||||
}, edit: { [weak self] in
|
||||
self?.editEntry(entry)
|
||||
}) }), centralItemIndex: focusOnItem, synchronous: true)
|
||||
self.entries = updatedEntries
|
||||
}
|
||||
}
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
let items: [ActionSheetItem] = [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in
|
||||
|
@ -266,7 +266,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
let mediaManager = self.context.sharedContext.mediaManager
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: entry.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, category), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
|
||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay)
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.isHidden = true
|
||||
self.videoStartTimestamp = video.representation.startTimestamp
|
||||
@ -429,7 +429,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self.contentNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: self.contentNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
|
||||
|
||||
self.contentNode.clipsToBounds = true
|
||||
if case .round = self.sourceCorners {
|
||||
if case .round(true) = self.sourceCorners {
|
||||
self.contentNode.layer.animate(from: (self.contentNode.frame.width / 2.0) as NSNumber, to: 0.0 as NSNumber, keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.default.rawValue, duration: 0.18, removeOnCompletion: false, completion: { [weak self] value in
|
||||
if value {
|
||||
self?.contentNode.clipsToBounds = false
|
||||
@ -443,6 +443,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
self?.contentNode.clipsToBounds = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.contentNode.clipsToBounds = false
|
||||
}
|
||||
|
||||
self.statusNodeContainer.layer.animatePosition(from: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), to: self.statusNodeContainer.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
|
@ -180,6 +180,15 @@ enum PeerInfoAvatarListItem: Equatable {
|
||||
return videoRepresentations
|
||||
}
|
||||
}
|
||||
|
||||
init(entry: AvatarGalleryEntry) {
|
||||
switch entry {
|
||||
case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _):
|
||||
self = .topImage(representations, videoRepresentations, immediateThumbnailData)
|
||||
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
||||
self = .image(reference, representations, videoRepresentations, immediateThumbnailData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
@ -972,12 +981,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
||||
|
||||
var items: [PeerInfoAvatarListItem] = []
|
||||
for entry in entries {
|
||||
switch entry {
|
||||
case let .topImage(representations, videoRepresentations, _, _, immediateThumbnailData, _):
|
||||
items.append(.topImage(representations, videoRepresentations, immediateThumbnailData))
|
||||
case let .image(_, reference, representations, videoRepresentations, _, _, _, _, immediateThumbnailData, _):
|
||||
items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData))
|
||||
}
|
||||
items.append(PeerInfoAvatarListItem(entry: entry))
|
||||
}
|
||||
strongSelf.galleryEntries = entries
|
||||
strongSelf.items = items
|
||||
@ -1532,7 +1536,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
let mediaManager = self.context.sharedContext.mediaManager
|
||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay)
|
||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .gallery)
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
self.videoStartTimestamp = video.representation.startTimestamp
|
||||
self.videoContent = videoContent
|
||||
@ -2536,10 +2540,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.addSubnode(self.separatorNode)
|
||||
|
||||
self.avatarListNode.avatarContainerNode.tapped = { [weak self] in
|
||||
self?.initiateAvatarExpansion(gallery: false)
|
||||
self?.initiateAvatarExpansion(gallery: false, first: false)
|
||||
}
|
||||
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
||||
self?.initiateAvatarExpansion(gallery: true)
|
||||
self?.initiateAvatarExpansion(gallery: true, first: true)
|
||||
}
|
||||
self.editingContentNode.requestEditing = { [weak self] in
|
||||
self?.requestOpenAvatarForEditing?(true)
|
||||
@ -2575,10 +2579,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func initiateAvatarExpansion(gallery: Bool) {
|
||||
func initiateAvatarExpansion(gallery: Bool, first: Bool) {
|
||||
if self.isAvatarExpanded || gallery {
|
||||
if let currentEntry = self.avatarListNode.listContainerNode.currentEntry, let firstEntry = self.avatarListNode.listContainerNode.galleryEntries.first {
|
||||
let entry = gallery ? firstEntry : currentEntry
|
||||
let entry = first ? firstEntry : currentEntry
|
||||
self.requestAvatarExpansion?(true, self.avatarListNode.listContainerNode.galleryEntries, entry, self.avatarTransitionArguments(entry: currentEntry))
|
||||
}
|
||||
} else if let entry = self.avatarListNode.listContainerNode.galleryEntries.first {
|
||||
|
@ -2105,7 +2105,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
|
||||
let entriesPromise = Promise<[AvatarGalleryEntry]>(entries)
|
||||
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceCorners: !strongSelf.headerNode.isAvatarExpanded ? .round : .none, remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
|
||||
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceCorners: .round(!strongSelf.headerNode.isAvatarExpanded), remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
|
||||
})
|
||||
galleryController.openAvatarSetup = { [weak self] completion in
|
||||
self?.openAvatarForEditing(hasRemove: false, completion: completion)
|
||||
@ -2116,6 +2116,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
galleryController.avatarVideoEditCompletion = { [weak self] image, url, adjustments in
|
||||
self?.updateProfileVideo(image, url: url, adjustments: adjustments)
|
||||
}
|
||||
galleryController.removedEntry = { [weak self] entry in
|
||||
self?.headerNode.avatarListNode.listContainerNode.deleteItem(PeerInfoAvatarListItem(entry: entry))
|
||||
}
|
||||
strongSelf.hiddenAvatarRepresentationDisposable.set((galleryController.hiddenMedia |> deliverOnMainQueue).start(next: { entry in
|
||||
self?.headerNode.updateAvatarIsHidden(entry: entry)
|
||||
}))
|
||||
@ -2129,6 +2132,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
|
||||
Queue.mainQueue().after(0.4) {
|
||||
strongSelf.resetHeaderExpansion()
|
||||
}
|
||||
}
|
||||
|
||||
self.headerNode.requestOpenAvatarForEditing = { [weak self] confirm in
|
||||
@ -5401,7 +5408,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
self.canOpenAvatarByDragging = false
|
||||
let contentOffset = scrollView.contentOffset.y
|
||||
scrollView.panGestureRecognizer.isEnabled = false
|
||||
self.headerNode.initiateAvatarExpansion(gallery: true)
|
||||
self.headerNode.initiateAvatarExpansion(gallery: true, first: false)
|
||||
scrollView.panGestureRecognizer.isEnabled = true
|
||||
scrollView.contentOffset = CGPoint(x: 0.0, y: contentOffset)
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user