Location view fixes

This commit is contained in:
Ilya Laktyushin 2020-10-26 12:47:25 +04:00
parent c8734c7728
commit 94ece899d8
14 changed files with 249 additions and 125 deletions

View File

@ -105,7 +105,7 @@ final class LocationActionListItem: ListViewItem {
async { async {
let node = LocationActionListItemNode() let node = LocationActionListItemNode()
let makeLayout = node.asyncLayout() let makeLayout = node.asyncLayout()
let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem is LocationActionListItem) let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem is LocationActionListItem || nextItem is LocationLiveListItem)
node.contentSize = nodeLayout.contentSize node.contentSize = nodeLayout.contentSize
node.insets = nodeLayout.insets node.insets = nodeLayout.insets
@ -118,7 +118,7 @@ final class LocationActionListItem: ListViewItem {
if let nodeValue = node() as? LocationActionListItemNode { if let nodeValue = node() as? LocationActionListItemNode {
let layout = nodeValue.asyncLayout() let layout = nodeValue.asyncLayout()
async { async {
let (nodeLayout, apply) = layout(self, params, nextItem is LocationActionListItem) let (nodeLayout, apply) = layout(self, params, nextItem is LocationActionListItem || nextItem is LocationLiveListItem)
Queue.mainQueue().async { Queue.mainQueue().async {
completion(nodeLayout, { info in completion(nodeLayout, { info in
apply().1(info) apply().1(info)

View File

@ -96,7 +96,9 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
} }
var id: String { var id: String {
if let peer = self.peer { if let message = self.message {
return "\(message.id.id)"
} else if let peer = self.peer {
return "\(peer.id.toInt64())" return "\(peer.id.toInt64())"
} else if let venueId = self.location?.venue?.id { } else if let venueId = self.location?.venue?.id {
return venueId return venueId
@ -257,8 +259,8 @@ class LocationPinAnnotationView: MKAnnotationView {
self.dotNode.isHidden = false self.dotNode.isHidden = false
self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground") self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground")
if let author = message.author, let peer = message.peers[author.id] { if let author = message.author {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: peer) self.setPeer(context: annotation.context, theme: annotation.theme, peer: author)
} else if let selfPeer = annotation.selfPeer { } else if let selfPeer = annotation.selfPeer {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: selfPeer) self.setPeer(context: annotation.context, theme: annotation.theme, peer: selfPeer)
} }

View File

@ -338,8 +338,14 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
private func updateDoneButtonTitle() { private func updateDoneButtonTitle() {
if let pickerView = self.pickerView { if let pickerView = self.pickerView {
let largeValue = unitValues[pickerView.selectedRow(inComponent: 0)] let selectedLargeRow = pickerView.selectedRow(inComponent: 0)
let smallValue = smallUnitValues[pickerView.selectedRow(inComponent: 1)] var selectedSmallRow = pickerView.selectedRow(inComponent: 1)
if selectedLargeRow == 0 && selectedSmallRow == 0 {
selectedSmallRow = 1
}
let largeValue = unitValues[selectedLargeRow]
let smallValue = smallUnitValues[selectedSmallRow]
var value = largeValue * 1000 + smallValue * 10 var value = largeValue * 1000 + smallValue * 10
if !self.usesMetricSystem() { if !self.usesMetricSystem() {

View File

@ -6,7 +6,10 @@ import Display
import SwiftSignalKit import SwiftSignalKit
import TelegramCore import TelegramCore
import SyncCore import SyncCore
import AccountContext
import TelegramPresentationData import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
import ItemListUI import ItemListUI
import LocationResources import LocationResources
import AppBundle import AppBundle
@ -15,15 +18,19 @@ import LiveLocationTimerNode
final class LocationLiveListItem: ListViewItem { final class LocationLiveListItem: ListViewItem {
let presentationData: ItemListPresentationData let presentationData: ItemListPresentationData
let account: Account let dateTimeFormat: PresentationDateTimeFormat
let nameDisplayOrder: PresentationPersonNameOrder
let context: AccountContext
let message: Message let message: Message
let distance: Double? let distance: Double?
let action: () -> Void let action: () -> Void
let longTapAction: () -> Void let longTapAction: () -> Void
public init(presentationData: ItemListPresentationData, account: Account, message: Message, distance: Double?, action: @escaping () -> Void, longTapAction: @escaping () -> Void = { }) { public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, message: Message, distance: Double?, action: @escaping () -> Void, longTapAction: @escaping () -> Void = { }) {
self.presentationData = presentationData self.presentationData = presentationData
self.account = account self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder
self.context = context
self.message = message self.message = message
self.distance = distance self.distance = distance
self.action = action self.action = action
@ -92,6 +99,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
self.highlightedBackgroundNode.isLayerBacked = true self.highlightedBackgroundNode.isLayerBacked = true
self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
@ -146,20 +154,39 @@ final class LocationLiveListItemNode: ListViewItemNode {
let leftInset: CGFloat = 65.0 + params.leftInset let leftInset: CGFloat = 65.0 + params.leftInset
let rightInset: CGFloat = params.rightInset let rightInset: CGFloat = params.rightInset
let verticalInset: CGFloat = 8.0 let verticalInset: CGFloat = 8.0
let iconSize: CGFloat = 40.0
let titleFont = Font.medium(item.presentationData.fontSize.itemListBaseFontSize) let titleFont = Font.medium(item.presentationData.fontSize.itemListBaseFontSize)
let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0)) let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
let titleAttributedString = NSAttributedString(string: "title", font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) var title: String = ""
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 15.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) if let author = item.message.author {
title = author.displayTitle(strings: item.presentationData.strings, displayOrder: item.nameDisplayOrder)
}
let titleAttributedString = NSAttributedString(string: title, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 54.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let subtitleAttributedString = NSAttributedString(string: "subtitle", font: subtitleFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor) var updateTimestamp = item.message.timestamp
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 15.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) for attribute in item.message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
updateTimestamp = attribute.date
break
}
}
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
let timeString = stringForRelativeLiveLocationTimestamp(strings: item.presentationData.strings, relativeTimestamp: Int32(updateTimestamp), relativeTo: Int32(timestamp), dateTimeFormat: item.dateTimeFormat)
var subtitle = timeString
if let distance = item.distance {
let distanceString = item.presentationData.strings.Map_DistanceAway(stringForDistance(strings: item.presentationData.strings, distance: distance)).0
subtitle = "\(timeString)\(distanceString)"
}
let subtitleAttributedString = NSAttributedString(string: subtitle, font: subtitleFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 54.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let titleSpacing: CGFloat = 1.0 let titleSpacing: CGFloat = 1.0
let bottomInset: CGFloat = hasSeparator ? 0.0 : 4.0 let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height)
let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height + bottomInset)
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets()) let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets())
return (nodeLayout, { [weak self] in return (nodeLayout, { [weak self] in
@ -168,7 +195,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
updatedTheme = item.presentationData.theme updatedTheme = item.presentationData.theme
} }
return (nil, { _ in return (self?.avatarNode.ready, { _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
strongSelf.layoutParams = params strongSelf.layoutParams = params
@ -199,32 +226,41 @@ final class LocationLiveListItemNode: ListViewItemNode {
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
let topHighlightInset: CGFloat = separatorHeight let topHighlightInset: CGFloat = separatorHeight
let avatarSize: CGFloat = 40.0
// let iconNodeFrame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floorToScreenPixels((contentSize.height - bottomInset - iconSize) / 2.0)), size: CGSize(width: iconSize, height: iconSize)) if let peer = item.message.author {
// strongSelf.iconNode.frame = iconNodeFrame strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false)
// strongSelf.venueIconNode.frame = iconNodeFrame }
strongSelf.avatarNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floorToScreenPixels((contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentSize.width, height: contentSize.height)) strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentSize.width, height: contentSize.height))
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: contentSize.width, height: contentSize.height + topHighlightInset)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: contentSize.width, height: contentSize.height + topHighlightInset))
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width, height: separatorHeight)) strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width, height: separatorHeight))
strongSelf.separatorNode.isHidden = !hasSeparator strongSelf.separatorNode.isHidden = !hasSeparator
// if let (beginTimestamp, timeout) = item.beginTimeAndTimeout { var liveBroadcastingTimeout: Int32 = 0
// let timerNode: ChatMessageLiveLocationTimerNode if let location = getLocation(from: item.message), let timeout = location.liveBroadcastingTimeout {
// if let current = strongSelf.timerNode { liveBroadcastingTimeout = timeout
// timerNode = current }
// } else {
// timerNode = ChatMessageLiveLocationTimerNode() let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
// strongSelf.addSubnode(timerNode) if currentTimestamp < item.message.timestamp + liveBroadcastingTimeout {
// strongSelf.timerNode = timerNode let timerNode: ChatMessageLiveLocationTimerNode
// } if let current = strongSelf.timerNode {
// let timerSize = CGSize(width: 28.0, height: 28.0) timerNode = current
// timerNode.update(backgroundColor: item.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.4), foregroundColor: item.presentationData.theme.list.itemAccentColor, textColor: item.presentationData.theme.list.itemAccentColor, beginTimestamp: beginTimestamp, timeout: timeout, strings: item.presentationData.strings) } else {
// timerNode.frame = CGRect(origin: CGPoint(x: contentSize.width - 16.0 - timerSize.width, y: floorToScreenPixels((contentSize.height - timerSize.height) / 2.0) - 2.0), size: timerSize) timerNode = ChatMessageLiveLocationTimerNode()
// } else if let timerNode = strongSelf.timerNode { strongSelf.addSubnode(timerNode)
// strongSelf.timerNode = nil strongSelf.timerNode = timerNode
// timerNode.removeFromSupernode() }
// } let timerSize = CGSize(width: 28.0, height: 28.0)
timerNode.update(backgroundColor: item.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.4), foregroundColor: item.presentationData.theme.list.itemAccentColor, textColor: item.presentationData.theme.list.itemAccentColor, beginTimestamp: Double(item.message.timestamp), timeout: Double(liveBroadcastingTimeout), strings: item.presentationData.strings)
timerNode.frame = CGRect(origin: CGPoint(x: contentSize.width - 16.0 - timerSize.width, y: floorToScreenPixels((contentSize.height - timerSize.height) / 2.0)), size: timerSize)
} else if let timerNode = strongSelf.timerNode {
strongSelf.timerNode = nil
timerNode.removeFromSupernode()
}
} }
}) })
}) })

