mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Shared media improvements
This commit is contained in:
parent
87890295be
commit
c821cc1ab3
@ -296,6 +296,14 @@
|
|||||||
"Weekday.Today" = "Today";
|
"Weekday.Today" = "Today";
|
||||||
"Weekday.Yesterday" = "Yesterday";
|
"Weekday.Yesterday" = "Yesterday";
|
||||||
|
|
||||||
|
"Calendar.ShortMonday" = "M";
|
||||||
|
"Calendar.ShortTuesday" = "T";
|
||||||
|
"Calendar.ShortWednesday" = "W";
|
||||||
|
"Calendar.ShortThursday" = "T";
|
||||||
|
"Calendar.ShortFriday" = "F";
|
||||||
|
"Calendar.ShortSaturday" = "S";
|
||||||
|
"Calendar.ShortSunday" = "S";
|
||||||
|
|
||||||
"Time.TodayAt" = "today at %@";
|
"Time.TodayAt" = "today at %@";
|
||||||
"Time.YesterdayAt" = "yesterday at %@";
|
"Time.YesterdayAt" = "yesterday at %@";
|
||||||
|
|
||||||
@ -6986,3 +6994,33 @@ Sorry for the inconvenience.";
|
|||||||
"AuthSessions.View.IP" = "IP Address";
|
"AuthSessions.View.IP" = "IP Address";
|
||||||
"AuthSessions.View.TerminateSession" = "Terminate Session";
|
"AuthSessions.View.TerminateSession" = "Terminate Session";
|
||||||
"AuthSessions.View.Logout" = "Log Out";
|
"AuthSessions.View.Logout" = "Log Out";
|
||||||
|
|
||||||
|
"MessageCalendar.Title" = "Calendar";
|
||||||
|
"MessageCalendar.DaysSelectedTitle_1" = "1 day selected";
|
||||||
|
"MessageCalendar.DaysSelectedTitle_any" = "%@ days selected";
|
||||||
|
"MessageCalendar.DeleteConfirmation_1" = "Are you sure you want to delete all messages for the selected day?";
|
||||||
|
"MessageCalendar.DeleteConfirmation_1" = "Are you sure you want to delete all messages for the selected %@ days?";
|
||||||
|
|
||||||
|
"SharedMedia.PhotoCount_1" = "1 photo";
|
||||||
|
"SharedMedia.PhotoCount_any" = "%@ photos";
|
||||||
|
"SharedMedia.VideoCount_1" = "1 video";
|
||||||
|
"SharedMedia.VideoCount_any" = "%@ videos";
|
||||||
|
"SharedMedia.GifCount_1" = "1 gif";
|
||||||
|
"SharedMedia.GifCount_any" = "%@ gifs";
|
||||||
|
"SharedMedia.FileCount_1" = "1 file";
|
||||||
|
"SharedMedia.FileCount_any" = "%@ files";
|
||||||
|
"SharedMedia.MusicCount_1" = "1 music file";
|
||||||
|
"SharedMedia.MusicCount_any" = "%@ music files";
|
||||||
|
"SharedMedia.VoiceMessageCount_1" = "1 voice message";
|
||||||
|
"SharedMedia.VoiceMessageCount_any" = "%@ voice messages";
|
||||||
|
"SharedMedia.LinkCount_1" = "1 link";
|
||||||
|
"SharedMedia.LinkCount_any" = "%@ links";
|
||||||
|
|
||||||
|
"SharedMedia.FastScrollTooltip" = "You can hold and move this bar for faster scrolling";
|
||||||
|
"SharedMedia.CalendarTooltip" = "Tap on this icon for calendar view";
|
||||||
|
|
||||||
|
"SharedMedia.ZoomIn" = "Zoom In";
|
||||||
|
"SharedMedia.ZoomOut" = "Zoom Out";
|
||||||
|
"SharedMedia.ShowCalendar" = "Show Calendar";
|
||||||
|
"SharedMedia.ShowPhotos" = "Show Photos";
|
||||||
|
"SharedMedia.ShowVideos" = "Show Videos";
|
||||||
|
@ -151,24 +151,21 @@ private func monthName(index: Int, strings: PresentationStrings) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func dayName(index: Int, strings: PresentationStrings) -> String {
|
private func dayName(index: Int, strings: PresentationStrings) -> String {
|
||||||
let _ = strings
|
|
||||||
//TODO:localize
|
|
||||||
|
|
||||||
switch index {
|
switch index {
|
||||||
case 0:
|
case 0:
|
||||||
return "M"
|
return strings.Calendar_ShortMonday
|
||||||
case 1:
|
case 1:
|
||||||
return "T"
|
return strings.Calendar_ShortTuesday
|
||||||
case 2:
|
case 2:
|
||||||
return "W"
|
return strings.Calendar_ShortWednesday
|
||||||
case 3:
|
case 3:
|
||||||
return "T"
|
return strings.Calendar_ShortThursday
|
||||||
case 4:
|
case 4:
|
||||||
return "F"
|
return strings.Calendar_ShortFriday
|
||||||
case 5:
|
case 5:
|
||||||
return "S"
|
return strings.Calendar_ShortSaturday
|
||||||
case 6:
|
case 6:
|
||||||
return "S"
|
return strings.Calendar_ShortSunday
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -1402,8 +1399,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let _ = info.canClearForMyself ?? info.canClearForEveryone {
|
if let _ = info.canClearForMyself ?? info.canClearForEveryone {
|
||||||
//TODO:localize
|
items.append(ActionSheetTextItem(title: strongSelf.presentationData.strings.MessageCalendar_DeleteConfirmation(Int32(selectedCount))))
|
||||||
items.append(ActionSheetTextItem(title: "Are you sure you want to delete all messages for the \(selectedCount) selected days?"))
|
|
||||||
|
|
||||||
if let canClearForEveryone = info.canClearForEveryone {
|
if let canClearForEveryone = info.canClearForEveryone {
|
||||||
let text: String
|
let text: String
|
||||||
@ -1627,8 +1623,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateSelectionState() {
|
private func updateSelectionState() {
|
||||||
//TODO:localize
|
var title = self.presentationData.strings.MessageCalendar_Title
|
||||||
var title = "Calendar"
|
|
||||||
if let selectionState = self.selectionState, let dayRange = selectionState.dayRange {
|
if let selectionState = self.selectionState, let dayRange = selectionState.dayRange {
|
||||||
var selectedCount = 0
|
var selectedCount = 0
|
||||||
for i in 0 ..< self.months.count {
|
for i in 0 ..< self.months.count {
|
||||||
@ -1643,11 +1638,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if selectedCount != 0 {
|
if selectedCount != 0 {
|
||||||
if selectedCount == 1 {
|
title = self.presentationData.strings.MessageCalendar_DaysSelectedTitle(Int32(selectedCount))
|
||||||
title = "1 day selected"
|
|
||||||
} else {
|
|
||||||
title = "\(selectedCount) days selected"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1732,8 +1723,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
self.navigationPresentation = .modal
|
self.navigationPresentation = .modal
|
||||||
|
|
||||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(dismissPressed)), animated: false)
|
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(dismissPressed)), animated: false)
|
||||||
//TODO:localize
|
self.navigationItem.setTitle(self.presentationData.strings.MessageCalendar_Title, animated: false)
|
||||||
self.navigationItem.setTitle("Calendar", animated: false)
|
|
||||||
|
|
||||||
/*if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
|
/*if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
|
||||||
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.toggleSelectPressed)), animated: false)
|
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.toggleSelectPressed)), animated: false)
|
||||||
|
@ -290,3 +290,177 @@ public class InteractiveCheckNode: CheckNode {
|
|||||||
self.buttonNode.frame = self.bounds
|
self.buttonNode.frame = self.bounds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class NullActionClass: NSObject, CAAction {
|
||||||
|
@objc func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let nullAction = NullActionClass()
|
||||||
|
|
||||||
|
public class CheckLayer: CALayer {
|
||||||
|
private var animatingOut = false
|
||||||
|
private var animationProgress: CGFloat = 0.0
|
||||||
|
public var theme: CheckNodeTheme {
|
||||||
|
didSet {
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(theme: CheckNodeTheme, content: CheckNodeContent = .check) {
|
||||||
|
self.theme = theme
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.isOpaque = false
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func action(forKey event: String) -> CAAction? {
|
||||||
|
return nullAction
|
||||||
|
}
|
||||||
|
|
||||||
|
public var content: CheckNodeContent {
|
||||||
|
didSet {
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var selected = false
|
||||||
|
public func setSelected(_ selected: Bool, animated: Bool = false) {
|
||||||
|
guard self.selected != selected else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.selected = selected
|
||||||
|
|
||||||
|
if animated {
|
||||||
|
self.animatingOut = !selected
|
||||||
|
|
||||||
|
let animation = POPBasicAnimation()
|
||||||
|
animation.property = (POPAnimatableProperty.property(withName: "progress", initializer: { property in
|
||||||
|
property?.readBlock = { node, values in
|
||||||
|
values?.pointee = (node as! CheckLayer).animationProgress
|
||||||
|
}
|
||||||
|
property?.writeBlock = { node, values in
|
||||||
|
(node as! CheckLayer).animationProgress = values!.pointee
|
||||||
|
(node as! CheckLayer).setNeedsDisplay()
|
||||||
|
}
|
||||||
|
property?.threshold = 0.01
|
||||||
|
}) as! POPAnimatableProperty)
|
||||||
|
animation.fromValue = (selected ? 0.0 : 1.0) as NSNumber
|
||||||
|
animation.toValue = (selected ? 1.0 : 0.0) as NSNumber
|
||||||
|
animation.timingFunction = CAMediaTimingFunction(name: selected ? CAMediaTimingFunctionName.easeOut : CAMediaTimingFunctionName.easeIn)
|
||||||
|
animation.duration = selected ? 0.21 : 0.15
|
||||||
|
self.pop_add(animation, forKey: "progress")
|
||||||
|
} else {
|
||||||
|
self.pop_removeAllAnimations()
|
||||||
|
self.animatingOut = false
|
||||||
|
self.animationProgress = selected ? 1.0 : 0.0
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setHighlighted(_ highlighted: Bool, animated: Bool = false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func display() {
|
||||||
|
if self.bounds.isEmpty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.contents = generateImage(self.bounds.size, rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let parameters = CheckNodeParameters(theme: self.theme, content: self.content, animationProgress: self.animationProgress, selected: self.selected, animatingOut: self.animatingOut)
|
||||||
|
|
||||||
|
let center = CGPoint(x: bounds.width / 2.0, y: bounds.width / 2.0)
|
||||||
|
|
||||||
|
var borderWidth: CGFloat = 1.0 + UIScreenPixel
|
||||||
|
if parameters.theme.hasInset {
|
||||||
|
borderWidth = 1.5
|
||||||
|
}
|
||||||
|
if let customBorderWidth = parameters.theme.borderWidth {
|
||||||
|
borderWidth = customBorderWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkWidth: CGFloat = 1.5
|
||||||
|
|
||||||
|
let inset: CGFloat = parameters.theme.hasInset ? 2.0 - UIScreenPixel : 0.0
|
||||||
|
|
||||||
|
let checkProgress = parameters.animatingOut ? 1.0 : parameters.animationProgress
|
||||||
|
let fillProgress = parameters.animatingOut ? 1.0 : min(1.0, parameters.animationProgress * 1.35)
|
||||||
|
|
||||||
|
context.setStrokeColor(parameters.theme.borderColor.cgColor)
|
||||||
|
context.setLineWidth(borderWidth)
|
||||||
|
|
||||||
|
let maybeScaleOut = {
|
||||||
|
if parameters.animatingOut {
|
||||||
|
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||||
|
context.scaleBy(x: parameters.animationProgress, y: parameters.animationProgress)
|
||||||
|
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||||
|
|
||||||
|
context.setAlpha(parameters.animationProgress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let borderInset = borderWidth / 2.0 + inset
|
||||||
|
let borderProgress: CGFloat = parameters.theme.filledBorder ? fillProgress : 1.0
|
||||||
|
let borderFrame = bounds.insetBy(dx: borderInset, dy: borderInset)
|
||||||
|
|
||||||
|
if parameters.theme.filledBorder {
|
||||||
|
maybeScaleOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
context.saveGState()
|
||||||
|
if parameters.theme.hasShadow {
|
||||||
|
context.setShadow(offset: CGSize(), blur: 2.5, color: UIColor(rgb: 0x000000, alpha: 0.22).cgColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.strokeEllipse(in: borderFrame.insetBy(dx: borderFrame.width * (1.0 - borderProgress), dy: borderFrame.height * (1.0 - borderProgress)))
|
||||||
|
context.restoreGState()
|
||||||
|
|
||||||
|
if !parameters.theme.filledBorder {
|
||||||
|
maybeScaleOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setFillColor(parameters.theme.backgroundColor.cgColor)
|
||||||
|
|
||||||
|
let fillInset = parameters.theme.overlayBorder ? borderWidth + inset : inset
|
||||||
|
let fillFrame = bounds.insetBy(dx: fillInset, dy: fillInset)
|
||||||
|
context.fillEllipse(in: fillFrame.insetBy(dx: fillFrame.width * (1.0 - fillProgress), dy: fillFrame.height * (1.0 - fillProgress)))
|
||||||
|
|
||||||
|
let scale = (bounds.width - inset) / 18.0
|
||||||
|
|
||||||
|
let firstSegment: CGFloat = max(0.0, min(1.0, checkProgress * 3.0))
|
||||||
|
let s = CGPoint(x: center.x - (4.0 - 0.3333) * scale, y: center.y + 0.5 * scale)
|
||||||
|
let p1 = CGPoint(x: 2.5 * scale, y: 3.0 * scale)
|
||||||
|
let p2 = CGPoint(x: 4.6667 * scale, y: -6.0 * scale)
|
||||||
|
|
||||||
|
if !firstSegment.isZero {
|
||||||
|
if firstSegment < 1.0 {
|
||||||
|
context.move(to: CGPoint(x: s.x + p1.x * firstSegment, y: s.y + p1.y * firstSegment))
|
||||||
|
context.addLine(to: s)
|
||||||
|
} else {
|
||||||
|
let secondSegment = (checkProgress - 0.33) * 1.5
|
||||||
|
context.move(to: CGPoint(x: s.x + p1.x + p2.x * secondSegment, y: s.y + p1.y + p2.y * secondSegment))
|
||||||
|
context.addLine(to: CGPoint(x: s.x + p1.x, y: s.y + p1.y))
|
||||||
|
context.addLine(to: s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setStrokeColor(parameters.theme.strokeColor.cgColor)
|
||||||
|
if parameters.theme.strokeColor == .clear {
|
||||||
|
context.setBlendMode(.clear)
|
||||||
|
}
|
||||||
|
context.setLineWidth(checkWidth)
|
||||||
|
context.setLineCap(.round)
|
||||||
|
context.setLineJoin(.round)
|
||||||
|
context.setMiterLimit(10.0)
|
||||||
|
|
||||||
|
context.strokePath()
|
||||||
|
})?.cgImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -59,3 +59,59 @@ public final class GridMessageSelectionNode: ASDisplayNode {
|
|||||||
self.checkNode.frame = CGRect(origin: CGPoint(x: self.bounds.size.width - checkSize.width - 2.0, y: 2.0), size: checkSize)
|
self.checkNode.frame = CGRect(origin: CGPoint(x: self.bounds.size.width - checkSize.width - 2.0, y: 2.0), size: checkSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class NullActionClass: NSObject, CAAction {
|
||||||
|
@objc func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let nullAction = NullActionClass()
|
||||||
|
|
||||||
|
public final class GridMessageSelectionLayer: CALayer {
|
||||||
|
private var selected = false
|
||||||
|
private let checkLayer: CheckLayer
|
||||||
|
|
||||||
|
public init(theme: CheckNodeTheme) {
|
||||||
|
self.checkLayer = CheckLayer(theme: theme, content: .check)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSublayer(self.checkLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func action(forKey event: String) -> CAAction? {
|
||||||
|
return nullAction
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateIn() {
|
||||||
|
self.checkLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
self.checkLayer.animateScale(from: 0.2, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateOut(completion: @escaping () -> Void) {
|
||||||
|
self.checkLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
|
self.checkLayer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateSelected(_ selected: Bool, animated: Bool) {
|
||||||
|
if self.selected != selected {
|
||||||
|
self.selected = selected
|
||||||
|
self.checkLayer.setSelected(selected, animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateLayout(size: CGSize) {
|
||||||
|
let checkSize = CGSize(width: 28.0, height: 28.0)
|
||||||
|
let previousSize = self.checkLayer.bounds.size
|
||||||
|
self.checkLayer.frame = CGRect(origin: CGPoint(x: self.bounds.size.width - checkSize.width - 2.0, y: 2.0), size: checkSize)
|
||||||
|
if self.checkLayer.bounds.size != previousSize {
|
||||||
|
self.checkLayer.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1214,6 +1214,12 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
public var cancelExternalContentGestures: (() -> Void)?
|
public var cancelExternalContentGestures: (() -> Void)?
|
||||||
public var zoomLevelUpdated: ((ZoomLevel) -> Void)?
|
public var zoomLevelUpdated: ((ZoomLevel) -> Void)?
|
||||||
|
|
||||||
|
public var pinchEnabled: Bool = true {
|
||||||
|
didSet {
|
||||||
|
self.pinchRecognizer?.isEnabled = self.pinchEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public init(theme: PresentationTheme) {
|
public init(theme: PresentationTheme) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
|
||||||
|
@ -148,16 +148,17 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
switch tagMask {
|
switch tagMask {
|
||||||
case MessageTags.file:
|
case MessageTags.file:
|
||||||
return PeerInfoStatusData(text: "\(count) files", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_FileCount(Int32(count)), isActivity: false)
|
||||||
case MessageTags.music:
|
case MessageTags.music:
|
||||||
return PeerInfoStatusData(text: "\(count) music files", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_MusicCount(Int32(count)), isActivity: false)
|
||||||
case MessageTags.voiceOrInstantVideo:
|
case MessageTags.voiceOrInstantVideo:
|
||||||
return PeerInfoStatusData(text: "\(count) voice messages", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_VoiceMessageCount(Int32(count)), isActivity: false)
|
||||||
case MessageTags.webPage:
|
case MessageTags.webPage:
|
||||||
return PeerInfoStatusData(text: "\(count) links", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_LinkCount(Int32(count)), isActivity: false)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import DirectMediaImageCache
|
|||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import TelegramNotices
|
import TelegramNotices
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
|
import CheckNode
|
||||||
|
|
||||||
private final class FrameSequenceThumbnailNode: ASDisplayNode {
|
private final class FrameSequenceThumbnailNode: ASDisplayNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -754,6 +755,7 @@ private final class DurationLayer: CALayer {
|
|||||||
private final class ItemLayer: CALayer, SparseItemGridLayer {
|
private final class ItemLayer: CALayer, SparseItemGridLayer {
|
||||||
var item: VisualMediaItem?
|
var item: VisualMediaItem?
|
||||||
var durationLayer: DurationLayer?
|
var durationLayer: DurationLayer?
|
||||||
|
var selectionLayer: GridMessageSelectionLayer?
|
||||||
var disposable: Disposable?
|
var disposable: Disposable?
|
||||||
|
|
||||||
var hasContents: Bool = false
|
var hasContents: Bool = false
|
||||||
@ -796,6 +798,35 @@ private final class ItemLayer: CALayer, SparseItemGridLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateSelection(theme: CheckNodeTheme, isSelected: Bool?, animated: Bool) {
|
||||||
|
if let isSelected = isSelected {
|
||||||
|
if let selectionLayer = self.selectionLayer {
|
||||||
|
selectionLayer.updateSelected(isSelected, animated: animated)
|
||||||
|
} else {
|
||||||
|
let selectionLayer = GridMessageSelectionLayer(theme: theme)
|
||||||
|
selectionLayer.updateSelected(isSelected, animated: false)
|
||||||
|
self.selectionLayer = selectionLayer
|
||||||
|
self.addSublayer(selectionLayer)
|
||||||
|
if !self.bounds.isEmpty {
|
||||||
|
selectionLayer.frame = CGRect(origin: CGPoint(), size: self.bounds.size)
|
||||||
|
selectionLayer.updateLayout(size: self.bounds.size)
|
||||||
|
if animated {
|
||||||
|
selectionLayer.animateIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let selectionLayer = self.selectionLayer {
|
||||||
|
self.selectionLayer = nil
|
||||||
|
if animated {
|
||||||
|
selectionLayer.animateOut { [weak selectionLayer] in
|
||||||
|
selectionLayer?.removeFromSuperlayer()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectionLayer.removeFromSuperlayer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func unbind() {
|
func unbind() {
|
||||||
self.item = nil
|
self.item = nil
|
||||||
}
|
}
|
||||||
@ -975,6 +1006,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
let listItemInteraction: ListMessageItemInteraction
|
let listItemInteraction: ListMessageItemInteraction
|
||||||
let chatControllerInteraction: ChatControllerInteraction
|
let chatControllerInteraction: ChatControllerInteraction
|
||||||
let chatPresentationData: ChatPresentationData
|
let chatPresentationData: ChatPresentationData
|
||||||
|
let checkNodeTheme: CheckNodeTheme
|
||||||
|
|
||||||
var loadHoleImpl: ((SparseItemGrid.HoleAnchor, SparseItemGrid.HoleLocation) -> Signal<Never, NoError>)?
|
var loadHoleImpl: ((SparseItemGrid.HoleAnchor, SparseItemGrid.HoleLocation) -> Signal<Never, NoError>)?
|
||||||
var onTapImpl: ((VisualMediaItem) -> Void)?
|
var onTapImpl: ((VisualMediaItem) -> Void)?
|
||||||
@ -1000,6 +1032,8 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
|
|
||||||
let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)
|
let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)
|
||||||
self.chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0)
|
self.chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0)
|
||||||
|
|
||||||
|
self.checkNodeTheme = CheckNodeTheme(theme: presentationData.theme, style: .overlay, hasInset: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getListShimmerImage(height: CGFloat) -> UIImage {
|
func getListShimmerImage(height: CGFloat) -> UIImage {
|
||||||
@ -1250,6 +1284,12 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
layer.updateDuration(duration: duration)
|
layer.updateDuration(duration: duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let selectionState = self.chatControllerInteraction.selectionState {
|
||||||
|
layer.updateSelection(theme: self.checkNodeTheme, isSelected: selectionState.selectedIds.contains(message.id), animated: false)
|
||||||
|
} else {
|
||||||
|
layer.updateSelection(theme: self.checkNodeTheme, isSelected: nil, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
layer.bind(item: item)
|
layer.bind(item: item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1484,8 +1524,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if count < 1 {
|
if count < 1 {
|
||||||
//TODO:localize
|
strongSelf.itemGrid.updateScrollingAreaTooltip(tooltip: SparseItemGridScrollingArea.DisplayTooltip(animation: "anim_infotip", text: strongSelf.itemGridBinding.chatPresentationData.strings.SharedMedia_FastScrollTooltip, completed: {
|
||||||
strongSelf.itemGrid.updateScrollingAreaTooltip(tooltip: SparseItemGridScrollingArea.DisplayTooltip(animation: "anim_infotip", text: "You can hold and move this bar for faster scrolling", completed: {
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1505,8 +1544,16 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if let selectionState = strongSelf.chatControllerInteraction.selectionState {
|
||||||
|
var toggledValue = true
|
||||||
|
if selectionState.selectedIds.contains(item.message.id) {
|
||||||
|
toggledValue = false
|
||||||
|
}
|
||||||
|
strongSelf.chatControllerInteraction.toggleMessagesSelection([item.message.id], toggledValue)
|
||||||
|
} else {
|
||||||
let _ = strongSelf.chatControllerInteraction.openMessage(item.message, .default)
|
let _ = strongSelf.chatControllerInteraction.openMessage(item.message, .default)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.itemGridBinding.onTagTapImpl = { [weak self] in
|
self.itemGridBinding.onTagTapImpl = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -1813,72 +1860,67 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|> map { contentType, dict -> PeerInfoStatusData? in
|
|> map { contentType, dict -> PeerInfoStatusData? in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
switch contentType {
|
switch contentType {
|
||||||
case .photoOrVideo:
|
case .photoOrVideo:
|
||||||
let photoCount: Int32 = dict[.photo] ?? 0
|
let photoCount: Int32 = dict[.photo] ?? 0
|
||||||
let videoCount: Int32 = dict[.video] ?? 0
|
let videoCount: Int32 = dict[.video] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if photoCount != 0 && videoCount != 0 {
|
if photoCount != 0 && videoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(photoCount) photos, \(videoCount) videos", isActivity: false)
|
return PeerInfoStatusData(text: "\(presentationData.strings.SharedMedia_PhotoCount(Int32(photoCount))), \(presentationData.strings.SharedMedia_VideoCount(Int32(videoCount)))", isActivity: false)
|
||||||
} else if photoCount != 0 {
|
} else if photoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(photoCount) photos", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_PhotoCount(Int32(photoCount)), isActivity: false)
|
||||||
} else if videoCount != 0 {
|
} else if videoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(videoCount) videos", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_VideoCount(Int32(videoCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .photo:
|
case .photo:
|
||||||
let photoCount: Int32 = dict[.photo] ?? 0
|
let photoCount: Int32 = dict[.photo] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if photoCount != 0 {
|
if photoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(photoCount) photos", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_PhotoCount(Int32(photoCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .video:
|
case .video:
|
||||||
let videoCount: Int32 = dict[.video] ?? 0
|
let videoCount: Int32 = dict[.video] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if videoCount != 0 {
|
if videoCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(videoCount) videos", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_VideoCount(Int32(videoCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .gifs:
|
case .gifs:
|
||||||
let gifCount: Int32 = dict[.gif] ?? 0
|
let gifCount: Int32 = dict[.gif] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if gifCount != 0 {
|
if gifCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(gifCount) gifs", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_GifCount(Int32(gifCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .files:
|
case .files:
|
||||||
let fileCount: Int32 = dict[.file] ?? 0
|
let fileCount: Int32 = dict[.file] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if fileCount != 0 {
|
if fileCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(fileCount) files", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_FileCount(Int32(fileCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .voiceAndVideoMessages:
|
case .voiceAndVideoMessages:
|
||||||
let itemCount: Int32 = dict[.voiceOrInstantVideo] ?? 0
|
let itemCount: Int32 = dict[.voiceOrInstantVideo] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if itemCount != 0 {
|
if itemCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(itemCount) voice messages", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_VoiceMessageCount(Int32(itemCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .music:
|
case .music:
|
||||||
let itemCount: Int32 = dict[.music] ?? 0
|
let itemCount: Int32 = dict[.music] ?? 0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
if itemCount != 0 {
|
if itemCount != 0 {
|
||||||
return PeerInfoStatusData(text: "\(itemCount) music files", isActivity: false)
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_MusicCount(Int32(itemCount)), isActivity: false)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2105,6 +2147,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateSelectedMessages(animated: Bool) {
|
func updateSelectedMessages(animated: Bool) {
|
||||||
|
switch self.contentType {
|
||||||
|
case .files, .music, .voiceAndVideoMessages:
|
||||||
self.itemGrid.forEachVisibleItem { item in
|
self.itemGrid.forEachVisibleItem { item in
|
||||||
guard let itemView = item.view as? ItemView else {
|
guard let itemView = item.view as? ItemView else {
|
||||||
return
|
return
|
||||||
@ -2121,6 +2165,16 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case .photo, .video, .photoOrVideo, .gifs:
|
||||||
|
self.itemGrid.forEachVisibleItem { item in
|
||||||
|
guard let itemLayer = item.layer as? ItemLayer, let item = itemLayer.item else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: self.chatControllerInteraction.selectionState?.selectedIds.contains(item.message.id), animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.itemGrid.pinchEnabled = self.chatControllerInteraction.selectionState == nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
@ -6061,8 +6061,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let buttonFrame = buttonNode.view.convert(buttonNode.bounds, to: self.view)
|
let buttonFrame = buttonNode.view.convert(buttonNode.bounds, to: self.view)
|
||||||
//TODO:localize
|
controller.present(TooltipScreen(account: self.context.account, text: self.presentationData.strings.SharedMedia_CalendarTooltip, style: .default, icon: .none, location: .point(buttonFrame.insetBy(dx: 0.0, dy: 5.0), .top), shouldDismissOnTouch: { point in
|
||||||
controller.present(TooltipScreen(account: self.context.account, text: "Tap on this icon for calendar view", style: .default, icon: .none, location: .point(buttonFrame.insetBy(dx: 0.0, dy: 5.0), .top), shouldDismissOnTouch: { point in
|
|
||||||
return .dismiss(consume: false)
|
return .dismiss(consume: false)
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
@ -6099,14 +6098,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
//TODO:localize
|
|
||||||
|
let strings = strongSelf.presentationData.strings
|
||||||
|
|
||||||
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
|
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
|
||||||
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
|
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
|
||||||
let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement
|
let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement
|
||||||
let canZoom: Bool = nextZoomLevel != nil
|
let canZoom: Bool = nextZoomLevel != nil
|
||||||
|
|
||||||
return ContextMenuActionItem(id: isZoomIn ? 0 : 1, text: isZoomIn ? "Zoom In" : "Zoom Out", textColor: canZoom ? .primary : .disabled, icon: { theme in
|
return ContextMenuActionItem(id: isZoomIn ? 0 : 1, text: isZoomIn ? strings.SharedMedia_ZoomIn : strings.SharedMedia_ZoomOut, textColor: canZoom ? .primary : .disabled, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: isZoomIn ? "Chat/Context Menu/ZoomIn" : "Chat/Context Menu/ZoomOut"), color: canZoom ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4))
|
return generateTintedImage(image: UIImage(bundleImageName: isZoomIn ? "Chat/Context Menu/ZoomIn" : "Chat/Context Menu/ZoomOut"), color: canZoom ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4))
|
||||||
}, action: canZoom ? { action in
|
}, action: canZoom ? { action in
|
||||||
guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else {
|
guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else {
|
||||||
@ -6127,7 +6127,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
items.append(.action(generateAction(false)))
|
items.append(.action(generateAction(false)))
|
||||||
|
|
||||||
var ignoreNextActions = false
|
var ignoreNextActions = false
|
||||||
items.append(.action(ContextMenuActionItem(text: "Show Calendar", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowCalendar, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor)
|
||||||
}, action: { _, a in
|
}, action: { _, a in
|
||||||
if ignoreNextActions {
|
if ignoreNextActions {
|
||||||
@ -6157,7 +6157,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
showVideos = false
|
showVideos = false
|
||||||
}
|
}
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: "Show Photos", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowPhotos, icon: { theme in
|
||||||
if !showPhotos {
|
if !showPhotos {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -6181,7 +6181,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
pane.updateContentType(contentType: updatedContentType)
|
pane.updateContentType(contentType: updatedContentType)
|
||||||
})))
|
})))
|
||||||
items.append(.action(ContextMenuActionItem(text: "Show Videos", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowVideos, icon: { theme in
|
||||||
if !showVideos {
|
if !showVideos {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -798,8 +798,9 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe
|
|||||||
|
|
||||||
private var animationTimer: SwiftSignalKit.Timer?
|
private var animationTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
|
private let statusPromise = Promise<PeerInfoStatusData?>(nil)
|
||||||
var status: Signal<PeerInfoStatusData?, NoError> {
|
var status: Signal<PeerInfoStatusData?, NoError> {
|
||||||
return .single(nil)
|
self.statusPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)?
|
var tabBarOffsetUpdated: ((ContainedViewLayoutTransition) -> Void)?
|
||||||
@ -872,6 +873,23 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe
|
|||||||
}, queue: .mainQueue())
|
}, queue: .mainQueue())
|
||||||
self.animationTimer = animationTimer
|
self.animationTimer = animationTimer
|
||||||
animationTimer.start()
|
animationTimer.start()
|
||||||
|
|
||||||
|
self.statusPromise.set(context.account.postbox.combinedView(keys: [PostboxViewKey.historyTagSummaryView(tag: tagMaskForType(self.contentType), peerId: peerId, namespace: Namespaces.Message.Cloud)])
|
||||||
|
|> map { views -> PeerInfoStatusData? in
|
||||||
|
let count: Int32 = (views.views[PostboxViewKey.historyTagSummaryView(tag: tagMaskForType(self.contentType), peerId: peerId, namespace: Namespaces.Message.Cloud)] as? MessageHistoryTagSummaryView)?.count ?? 0
|
||||||
|
if count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
switch contentType {
|
||||||
|
case .gifs:
|
||||||
|
return PeerInfoStatusData(text: presentationData.strings.SharedMedia_GifCount(Int32(count)), isActivity: false)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user