mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
6a15dbb4bb
commit
fabf9220a6
@ -8032,7 +8032,7 @@ Sorry for the inconvenience.";
|
||||
"Login.Continue" = "Continue";
|
||||
|
||||
"Login.EnterCodeSMSTitle" = "Enter Code";
|
||||
"Login.EnterCodeSMSText" = "We've sent and SMS with an activation code to your phone **%@**.";
|
||||
"Login.EnterCodeSMSText" = "We've sent an SMS with an activation code to your phone **%@**.";
|
||||
"Login.SendCodeAsSMS" = "Send the code as an SMS";
|
||||
"Login.EnterCodeTelegramTitle" = "Enter Code";
|
||||
"Login.EnterCodeTelegramText" = "We've sent the code to the **Telegram app** for %@ on your other device.";
|
||||
|
@ -2096,6 +2096,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
let previousSelectedMessages = Atomic<Set<EngineMessage.Id>?>(value: nil)
|
||||
let previousExpandGlobalSearch = Atomic<Bool>(value: false)
|
||||
|
||||
let _ = (searchQuery
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak listInteraction, weak chatListInteraction] query in
|
||||
@ -2109,10 +2110,12 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self?.searchOptionsValue = options
|
||||
})
|
||||
|
||||
|
||||
self.searchDisposable.set((foundItems
|
||||
|> deliverOnMainQueue).start(next: { [weak self] foundItems in
|
||||
if let strongSelf = self {
|
||||
let previousSelectedMessageIds = previousSelectedMessages.swap(strongSelf.selectedMessages)
|
||||
let previousExpandGlobalSearch = previousExpandGlobalSearch.swap(strongSelf.searchStateValue.expandGlobalSearch)
|
||||
|
||||
var entriesAndFlags = foundItems?.0
|
||||
|
||||
@ -2147,7 +2150,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let previousEntries = previousSearchItems.swap(entriesAndFlags)
|
||||
let newEntries = entriesAndFlags ?? []
|
||||
|
||||
let animated = (previousSelectedMessageIds == nil) != (strongSelf.selectedMessages == nil)
|
||||
let selectionChanged = (previousSelectedMessageIds == nil) != (strongSelf.selectedMessages == nil)
|
||||
let expandGlobalSearchChanged = previousExpandGlobalSearch != strongSelf.searchStateValue.expandGlobalSearch
|
||||
|
||||
let animated = selectionChanged || expandGlobalSearchChanged
|
||||
let firstTime = previousEntries == nil
|
||||
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags != nil, isEmpty: !isSearching && (entriesAndFlags?.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, location: location, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in
|
||||
interaction.peerContextAction?(message, node, rect, gesture, location)
|
||||
|
@ -120,7 +120,7 @@ final class ChatListBadgeNode: ASDisplayNode {
|
||||
strongSelf.isHiddenInternal = false
|
||||
if !strongSelf.disableBounce {
|
||||
if bounce {
|
||||
strongSelf.layer.animateScale(from: 0.0001, to: 1.2, duration: 0.2, removeOnCompletion: false, completion: { [weak self] finished in
|
||||
strongSelf.layer.animateScale(from: 0.0001, to: 1.2, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.layer.animateScale(from: 1.15, to: 1.0, duration: 0.12, removeOnCompletion: false)
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ final class PenTool: DrawingElement {
|
||||
context.scaleBy(x: 1.0 / parent.drawScale.width, y: 1.0 / parent.drawScale.height)
|
||||
element.drawSegments(in: context, from: parent.start, to: parent.segmentsCount)
|
||||
|
||||
if !element.isEraser || !element.isBlur {
|
||||
if !element.isEraser && !element.isBlur {
|
||||
element.drawActiveSegments(in: context, strokeWidth: !parent.isActiveDrying ? element.renderLineWidth * parent.dryingFactor : nil)
|
||||
} else {
|
||||
element.drawActiveSegments(in: context, strokeWidth: nil)
|
||||
|
@ -40,6 +40,7 @@ swift_library(
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||
"//submodules/TranslateUI:TranslateUI",
|
||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||
"//submodules/Utils/RangeSet:RangeSet",
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities:TextNodeWithEntities",
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
|
@ -188,8 +188,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
case .info:
|
||||
self.authorNameNode.isHidden = false
|
||||
self.dateNode.isHidden = false
|
||||
self.backwardButton.isHidden = true
|
||||
self.forwardButton.isHidden = true
|
||||
self.hasSeekControls = false
|
||||
self.playbackControlButton.isHidden = true
|
||||
self.statusButtonNode.isHidden = true
|
||||
self.statusNode.isHidden = true
|
||||
@ -197,8 +196,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
self.currentIsPaused = true
|
||||
self.authorNameNode.isHidden = true
|
||||
self.dateNode.isHidden = true
|
||||
self.backwardButton.isHidden = !seekable
|
||||
self.forwardButton.isHidden = !seekable
|
||||
self.hasSeekControls = seekable
|
||||
if status == .Local {
|
||||
self.playbackControlButton.isHidden = false
|
||||
self.playPauseIconNode.enqueueState(.play, animated: true)
|
||||
@ -226,8 +224,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
self.currentIsPaused = paused
|
||||
self.authorNameNode.isHidden = true
|
||||
self.dateNode.isHidden = true
|
||||
self.backwardButton.isHidden = !seekable
|
||||
self.forwardButton.isHidden = !seekable
|
||||
self.hasSeekControls = seekable
|
||||
self.playbackControlButton.isHidden = false
|
||||
|
||||
let icon: PlayPauseIconNodeState
|
||||
@ -244,6 +241,17 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
}
|
||||
}
|
||||
|
||||
var hasSeekControls: Bool = false {
|
||||
didSet {
|
||||
let alpha = self.hasSeekControls ? 1.0 : 0.0
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateAlpha(node: self.backwardButton, alpha: alpha)
|
||||
transition.updateAlpha(node: self.forwardButton, alpha: alpha)
|
||||
self.backwardButton.isUserInteractionEnabled = self.hasSeekControls
|
||||
self.forwardButton.isUserInteractionEnabled = self.hasSeekControls
|
||||
}
|
||||
}
|
||||
|
||||
private var scrubbingHandleRelativePosition: CGFloat = 0.0
|
||||
private var scrubbingVisualTimestamp: Double?
|
||||
|
||||
@ -339,11 +347,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
self.dateNode.displaysAsynchronously = false
|
||||
|
||||
self.backwardButton = PlaybackButtonNode()
|
||||
self.backwardButton.isHidden = true
|
||||
self.backwardButton.alpha = 0.0
|
||||
self.backwardButton.isUserInteractionEnabled = false
|
||||
self.backwardButton.backgroundIconNode.image = backwardImage
|
||||
|
||||
self.forwardButton = PlaybackButtonNode()
|
||||
self.forwardButton.isHidden = true
|
||||
self.forwardButton.alpha = 0.0
|
||||
self.forwardButton.isUserInteractionEnabled = false
|
||||
self.forwardButton.forward = true
|
||||
self.forwardButton.backgroundIconNode.image = forwardImage
|
||||
|
||||
|
@ -8,6 +8,7 @@ import Display
|
||||
import UniversalMediaPlayer
|
||||
import TelegramPresentationData
|
||||
import RangeSet
|
||||
import ShimmerEffect
|
||||
|
||||
private let textFont = Font.with(size: 13.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
||||
|
||||
@ -22,6 +23,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
private let rightTimestampNode: MediaPlayerTimeTextNode
|
||||
private let infoNode: ASTextNode
|
||||
private let scrubberNode: MediaPlayerScrubbingNode
|
||||
private let shimmerEffectNode: ShimmerEffectForegroundNode
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
@ -31,6 +33,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
private var fetchStatusDisposable = MetaDisposable()
|
||||
private var scrubbingDisposable = MetaDisposable()
|
||||
private var chapterDisposable = MetaDisposable()
|
||||
private var loadingDisposable = MetaDisposable()
|
||||
|
||||
private var leftTimestampNodePushed = false
|
||||
private var rightTimestampNodePushed = false
|
||||
@ -66,6 +69,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
init(chapters: [MediaPlayerScrubbingChapter]) {
|
||||
self.chapters = chapters
|
||||
self.scrubberNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 5.0, lineCap: .round, scrubberHandle: .circle, backgroundColor: scrubberBackgroundColor, foregroundColor: scrubberForegroundColor, bufferingColor: scrubberBufferingColor, chapters: chapters))
|
||||
self.shimmerEffectNode = ShimmerEffectForegroundNode()
|
||||
|
||||
self.leftTimestampNode = MediaPlayerTimeTextNode(textColor: .white)
|
||||
self.rightTimestampNode = MediaPlayerTimeTextNode(textColor: .white)
|
||||
@ -123,19 +127,20 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
deinit {
|
||||
self.scrubbingDisposable.dispose()
|
||||
self.fetchStatusDisposable.dispose()
|
||||
self.chapterDisposable.dispose()
|
||||
self.loadingDisposable.dispose()
|
||||
}
|
||||
|
||||
var collapsed: Bool?
|
||||
var isLoading = false
|
||||
var isCollapsed: Bool?
|
||||
func setCollapsed(_ collapsed: Bool, animated: Bool) {
|
||||
guard self.collapsed != collapsed else {
|
||||
guard self.isCollapsed != collapsed else {
|
||||
return
|
||||
}
|
||||
|
||||
self.collapsed = collapsed
|
||||
self.isCollapsed = collapsed
|
||||
|
||||
let alpha: CGFloat = collapsed ? 0.0 : 1.0
|
||||
self.leftTimestampNode.alpha = alpha
|
||||
self.rightTimestampNode.alpha = alpha
|
||||
self.updateTimestampsVisibility(animated: animated)
|
||||
self.updateScrubberVisibility(animated: animated)
|
||||
|
||||
if let (size, _, _) = self.containerLayout {
|
||||
@ -143,12 +148,19 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
func updateTimestampsVisibility(animated: Bool) {
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
||||
let alpha: CGFloat = self.isCollapsed == true || self.isLoading ? 0.0 : 1.0
|
||||
transition.updateAlpha(node: self.leftTimestampNode, alpha: alpha)
|
||||
transition.updateAlpha(node: self.rightTimestampNode, alpha: alpha)
|
||||
}
|
||||
|
||||
private func updateScrubberVisibility(animated: Bool) {
|
||||
var collapsed = self.collapsed
|
||||
var collapsed = self.isCollapsed
|
||||
var alpha: CGFloat = 1.0
|
||||
if let playbackStatus = self.playbackStatus, playbackStatus.duration <= 30.0 {
|
||||
} else {
|
||||
alpha = self.collapsed == true ? 0.0 : 1.0
|
||||
alpha = self.isCollapsed == true ? 0.0 : 1.0
|
||||
collapsed = false
|
||||
}
|
||||
self.scrubberNode.setCollapsed(collapsed == true, animated: animated)
|
||||
@ -174,6 +186,30 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
self.rightTimestampNode.status = mappedStatus
|
||||
|
||||
if let mappedStatus = mappedStatus {
|
||||
self.loadingDisposable.set((mappedStatus
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let strongSelf = self {
|
||||
if status.duration < 1.0 {
|
||||
strongSelf.isLoading = true
|
||||
strongSelf.updateTimestampsVisibility(animated: true)
|
||||
|
||||
if strongSelf.shimmerEffectNode.supernode == nil {
|
||||
strongSelf.scrubberNode.containerNode.addSubnode(strongSelf.shimmerEffectNode)
|
||||
}
|
||||
} else {
|
||||
strongSelf.isLoading = false
|
||||
strongSelf.updateTimestampsVisibility(animated: true)
|
||||
if strongSelf.shimmerEffectNode.supernode != nil {
|
||||
strongSelf.shimmerEffectNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.shimmerEffectNode.removeFromSupernode()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
self.chapterDisposable.set((mappedStatus
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let strongSelf = self, status.duration > 1.0, strongSelf.chapters.count > 0 {
|
||||
@ -320,9 +356,14 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
let infoSize = self.infoNode.measure(infoConstrainedSize)
|
||||
self.infoNode.bounds = CGRect(origin: CGPoint(), size: infoSize)
|
||||
transition.updatePosition(node: self.infoNode, position: CGPoint(x: size.width / 2.0, y: infoOffset + infoSize.height / 2.0))
|
||||
self.infoNode.alpha = size.width < size.height && self.collapsed == false ? 1.0 : 0.0
|
||||
self.infoNode.alpha = size.width < size.height && self.isCollapsed == false ? 1.0 : 0.0
|
||||
|
||||
self.scrubberNode.frame = CGRect(origin: CGPoint(x: scrubberInset, y: 6.0), size: CGSize(width: size.width - leftInset - rightInset - scrubberInset * 2.0, height: scrubberHeight))
|
||||
let scrubberFrame = CGRect(origin: CGPoint(x: scrubberInset, y: 6.0), size: CGSize(width: size.width - leftInset - rightInset - scrubberInset * 2.0, height: scrubberHeight))
|
||||
self.scrubberNode.frame = scrubberFrame
|
||||
self.shimmerEffectNode.updateAbsoluteRect(CGRect(origin: .zero, size: scrubberFrame.size), within: scrubberFrame.size)
|
||||
self.shimmerEffectNode.update(backgroundColor: .clear, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.75), horizontal: true, effectSize: nil, globalTimeOffset: false, duration: nil)
|
||||
self.shimmerEffectNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 4.0), size: CGSize(width: scrubberFrame.size.width, height: 5.0))
|
||||
self.shimmerEffectNode.cornerRadius = 2.5
|
||||
}
|
||||
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
|
@ -20,6 +20,7 @@ import TranslateUI
|
||||
import ShareController
|
||||
import UndoUI
|
||||
import ContextUI
|
||||
import SaveToCameraRoll
|
||||
|
||||
enum ChatMediaGalleryThumbnail: Equatable {
|
||||
case image(ImageMediaReference)
|
||||
@ -485,7 +486,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.SharedMedia_ViewInChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor)}, action: { [weak self] _, f in
|
||||
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self, let peer = peer else {
|
||||
return
|
||||
}
|
||||
@ -501,82 +502,34 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
f(.default)
|
||||
})
|
||||
})))
|
||||
|
||||
if !message.isCopyProtected(), let media = self.contextAndMedia?.1 {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Gallery_SaveImage, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
let _ = (SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: media)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let controller = strongSelf.galleryController() else {
|
||||
return
|
||||
}
|
||||
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: strongSelf.presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
if self.canDelete() {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Common_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
// if #available(iOS 11.0, *) {
|
||||
// items.append(.action(ContextMenuActionItem(text: "AirPlay", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/AirPlay"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
// f(.default)
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.beginAirPlaySetup()
|
||||
// })))
|
||||
// }
|
||||
|
||||
// if let (message, _, _) = strongSelf.contentInfo() {
|
||||
// for media in message.media {
|
||||
// if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||
// let url = content.url
|
||||
//
|
||||
// let item = OpenInItem.url(url: url)
|
||||
// let openText = strongSelf.presentationData.strings.Conversation_FileOpenIn
|
||||
// items.append(.action(ContextMenuActionItem(text: openText, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
// f(.default)
|
||||
//
|
||||
// if let strongSelf = self, let controller = strongSelf.galleryController() {
|
||||
// var presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
// if !presentationData.theme.overallDarkAppearance {
|
||||
// presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
// }
|
||||
// let actionSheet = OpenInActionSheetController(context: strongSelf.context, forceTheme: presentationData.theme, item: item, openUrl: { [weak self] url in
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: strongSelf.baseNavigationController(), dismissInput: {})
|
||||
// }
|
||||
// })
|
||||
// controller.present(actionSheet, in: .window(.root))
|
||||
// }
|
||||
// })))
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if let (message, maybeFile, _) = strongSelf.contentInfo(), let file = maybeFile, !message.isCopyProtected() {
|
||||
// items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Gallery_SaveVideo, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in
|
||||
// f(.default)
|
||||
//
|
||||
// if let strongSelf = self {
|
||||
// switch strongSelf.fetchStatus {
|
||||
// case .Local:
|
||||
// let _ = (SaveToCameraRoll.saveToCameraRoll(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .message(message: MessageReference(message), media: file))
|
||||
// |> deliverOnMainQueue).start(completed: {
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// guard let controller = strongSelf.galleryController() else {
|
||||
// return
|
||||
// }
|
||||
// controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: strongSelf.presentationData.strings.Gallery_VideoSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
// })
|
||||
// default:
|
||||
// guard let controller = strongSelf.galleryController() else {
|
||||
// return
|
||||
// }
|
||||
// controller.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Gallery_WaitForVideoDownoad, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
// })]), in: .window(.root))
|
||||
// }
|
||||
// }
|
||||
// })))
|
||||
// }
|
||||
// if strongSelf.canDelete() {
|
||||
// items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
||||
// f(.default)
|
||||
//
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.footerContentNode.deleteButtonPressed()
|
||||
// }
|
||||
// })))
|
||||
// }
|
||||
if let strongSelf = self {
|
||||
strongSelf.footerContentNode.deleteButtonPressed()
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
return .single(items)
|
||||
}
|
||||
@ -676,6 +629,13 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
self.zoomableContent = (largestSize.cgSize, self.imageNode)
|
||||
self.setupStatus(resource: fileReference.media.resource)
|
||||
|
||||
var barButtonItems: [UIBarButtonItem] = []
|
||||
if self.message != nil {
|
||||
let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)!
|
||||
barButtonItems.append(moreMenuItem)
|
||||
}
|
||||
self._rightBarButtonItems.set(.single(barButtonItems))
|
||||
} else {
|
||||
self._ready.set(.single(Void()))
|
||||
}
|
||||
|
@ -350,6 +350,15 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public var containerNode: ASDisplayNode {
|
||||
switch self.contentNodes {
|
||||
case let .standard(node):
|
||||
return node.containerNode
|
||||
case let .custom(node):
|
||||
return node.backgroundNode
|
||||
}
|
||||
}
|
||||
|
||||
private var _statusValue: MediaPlayerStatus?
|
||||
private var statusValue: MediaPlayerStatus? {
|
||||
get {
|
||||
@ -947,7 +956,10 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
|
||||
if let handleNodeContainer = node.handleNodeContainer {
|
||||
handleNodeContainer.bounds = bounds.offsetBy(dx: -floorToScreenPixels(bounds.size.width * progress), dy: 0.0)
|
||||
handleNodeContainer.isHidden = false
|
||||
if handleNodeContainer.alpha.isZero {
|
||||
handleNodeContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
handleNodeContainer.alpha = 1.0
|
||||
}
|
||||
} else if let statusValue = self.statusValue {
|
||||
var actualTimestamp: Double
|
||||
@ -975,15 +987,18 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
|
||||
if let handleNodeContainer = node.handleNodeContainer {
|
||||
handleNodeContainer.bounds = bounds.offsetBy(dx: -floorToScreenPixels(bounds.size.width * progress), dy: 0.0)
|
||||
handleNodeContainer.isHidden = false
|
||||
if handleNodeContainer.alpha.isZero {
|
||||
handleNodeContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
handleNodeContainer.alpha = 1.0
|
||||
}
|
||||
} else {
|
||||
node.handleNodeContainer?.isHidden = true
|
||||
node.foregroundNode.frame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height))
|
||||
node.handleNodeContainer?.alpha = 0.0
|
||||
}
|
||||
} else {
|
||||
node.foregroundNode.frame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height))
|
||||
node.handleNodeContainer?.isHidden = true
|
||||
node.handleNodeContainer?.alpha = 0.0
|
||||
}
|
||||
case let .custom(node):
|
||||
if let handleNodeContainer = node.handleNodeContainer {
|
||||
|
@ -78,7 +78,6 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo
|
||||
if let strongSelf = self {
|
||||
if highlighted, !strongSelf.bounds.width.isZero {
|
||||
let scale = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width
|
||||
|
||||
strongSelf.layer.animateScale(from: 1.0, to: scale, duration: 0.15, removeOnCompletion: false)
|
||||
|
||||
strongSelf.backgroundContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||
|
@ -4,6 +4,7 @@ import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import WallpaperBackgroundNode
|
||||
import AnimatedCountLabelNode
|
||||
|
||||
private let badgeFont = Font.regular(13.0)
|
||||
|
||||
@ -20,7 +21,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||
private let imageNode: ASImageNode
|
||||
private let badgeBackgroundNode: ASImageNode
|
||||
private let badgeTextNode: ASTextNode
|
||||
private let badgeTextNode: ImmediateAnimatedCountLabelNode
|
||||
|
||||
var tapped: (() -> Void)? {
|
||||
didSet {
|
||||
@ -67,15 +68,15 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
||||
self.imageNode.isLayerBacked = true
|
||||
|
||||
self.badgeBackgroundNode = ASImageNode()
|
||||
self.badgeBackgroundNode.isLayerBacked = true
|
||||
self.badgeBackgroundNode.displayWithoutProcessing = true
|
||||
self.badgeBackgroundNode.displaysAsynchronously = false
|
||||
self.badgeBackgroundNode.image = PresentationResourcesChat.chatHistoryNavigationButtonBadgeImage(theme)
|
||||
self.badgeBackgroundNode.alpha = 0.0
|
||||
|
||||
self.badgeTextNode = ASTextNode()
|
||||
self.badgeTextNode.maximumNumberOfLines = 1
|
||||
self.badgeTextNode = ImmediateAnimatedCountLabelNode()
|
||||
self.badgeTextNode.isUserInteractionEnabled = false
|
||||
self.badgeTextNode.displaysAsynchronously = false
|
||||
self.badgeTextNode.reverseAnimationDirection = true
|
||||
|
||||
super.init()
|
||||
|
||||
@ -99,7 +100,7 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
self.buttonNode.addSubnode(self.badgeBackgroundNode)
|
||||
self.buttonNode.addSubnode(self.badgeTextNode)
|
||||
self.badgeBackgroundNode.addSubnode(self.badgeTextNode)
|
||||
|
||||
self.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
@ -119,10 +120,15 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
||||
}
|
||||
self.badgeBackgroundNode.image = PresentationResourcesChat.chatHistoryNavigationButtonBadgeImage(theme)
|
||||
|
||||
if let string = self.badgeTextNode.attributedText?.string {
|
||||
self.badgeTextNode.attributedText = NSAttributedString(string: string, font: badgeFont, textColor: theme.chat.historyNavigation.badgeTextColor)
|
||||
self.badgeTextNode.redrawIfPossible()
|
||||
var segments: [AnimatedCountLabelNode.Segment] = []
|
||||
if let value = Int(self.badge) {
|
||||
self.currentValue = value
|
||||
segments.append(.number(value, NSAttributedString(string: self.badge, font: badgeFont, textColor: self.theme.chat.historyNavigation.badgeTextColor)))
|
||||
} else {
|
||||
self.currentValue = 0
|
||||
segments.append(.text(100, NSAttributedString(string: self.badge, font: badgeFont, textColor: self.theme.chat.historyNavigation.badgeTextColor)))
|
||||
}
|
||||
self.badgeTextNode.segments = segments
|
||||
}
|
||||
|
||||
if backgroundNode.hasExtraBubbleBackground() {
|
||||
@ -160,20 +166,53 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode {
|
||||
}
|
||||
}
|
||||
|
||||
private var currentValue: Int = 0
|
||||
private func layoutBadge() {
|
||||
if !self.badge.isEmpty {
|
||||
self.badgeTextNode.attributedText = NSAttributedString(string: self.badge, font: badgeFont, textColor: self.theme.chat.historyNavigation.badgeTextColor)
|
||||
self.badgeBackgroundNode.isHidden = false
|
||||
self.badgeTextNode.isHidden = false
|
||||
let previousValue = self.currentValue
|
||||
var segments: [AnimatedCountLabelNode.Segment] = []
|
||||
if let value = Int(self.badge) {
|
||||
self.currentValue = value
|
||||
segments.append(.number(value, NSAttributedString(string: self.badge, font: badgeFont, textColor: self.theme.chat.historyNavigation.badgeTextColor)))
|
||||
} else {
|
||||
self.currentValue = 0
|
||||
segments.append(.text(100, NSAttributedString(string: self.badge, font: badgeFont, textColor: self.theme.chat.historyNavigation.badgeTextColor)))
|
||||
}
|
||||
self.badgeTextNode.segments = segments
|
||||
|
||||
let badgeSize = self.badgeTextNode.measure(CGSize(width: 200.0, height: 100.0))
|
||||
let backgroundSize = CGSize(width: max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0)
|
||||
let badgeSize = self.badgeTextNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true)
|
||||
let backgroundSize = CGSize(width: self.badge.count == 1 ? 18.0 : max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0)
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floor((38.0 - backgroundSize.width) / 2.0), y: -9.0), size: backgroundSize)
|
||||
self.badgeBackgroundNode.frame = backgroundFrame
|
||||
self.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(backgroundFrame.midX - badgeSize.width / 2.0), y: -8.0), size: badgeSize)
|
||||
self.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundFrame.width - badgeSize.width) / 2.0), y: 1.0), size: badgeSize)
|
||||
|
||||
if self.badgeBackgroundNode.alpha < 1.0 {
|
||||
self.badgeBackgroundNode.alpha = 1.0
|
||||
|
||||
self.badgeBackgroundNode.layer.animateScale(from: 0.01, to: 1.2, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.badgeBackgroundNode.layer.animateScale(from: 1.15, to: 1.0, duration: 0.12, removeOnCompletion: false, completion: { _ in
|
||||
strongSelf.badgeBackgroundNode.layer.removeAllAnimations()
|
||||
})
|
||||
}
|
||||
})
|
||||
self.badgeBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
} else if previousValue != self.currentValue {
|
||||
self.badgeBackgroundNode.layer.animateScale(from: 1.0, to: 1.2, duration: 0.12, removeOnCompletion: false, completion: { [weak self] finished in
|
||||
if let strongSelf = self {
|
||||
strongSelf.badgeBackgroundNode.layer.animateScale(from: 1.2, to: 1.0, duration: 0.12, removeOnCompletion: false, completion: { _ in
|
||||
strongSelf.badgeBackgroundNode.layer.removeAllAnimations()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
self.badgeBackgroundNode.isHidden = true
|
||||
self.badgeTextNode.isHidden = true
|
||||
self.currentValue = 0
|
||||
if self.badgeBackgroundNode.alpha > 0.0 {
|
||||
self.badgeBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||
self.badgeBackgroundNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2)
|
||||
}
|
||||
self.badgeBackgroundNode.alpha = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user