View File

@ -758,7 +758,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
} }
if let zoomRect = zoomRect { if let zoomRect = zoomRect {
let insets = UIEdgeInsets(top: 0.0, left: 80.0, bottom: 0.0, right: 80.0) let insets = UIEdgeInsets(top: 88.0, left: 80.0, bottom: 160.0, right: 80.0)
let fittedZoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets) let fittedZoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets)
mapView.setVisibleMapRect(fittedZoomRect, animated: animated) mapView.setVisibleMapRect(fittedZoomRect, animated: animated)
} }

View File

@ -11,6 +11,7 @@ import ItemListUI
import ItemListVenueItem import ItemListVenueItem
import TelegramPresentationData import TelegramPresentationData
import TelegramStringFormatting import TelegramStringFormatting
import TelegramUIPreferences
import TelegramNotices import TelegramNotices
import AccountContext import AccountContext
import AppBundle import AppBundle
@ -48,7 +49,7 @@ private enum LocationViewEntryId: Hashable {
private enum LocationViewEntry: Comparable, Identifiable { private enum LocationViewEntry: Comparable, Identifiable {
case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?) case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?)
case toggleLiveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?, Double?, Double?) case toggleLiveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?, Double?, Double?)
case liveLocation(PresentationTheme, Message, Double?, Int) case liveLocation(PresentationTheme, PresentationDateTimeFormat, PresentationPersonNameOrder, Message, Double?, Int)
var stableId: LocationViewEntryId { var stableId: LocationViewEntryId {
switch self { switch self {
@ -56,7 +57,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
return .info return .info
case .toggleLiveLocation: case .toggleLiveLocation:
return .toggleLiveLocation return .toggleLiveLocation
case let .liveLocation(_, message, _, _): case let .liveLocation(_, _, _, message, _, _):
return .liveLocation(message.stableId) return .liveLocation(message.stableId)
} }
} }
@ -75,8 +76,8 @@ private enum LocationViewEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .liveLocation(lhsTheme, lhsMessage, lhsDistance, lhsIndex): case let .liveLocation(lhsTheme, lhsDateTimeFormat, lhsNameDisplayOrder, lhsMessage, lhsDistance, lhsIndex):
if case let .liveLocation(rhsTheme, rhsMessage, rhsDistance, rhsIndex) = rhs, lhsTheme === rhsTheme, areMessagesEqual(lhsMessage, rhsMessage), lhsDistance == rhsDistance, lhsIndex == rhsIndex { if case let .liveLocation(rhsTheme, rhsDateTimeFormat, rhsNameDisplayOrder, rhsMessage, rhsDistance, rhsIndex) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameDisplayOrder == rhsNameDisplayOrder, areMessagesEqual(lhsMessage, rhsMessage), lhsDistance == rhsDistance, lhsIndex == rhsIndex {
return true return true
} else { } else {
return false return false
@ -100,17 +101,17 @@ private enum LocationViewEntry: Comparable, Identifiable {
case .liveLocation: case .liveLocation:
return true return true
} }
case let .liveLocation(_, _, _, lhsIndex): case let .liveLocation(_, _, _, _, _, lhsIndex):
switch rhs { switch rhs {
case .info, .toggleLiveLocation: case .info, .toggleLiveLocation:
return false return false
case let .liveLocation(_, _, _, rhsIndex): case let .liveLocation(_, _, _, _, _, rhsIndex):
return lhsIndex < rhsIndex return lhsIndex < rhsIndex
} }
} }
} }
func item(account: Account, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem { func item(context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem {
switch self { switch self {
case let .info(_, location, address, distance, time): case let .info(_, location, address, distance, time):
let addressString: String? let addressString: String?
@ -126,7 +127,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
distanceString = nil distanceString = nil
} }
let eta = time.flatMap { stringForEstimatedDuration(strings: presentationData.strings, eta: $0) } let eta = time.flatMap { stringForEstimatedDuration(strings: presentationData.strings, eta: $0) }
return LocationInfoListItem(presentationData: ItemListPresentationData(presentationData), account: account, location: location, address: addressString, distance: distanceString, eta: eta, action: { return LocationInfoListItem(presentationData: ItemListPresentationData(presentationData), account: context.account, location: location, address: addressString, distance: distanceString, eta: eta, action: {
interaction?.goToCoordinate(location.coordinate) interaction?.goToCoordinate(location.coordinate)
}, getDirections: { }, getDirections: {
interaction?.requestDirections() interaction?.requestDirections()
@ -138,7 +139,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
} else { } else {
beginTimeAndTimeout = nil beginTimeAndTimeout = nil
} }
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: beginTimeAndTimeout != nil ? .stopLiveLocation : .liveLocation, beginTimeAndTimeout: beginTimeAndTimeout, action: { return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: context.account, title: title, subtitle: subtitle, icon: beginTimeAndTimeout != nil ? .stopLiveLocation : .liveLocation, beginTimeAndTimeout: beginTimeAndTimeout, action: {
if beginTimeAndTimeout != nil { if beginTimeAndTimeout != nil {
interaction?.stopLiveLocation() interaction?.stopLiveLocation()
} else if let coordinate = coordinate { } else if let coordinate = coordinate {
@ -147,24 +148,18 @@ private enum LocationViewEntry: Comparable, Identifiable {
}, highlighted: { highlight in }, highlighted: { highlight in
interaction?.updateSendActionHighlight(highlight) interaction?.updateSendActionHighlight(highlight)
}) })
case let .liveLocation(_, message, distance, _): case let .liveLocation(_, dateTimeFormat, nameDisplayOrder, message, distance, _):
let distanceString: String? return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: context, message: message, distance: distance, action: {}, longTapAction: {})
if let distance = distance {
distanceString = distance < 10 ? presentationData.strings.Map_YouAreHere : presentationData.strings.Map_DistanceAway(stringForDistance(strings: presentationData.strings, distance: distance)).0
} else {
distanceString = nil
}
return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), account: account, message: message, distance: distance, action: {}, longTapAction: {})
} }
} }
} }
private func preparedTransition(from fromEntries: [LocationViewEntry], to toEntries: [LocationViewEntry], account: Account, presentationData: PresentationData, interaction: LocationViewInteraction?) -> LocationViewTransaction { private func preparedTransition(from fromEntries: [LocationViewEntry], to toEntries: [LocationViewEntry], context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?) -> LocationViewTransaction {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
return LocationViewTransaction(deletions: deletions, insertions: insertions, updates: updates) return LocationViewTransaction(deletions: deletions, insertions: insertions, updates: updates)
} }
@ -401,12 +396,12 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
let subjectLocation = CLLocation(latitude: location.latitude, longitude: location.longitude) let subjectLocation = CLLocation(latitude: location.latitude, longitude: location.longitude)
let distance = userLocation.flatMap { subjectLocation.distance(from: $0) } let distance = userLocation.flatMap { subjectLocation.distance(from: $0) }
// entries.append(.liveLocation(presentationData.theme, message, distance, index))
if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer { if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer {
userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading) userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)
} else { } else {
annotations.append(LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)) annotations.append(LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading))
entries.append(.liveLocation(presentationData.theme, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, message, distance, index))
} }
index += 1 index += 1
} }
@ -414,11 +409,14 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
if subject.id.peerId.namespace != Namespaces.Peer.CloudUser, proximityNotification == nil { if subject.id.peerId.namespace != Namespaces.Peer.CloudUser, proximityNotification == nil {
proximityNotification = false proximityNotification = false
} }
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info {
proximityNotification = nil
}
let previousEntries = previousEntries.swap(entries) let previousEntries = previousEntries.swap(entries)
let previousState = previousState.swap(state) let previousState = previousState.swap(state)
let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction) let transition = preparedTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, interaction: strongSelf.interaction)
strongSelf.enqueueTransition(transition) strongSelf.enqueueTransition(transition)
strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false) strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)

View File

@ -2465,8 +2465,8 @@ public func chatWebFileImage(account: Account, file: TelegramMediaWebFile) -> Si
private let precomposedSmallAlbumArt = Atomic<UIImage?>(value: nil) private let precomposedSmallAlbumArt = Atomic<UIImage?>(value: nil)
private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource) -> Signal<Data?, NoError> { private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource, attemptSynchronously: Bool = false) -> Signal<Data?, NoError> {
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail) let thumbnailResource = postbox.mediaBox.resourceData(thumbnail, attemptSynchronously: attemptSynchronously)
let signal = thumbnailResource |> take(1) |> mapToSignal { maybeData -> Signal<Data?, NoError> in let signal = thumbnailResource |> take(1) |> mapToSignal { maybeData -> Signal<Data?, NoError> in
if maybeData.complete { if maybeData.complete {
@ -2602,7 +2602,7 @@ private func drawAlbumArtPlaceholder(into c: CGContext, arguments: TransformImag
} }
} }
public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true, attemptSynchronously: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
var fileArtworkData: Signal<Data?, NoError> = .single(nil) var fileArtworkData: Signal<Data?, NoError> = .single(nil)
if let fileReference = fileReference { if let fileReference = fileReference {
let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0) let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0)
@ -2628,7 +2628,7 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
let thumbnail = Signal<Data?, NoError> { subscriber in let thumbnail = Signal<Data?, NoError> { subscriber in
let fetchedDisposable = fetchedThumbnail.start() let fetchedDisposable = fetchedThumbnail.start()
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource).start(next: { next in let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource, attemptSynchronously: attemptSynchronously).start(next: { next in
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
}, error: subscriber.putError, completed: subscriber.putCompletion) }, error: subscriber.putError, completed: subscriber.putCompletion)
@ -2643,7 +2643,7 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
} }
} else if let albumArt = albumArt { } else if let albumArt = albumArt {
if thumbnail { if thumbnail {
immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource) immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource, attemptSynchronously: attemptSynchronously)
|> map { thumbnailData in |> map { thumbnailData in
return Tuple(thumbnailData, nil, false) return Tuple(thumbnailData, nil, false)
} }

View File

@ -591,8 +591,8 @@ private final class SemanticStatusNodeAppearanceContext {
self.cutout = cutout self.cutout = cutout
} }
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState { func drawingState(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState {
return SemanticStatusNodeAppearanceDrawingState(transitionFraction: transitionFraction, background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: self.cutout) return SemanticStatusNodeAppearanceDrawingState(backgroundTransitionFraction: backgroundTransitionFraction, foregroundTransitionFraction: foregroundTransitionFraction, background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: self.cutout)
} }
func withUpdatedBackground(_ background: UIColor) -> SemanticStatusNodeAppearanceContext { func withUpdatedBackground(_ background: UIColor) -> SemanticStatusNodeAppearanceContext {
@ -617,7 +617,8 @@ private final class SemanticStatusNodeAppearanceContext {
} }
private final class SemanticStatusNodeAppearanceDrawingState { private final class SemanticStatusNodeAppearanceDrawingState {
let transitionFraction: CGFloat let backgroundTransitionFraction: CGFloat
let foregroundTransitionFraction: CGFloat
let background: UIColor let background: UIColor
let foreground: UIColor let foreground: UIColor
let backgroundImage: UIImage? let backgroundImage: UIImage?
@ -632,8 +633,9 @@ private final class SemanticStatusNodeAppearanceDrawingState {
} }
} }
init(transitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) { init(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
self.transitionFraction = transitionFraction self.backgroundTransitionFraction = backgroundTransitionFraction
self.foregroundTransitionFraction = foregroundTransitionFraction
self.background = background self.background = background
self.foreground = foreground self.foreground = foreground
self.backgroundImage = backgroundImage self.backgroundImage = backgroundImage
@ -644,11 +646,12 @@ private final class SemanticStatusNodeAppearanceDrawingState {
func drawBackground(context: CGContext, size: CGSize) { func drawBackground(context: CGContext, size: CGSize) {
let bounds = CGRect(origin: CGPoint(), size: size) let bounds = CGRect(origin: CGPoint(), size: size)
context.setBlendMode(.normal)
if let backgroundImage = self.backgroundImage?.cgImage { if let backgroundImage = self.backgroundImage?.cgImage {
context.saveGState() context.saveGState()
context.translateBy(x: 0.0, y: bounds.height) context.translateBy(x: 0.0, y: bounds.height)
context.scaleBy(x: 1.0, y: -1.0) context.scaleBy(x: 1.0, y: -1.0)
context.setAlpha(self.transitionFraction) context.setAlpha(self.backgroundTransitionFraction)
context.draw(backgroundImage, in: bounds) context.draw(backgroundImage, in: bounds)
context.restoreGState() context.restoreGState()
} else { } else {
@ -659,7 +662,7 @@ private final class SemanticStatusNodeAppearanceDrawingState {
func drawForeground(context: CGContext, size: CGSize) { func drawForeground(context: CGContext, size: CGSize) {
if let cutout = self.cutout { if let cutout = self.cutout {
let size = CGSize(width: cutout.width * self.transitionFraction, height: cutout.height * self.transitionFraction) let size = CGSize(width: cutout.width * self.foregroundTransitionFraction, height: cutout.height * self.foregroundTransitionFraction)
let rect = CGRect(origin: CGPoint(x: cutout.midX - size.width / 2.0, y: cutout.midY - size.height / 2.0), size: size) let rect = CGRect(origin: CGPoint(x: cutout.midX - size.width / 2.0, y: cutout.midY - size.height / 2.0), size: size)
context.setBlendMode(.clear) context.setBlendMode(.clear)
@ -740,13 +743,23 @@ public final class SemanticStatusNode: ASControlNode {
return self.appearanceContext.cutout return self.appearanceContext.cutout
} }
set { set {
if self.appearanceContext.cutout != newValue { self.setCutout(newValue, animated: false)
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: self.appearanceContext, completion: {}) }
self.appearanceContext = self.appearanceContext.withUpdatedCutout(newValue) }
self.updateAnimations() public func setCutout(_ cutout: CGRect?, animated: Bool) {
self.setNeedsDisplay() guard cutout != self.appearanceContext.cutout else {
} return
}
if animated {
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.2, previousStateContext: nil, previousAppearanceContext: self.appearanceContext, completion: {})
self.appearanceContext = self.appearanceContext.withUpdatedCutout(cutout)
self.updateAnimations()
self.setNeedsDisplay()
} else {
self.appearanceContext = self.appearanceContext.withUpdatedCutout(cutout)
self.setNeedsDisplay()
} }
} }
@ -783,7 +796,7 @@ public final class SemanticStatusNode: ASControlNode {
let previousAppearanceContext = strongSelf.appearanceContext let previousAppearanceContext = strongSelf.appearanceContext
strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage()) strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage())
if CACurrentMediaTime() - start > 0.2 { if CACurrentMediaTime() - start > 0.3 {
strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {}) strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {})
strongSelf.updateAnimations() strongSelf.updateAnimations()
} }
@ -858,7 +871,8 @@ public final class SemanticStatusNode: ASControlNode {
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
var transitionState: SemanticStatusNodeTransitionDrawingState? var transitionState: SemanticStatusNodeTransitionDrawingState?
var transitionFraction: CGFloat = 1.0 var transitionFraction: CGFloat = 1.0
var appearanceTransitionFraction: CGFloat = 1.0 var appearanceBackgroundTransitionFraction: CGFloat = 1.0
var appearanceForegroundTransitionFraction: CGFloat = 1.0
if let transitionContext = self.transitionContext { if let transitionContext = self.transitionContext {
let timestamp = CACurrentMediaTime() let timestamp = CACurrentMediaTime()
@ -868,13 +882,20 @@ public final class SemanticStatusNode: ASControlNode {
if let _ = transitionContext.previousStateContext { if let _ = transitionContext.previousStateContext {
transitionFraction = t transitionFraction = t
} }
if let _ = transitionContext.previousAppearanceContext { var foregroundTransitionFraction: CGFloat = 1.0
appearanceTransitionFraction = t if let previousContext = transitionContext.previousAppearanceContext {
if previousContext.backgroundImage != self.appearanceContext.backgroundImage {
appearanceBackgroundTransitionFraction = t
}
if previousContext.cutout != self.appearanceContext.cutout {
appearanceForegroundTransitionFraction = t
foregroundTransitionFraction = 1.0 - t
}
} }
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext?.drawingState(transitionFraction: 1.0 - t), appearanceState: transitionContext.previousAppearanceContext?.drawingState(transitionFraction: 1.0 - t)) transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext?.drawingState(transitionFraction: 1.0 - t), appearanceState: transitionContext.previousAppearanceContext?.drawingState(backgroundTransitionFraction: 1.0, foregroundTransitionFraction: foregroundTransitionFraction))
} }
return SemanticStatusNodeDrawingState(transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), appearanceState: self.appearanceContext.drawingState(transitionFraction: appearanceTransitionFraction)) return SemanticStatusNodeDrawingState(transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), appearanceState: self.appearanceContext.drawingState(backgroundTransitionFraction: appearanceBackgroundTransitionFraction, foregroundTransitionFraction: appearanceForegroundTransitionFraction))
} }
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { @objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {

View File

@ -442,12 +442,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
case .phoneNumberRequest: case .phoneNumberRequest:
attributedString = nil attributedString = nil
case let .geoProximityReached(_, toId, distance): case let .geoProximityReached(fromId, toId, distance):
let distanceString = stringForDistance(strings: strings, distance: Double(distance)) let distanceString = stringForDistance(strings: strings, distance: Double(distance))
if toId == accountPeerId { if toId == accountPeerId {
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(authorName, distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
} else { } else {
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(authorName, distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (2, toId)])) attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (2, toId)]))
} }
case .unknown: case .unknown:
attributedString = nil attributedString = nil

View File

@ -7858,6 +7858,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let selfPeerId: PeerId let selfPeerId: PeerId
if let peer = peer as? TelegramChannel, case .broadcast = peer.info { if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
selfPeerId = peer.id selfPeerId = peer.id
} else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.hasPermission(.canBeAnonymous) {
selfPeerId = peer.id
} else { } else {
selfPeerId = self.context.account.peerId selfPeerId = self.context.account.peerId
} }
@ -8306,6 +8308,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controller.dismissWithCommitAction() controller.dismissWithCommitAction()
} }
}) })
self.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()
}
return true
})
let value: String? let value: String?
let emoji = dice.emoji.strippedEmoji let emoji = dice.emoji.strippedEmoji

View File

@ -853,7 +853,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
views = attribute.count views = attribute.count
} }
} }
if views >= 100 {
if let cachedData = cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats), views >= 100 {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextViewStats, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextViewStats, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor)
}, action: { c, _ in }, action: { c, _ in

View File

@ -445,7 +445,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
func updateVisibility() { private func updateVisibility() {
guard let item = self.item else { guard let item = self.item else {
return return
} }
@ -1104,7 +1104,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
switch recognizer.state { switch recognizer.state {
case .ended: case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
@ -1268,7 +1268,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
return nil return nil
} }
@objc func shareButtonPressed() { @objc private func shareButtonPressed() {
if let item = self.item { if let item = self.item {
if case .pinnedMessages = item.associatedData.subject { if case .pinnedMessages = item.associatedData.subject {
item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id) item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id)
@ -1299,7 +1299,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) { @objc private func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
switch recognizer.state { switch recognizer.state {
case .began: case .began:
self.currentSwipeToReplyTranslation = 0.0 self.currentSwipeToReplyTranslation = 0.0

View File

@ -829,10 +829,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
wasCheck = true wasCheck = true
} }
if adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) { if isAudio && !isVoice {
state = .check(appearance: nil) state = .play
} else { } else {
state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil) if adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) {
state = .check(appearance: nil)
} else {
state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil)
}
} }
case .Local: case .Local:
if isAudio { if isAudio {
@ -897,7 +901,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
} }
} }
image = playerAlbumArt(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false) image = playerAlbumArt(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated)
} }
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor) let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor)
self.statusNode = statusNode self.statusNode = statusNode
@ -975,11 +979,10 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let cutoutFrame = streamingCacheStatusFrame.insetBy(dx: -(1.0 + UIScreenPixel), dy: -(1.0 + UIScreenPixel)).offsetBy(dx: progressFrame.minX - 6.0, dy: progressFrame.minY) let cutoutFrame = streamingCacheStatusFrame.insetBy(dx: -(1.0 + UIScreenPixel), dy: -(1.0 + UIScreenPixel)).offsetBy(dx: progressFrame.minX - 6.0, dy: progressFrame.minY)
if streamingState == .none && self.selectionNode == nil { if streamingState == .none && self.selectionNode == nil {
self.statusNode?.cutout = nil self.statusNode?.setCutout(nil, animated: animated)
} else if let statusNode = self.statusNode, (self.iconNode?.isHidden ?? true) { } else if let statusNode = self.statusNode, (self.iconNode?.isHidden ?? true) {
self.statusNode?.cutout = cutoutFrame statusNode.setCutout(cutoutFrame, animated: true)
} }
if let (expandedString, compactString, font) = downloadingStrings { if let (expandedString, compactString, font) = downloadingStrings {

View File

@ -20,6 +20,42 @@ public enum PrefetchMediaItem {
case animatedEmojiSticker(TelegramMediaFile) case animatedEmojiSticker(TelegramMediaFile)
} }
private struct AnimatedEmojiSoundsConfiguration {
static var defaultValue: AnimatedEmojiSoundsConfiguration {
return AnimatedEmojiSoundsConfiguration(sounds: [:])
}
public let sounds: [String: TelegramMediaFile]
fileprivate init(sounds: [String: TelegramMediaFile]) {
self.sounds = sounds
}
static func with(appConfiguration: AppConfiguration) -> AnimatedEmojiSoundsConfiguration {
if let data = appConfiguration.data, let values = data["emojies_sounds"] as? [String: Any] {
var sounds: [String: TelegramMediaFile] = [:]
for (key, value) in values {
if let dict = value as? [String: String], var fileReferenceString = dict["file_reference_base64"] {
fileReferenceString = fileReferenceString.replacingOccurrences(of: "-", with: "+")
fileReferenceString = fileReferenceString.replacingOccurrences(of: "_", with: "/")
while fileReferenceString.count % 4 != 0 {
fileReferenceString.append("=")
}
if let idString = dict["id"], let id = Int64(idString), let accessHashString = dict["access_hash"], let accessHash = Int64(accessHashString), let fileReference = Data(base64Encoded: fileReferenceString) {
let resource = CloudDocumentMediaResource(datacenterId: 0, fileId: id, accessHash: accessHash, size: nil, fileReference: fileReference, fileName: nil)
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: [])
sounds[key] = file
}
}
}
return AnimatedEmojiSoundsConfiguration(sounds: sounds)
} else {
return .defaultValue
}
}
}
private final class PrefetchManagerImpl { private final class PrefetchManagerImpl {
private let queue: Queue private let queue: Queue
private let account: Account private let account: Account
@ -45,34 +81,47 @@ private final class PrefetchManagerImpl {
} }
|> distinctUntilChanged |> distinctUntilChanged
let orderedPreloadMedia = account.viewTracker.orderedPreloadMedia let appConfiguration = account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|> mapToSignal { orderedPreloadMedia in |> take(1)
return loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false) |> map { view in
|> map { result -> [PrefetchMediaItem] in return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) } }
switch result {
case let .result(_, items, _): let orderedPreloadMedia = combineLatest(account.viewTracker.orderedPreloadMedia, loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false), appConfiguration)
var animatedEmojiStickers: [String: StickerPackItem] = [:] |> map { orderedPreloadMedia, stickerPack, appConfiguration -> [PrefetchMediaItem] in
for case let item as StickerPackItem in items { let emojiSounds = AnimatedEmojiSoundsConfiguration.with(appConfiguration: appConfiguration)
if let emoji = item.getStringRepresentationsOfIndexKeys().first { let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) }
animatedEmojiStickers[emoji.basicEmoji.0] = item var stickerItems: [PrefetchMediaItem] = []
var prefetchItems: [PrefetchMediaItem] = []
switch stickerPack {
case let .result(_, items, _):
var animatedEmojiStickers: [String: StickerPackItem] = [:]
for case let item as StickerPackItem in items {
if let emoji = item.getStringRepresentationsOfIndexKeys().first {
animatedEmojiStickers[emoji.basicEmoji.0] = item
}
}
let popularEmoji = ["\u{2764}", "👍", "😳", "😒", "🥳"]
for emoji in popularEmoji {
if let sticker = animatedEmojiStickers[emoji] {
if let _ = account.postbox.mediaBox.completedResourcePath(sticker.file.resource) {
} else {
stickerItems.append(.animatedEmojiSticker(sticker.file))
} }
} }
var stickerItems: [PrefetchMediaItem] = [] }
let popularEmoji = ["\u{2764}", "👍", "😳", "😒", "🥳"] return stickerItems
for emoji in popularEmoji { default:
if let sticker = animatedEmojiStickers[emoji] { break
if let _ = account.postbox.mediaBox.completedResourcePath(sticker.file.resource) {
} else {
stickerItems.append(.animatedEmojiSticker(sticker.file))
}
}
}
return stickerItems + chatHistoryMediaItems
default:
return chatHistoryMediaItems
}
} }
prefetchItems.append(contentsOf: chatHistoryMediaItems)
prefetchItems.append(contentsOf: stickerItems)
return prefetchItems
} }
self.listDisposable = (combineLatest(orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType) self.listDisposable = (combineLatest(orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType)