mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Location view fixes
This commit is contained in:
parent
c8734c7728
commit
94ece899d8
@ -105,7 +105,7 @@ final class LocationActionListItem: ListViewItem {
|
||||
async {
|
||||
let node = LocationActionListItemNode()
|
||||
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.insets = nodeLayout.insets
|
||||
|
||||
@ -118,7 +118,7 @@ final class LocationActionListItem: ListViewItem {
|
||||
if let nodeValue = node() as? LocationActionListItemNode {
|
||||
let layout = nodeValue.asyncLayout()
|
||||
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 {
|
||||
completion(nodeLayout, { info in
|
||||
apply().1(info)
|
||||
|
@ -96,7 +96,9 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
|
||||
}
|
||||
|
||||
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())"
|
||||
} else if let venueId = self.location?.venue?.id {
|
||||
return venueId
|
||||
@ -257,8 +259,8 @@ class LocationPinAnnotationView: MKAnnotationView {
|
||||
self.dotNode.isHidden = false
|
||||
self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground")
|
||||
|
||||
if let author = message.author, let peer = message.peers[author.id] {
|
||||
self.setPeer(context: annotation.context, theme: annotation.theme, peer: peer)
|
||||
if let author = message.author {
|
||||
self.setPeer(context: annotation.context, theme: annotation.theme, peer: author)
|
||||
} else if let selfPeer = annotation.selfPeer {
|
||||
self.setPeer(context: annotation.context, theme: annotation.theme, peer: selfPeer)
|
||||
}
|
||||
|
@ -338,8 +338,14 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
|
||||
private func updateDoneButtonTitle() {
|
||||
if let pickerView = self.pickerView {
|
||||
let largeValue = unitValues[pickerView.selectedRow(inComponent: 0)]
|
||||
let smallValue = smallUnitValues[pickerView.selectedRow(inComponent: 1)]
|
||||
let selectedLargeRow = pickerView.selectedRow(inComponent: 0)
|
||||
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
|
||||
if !self.usesMetricSystem() {
|
||||
|
@ -6,7 +6,10 @@ import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import TelegramStringFormatting
|
||||
import ItemListUI
|
||||
import LocationResources
|
||||
import AppBundle
|
||||
@ -15,15 +18,19 @@ import LiveLocationTimerNode
|
||||
|
||||
final class LocationLiveListItem: ListViewItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let account: Account
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let context: AccountContext
|
||||
let message: Message
|
||||
let distance: Double?
|
||||
let action: () -> 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.account = account
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.context = context
|
||||
self.message = message
|
||||
self.distance = distance
|
||||
self.action = action
|
||||
@ -92,6 +99,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarFont)
|
||||
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||
|
||||
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 rightInset: CGFloat = params.rightInset
|
||||
let verticalInset: CGFloat = 8.0
|
||||
let iconSize: CGFloat = 40.0
|
||||
|
||||
let titleFont = Font.medium(item.presentationData.fontSize.itemListBaseFontSize)
|
||||
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)
|
||||
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()))
|
||||
var title: String = ""
|
||||
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)
|
||||
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()))
|
||||
var updateTimestamp = item.message.timestamp
|
||||
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 bottomInset: CGFloat = hasSeparator ? 0.0 : 4.0
|
||||
let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height + bottomInset)
|
||||
let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height)
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets())
|
||||
|
||||
return (nodeLayout, { [weak self] in
|
||||
@ -168,7 +195,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
|
||||
updatedTheme = item.presentationData.theme
|
||||
}
|
||||
|
||||
return (nil, { _ in
|
||||
return (self?.avatarNode.ready, { _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.layoutParams = params
|
||||
@ -199,32 +226,41 @@ final class LocationLiveListItemNode: ListViewItemNode {
|
||||
|
||||
let separatorHeight = UIScreenPixel
|
||||
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))
|
||||
// strongSelf.iconNode.frame = iconNodeFrame
|
||||
// strongSelf.venueIconNode.frame = iconNodeFrame
|
||||
if let peer = item.message.author {
|
||||
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false)
|
||||
}
|
||||
|
||||
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.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.isHidden = !hasSeparator
|
||||
|
||||
// if let (beginTimestamp, timeout) = item.beginTimeAndTimeout {
|
||||
// let timerNode: ChatMessageLiveLocationTimerNode
|
||||
// if let current = strongSelf.timerNode {
|
||||
// timerNode = current
|
||||
// } else {
|
||||
// timerNode = ChatMessageLiveLocationTimerNode()
|
||||
// strongSelf.addSubnode(timerNode)
|
||||
// strongSelf.timerNode = timerNode
|
||||
// }
|
||||
// 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: beginTimestamp, timeout: timeout, strings: item.presentationData.strings)
|
||||
// timerNode.frame = CGRect(origin: CGPoint(x: contentSize.width - 16.0 - timerSize.width, y: floorToScreenPixels((contentSize.height - timerSize.height) / 2.0) - 2.0), size: timerSize)
|
||||
// } else if let timerNode = strongSelf.timerNode {
|
||||
// strongSelf.timerNode = nil
|
||||
// timerNode.removeFromSupernode()
|
||||
// }
|
||||
var liveBroadcastingTimeout: Int32 = 0
|
||||
if let location = getLocation(from: item.message), let timeout = location.liveBroadcastingTimeout {
|
||||
liveBroadcastingTimeout = timeout
|
||||
}
|
||||
|
||||
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if currentTimestamp < item.message.timestamp + liveBroadcastingTimeout {
|
||||
let timerNode: ChatMessageLiveLocationTimerNode
|
||||
if let current = strongSelf.timerNode {
|
||||
timerNode = current
|
||||
} else {
|
||||
timerNode = ChatMessageLiveLocationTimerNode()
|
||||
strongSelf.addSubnode(timerNode)
|
||||
strongSelf.timerNode = timerNode
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -758,7 +758,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
||||
}
|
||||
|
||||
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)
|
||||
mapView.setVisibleMapRect(fittedZoomRect, animated: animated)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import ItemListUI
|
||||
import ItemListVenueItem
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
import TelegramUIPreferences
|
||||
import TelegramNotices
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
@ -48,7 +49,7 @@ private enum LocationViewEntryId: Hashable {
|
||||
private enum LocationViewEntry: Comparable, Identifiable {
|
||||
case info(PresentationTheme, TelegramMediaMap, String?, 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 {
|
||||
switch self {
|
||||
@ -56,7 +57,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
||||
return .info
|
||||
case .toggleLiveLocation:
|
||||
return .toggleLiveLocation
|
||||
case let .liveLocation(_, message, _, _):
|
||||
case let .liveLocation(_, _, _, message, _, _):
|
||||
return .liveLocation(message.stableId)
|
||||
}
|
||||
}
|
||||
@ -75,8 +76,8 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .liveLocation(lhsTheme, lhsMessage, lhsDistance, lhsIndex):
|
||||
if case let .liveLocation(rhsTheme, rhsMessage, rhsDistance, rhsIndex) = rhs, lhsTheme === rhsTheme, areMessagesEqual(lhsMessage, rhsMessage), lhsDistance == rhsDistance, lhsIndex == rhsIndex {
|
||||
case let .liveLocation(lhsTheme, lhsDateTimeFormat, lhsNameDisplayOrder, lhsMessage, lhsDistance, lhsIndex):
|
||||
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
|
||||
} else {
|
||||
return false
|
||||
@ -100,17 +101,17 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
||||
case .liveLocation:
|
||||
return true
|
||||
}
|
||||
case let .liveLocation(_, _, _, lhsIndex):
|
||||
case let .liveLocation(_, _, _, _, _, lhsIndex):
|
||||
switch rhs {
|
||||
case .info, .toggleLiveLocation:
|
||||
return false
|
||||
case let .liveLocation(_, _, _, rhsIndex):
|
||||
case let .liveLocation(_, _, _, _, _, rhsIndex):
|
||||
return lhsIndex < rhsIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item(account: Account, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem {
|
||||
func item(context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem {
|
||||
switch self {
|
||||
case let .info(_, location, address, distance, time):
|
||||
let addressString: String?
|
||||
@ -126,7 +127,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
||||
distanceString = nil
|
||||
}
|
||||
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)
|
||||
}, getDirections: {
|
||||
interaction?.requestDirections()
|
||||
@ -138,7 +139,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
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 {
|
||||
interaction?.stopLiveLocation()
|
||||
} else if let coordinate = coordinate {
|
||||
@ -147,24 +148,18 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
||||
}, highlighted: { highlight in
|
||||
interaction?.updateSendActionHighlight(highlight)
|
||||
})
|
||||
case let .liveLocation(_, message, distance, _):
|
||||
let distanceString: String?
|
||||
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: {})
|
||||
case let .liveLocation(_, dateTimeFormat, nameDisplayOrder, message, distance, _):
|
||||
return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: context, 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 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 updates = updateIndices.map { ListViewUpdateItem(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(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
|
||||
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 distance = userLocation.flatMap { subjectLocation.distance(from: $0) }
|
||||
// entries.append(.liveLocation(presentationData.theme, message, distance, index))
|
||||
|
||||
if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer {
|
||||
userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
@ -414,11 +409,14 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
if subject.id.peerId.namespace != Namespaces.Peer.CloudUser, proximityNotification == nil {
|
||||
proximityNotification = false
|
||||
}
|
||||
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info {
|
||||
proximityNotification = nil
|
||||
}
|
||||
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
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.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
|
||||
|
@ -2465,8 +2465,8 @@ public func chatWebFileImage(account: Account, file: TelegramMediaWebFile) -> Si
|
||||
|
||||
private let precomposedSmallAlbumArt = Atomic<UIImage?>(value: nil)
|
||||
|
||||
private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource) -> Signal<Data?, NoError> {
|
||||
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail)
|
||||
private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource, attemptSynchronously: Bool = false) -> Signal<Data?, NoError> {
|
||||
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail, attemptSynchronously: attemptSynchronously)
|
||||
|
||||
let signal = thumbnailResource |> take(1) |> mapToSignal { maybeData -> Signal<Data?, NoError> in
|
||||
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)
|
||||
if let fileReference = fileReference {
|
||||
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 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: []))
|
||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
|
||||
@ -2643,7 +2643,7 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
|
||||
}
|
||||
} else if let albumArt = albumArt {
|
||||
if thumbnail {
|
||||
immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource)
|
||||
immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource, attemptSynchronously: attemptSynchronously)
|
||||
|> map { thumbnailData in
|
||||
return Tuple(thumbnailData, nil, false)
|
||||
}
|
||||
|
@ -591,8 +591,8 @@ private final class SemanticStatusNodeAppearanceContext {
|
||||
self.cutout = cutout
|
||||
}
|
||||
|
||||
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState {
|
||||
return SemanticStatusNodeAppearanceDrawingState(transitionFraction: transitionFraction, background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: self.cutout)
|
||||
func drawingState(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState {
|
||||
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 {
|
||||
@ -617,7 +617,8 @@ private final class SemanticStatusNodeAppearanceContext {
|
||||
}
|
||||
|
||||
private final class SemanticStatusNodeAppearanceDrawingState {
|
||||
let transitionFraction: CGFloat
|
||||
let backgroundTransitionFraction: CGFloat
|
||||
let foregroundTransitionFraction: CGFloat
|
||||
let background: UIColor
|
||||
let foreground: UIColor
|
||||
let backgroundImage: UIImage?
|
||||
@ -632,8 +633,9 @@ private final class SemanticStatusNodeAppearanceDrawingState {
|
||||
}
|
||||
}
|
||||
|
||||
init(transitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
|
||||
self.transitionFraction = transitionFraction
|
||||
init(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
|
||||
self.backgroundTransitionFraction = backgroundTransitionFraction
|
||||
self.foregroundTransitionFraction = foregroundTransitionFraction
|
||||
self.background = background
|
||||
self.foreground = foreground
|
||||
self.backgroundImage = backgroundImage
|
||||
@ -644,11 +646,12 @@ private final class SemanticStatusNodeAppearanceDrawingState {
|
||||
func drawBackground(context: CGContext, size: CGSize) {
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
if let backgroundImage = self.backgroundImage?.cgImage {
|
||||
context.saveGState()
|
||||
context.translateBy(x: 0.0, y: bounds.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.setAlpha(self.transitionFraction)
|
||||
context.setAlpha(self.backgroundTransitionFraction)
|
||||
context.draw(backgroundImage, in: bounds)
|
||||
context.restoreGState()
|
||||
} else {
|
||||
@ -659,7 +662,7 @@ private final class SemanticStatusNodeAppearanceDrawingState {
|
||||
|
||||
func drawForeground(context: CGContext, size: CGSize) {
|
||||
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)
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
@ -740,13 +743,23 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
return self.appearanceContext.cutout
|
||||
}
|
||||
set {
|
||||
if self.appearanceContext.cutout != newValue {
|
||||
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: self.appearanceContext, completion: {})
|
||||
self.appearanceContext = self.appearanceContext.withUpdatedCutout(newValue)
|
||||
|
||||
self.updateAnimations()
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
self.setCutout(newValue, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
public func setCutout(_ cutout: CGRect?, animated: Bool) {
|
||||
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
|
||||
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.updateAnimations()
|
||||
}
|
||||
@ -858,7 +871,8 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
||||
var transitionState: SemanticStatusNodeTransitionDrawingState?
|
||||
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 {
|
||||
let timestamp = CACurrentMediaTime()
|
||||
@ -868,13 +882,20 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
if let _ = transitionContext.previousStateContext {
|
||||
transitionFraction = t
|
||||
}
|
||||
if let _ = transitionContext.previousAppearanceContext {
|
||||
appearanceTransitionFraction = t
|
||||
var foregroundTransitionFraction: CGFloat = 1.0
|
||||
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) {
|
||||
|
@ -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)]))
|
||||
case .phoneNumberRequest:
|
||||
attributedString = nil
|
||||
case let .geoProximityReached(_, toId, distance):
|
||||
case let .geoProximityReached(fromId, toId, distance):
|
||||
let distanceString = stringForDistance(strings: strings, distance: Double(distance))
|
||||
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 {
|
||||
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:
|
||||
attributedString = nil
|
||||
|
@ -7858,6 +7858,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let selfPeerId: PeerId
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
selfPeerId = peer.id
|
||||
} else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.hasPermission(.canBeAnonymous) {
|
||||
selfPeerId = peer.id
|
||||
} else {
|
||||
selfPeerId = self.context.account.peerId
|
||||
}
|
||||
@ -8306,6 +8308,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
})
|
||||
self.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
let value: String?
|
||||
let emoji = dice.emoji.strippedEmoji
|
||||
|
@ -853,7 +853,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
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
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { c, _ in
|
||||
|
@ -445,7 +445,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
func updateVisibility() {
|
||||
private func updateVisibility() {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
@ -1104,7 +1104,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
@objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
@ -1268,7 +1268,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc func shareButtonPressed() {
|
||||
@objc private func shareButtonPressed() {
|
||||
if let item = self.item {
|
||||
if case .pinnedMessages = item.associatedData.subject {
|
||||
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 {
|
||||
case .began:
|
||||
self.currentSwipeToReplyTranslation = 0.0
|
||||
|
@ -829,10 +829,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
wasCheck = true
|
||||
}
|
||||
|
||||
if adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) {
|
||||
state = .check(appearance: nil)
|
||||
if isAudio && !isVoice {
|
||||
state = .play
|
||||
} 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:
|
||||
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)
|
||||
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)
|
||||
|
||||
|
||||
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) {
|
||||
self.statusNode?.cutout = cutoutFrame
|
||||
statusNode.setCutout(cutoutFrame, animated: true)
|
||||
}
|
||||
|
||||
if let (expandedString, compactString, font) = downloadingStrings {
|
||||
|
@ -20,6 +20,42 @@ public enum PrefetchMediaItem {
|
||||
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 let queue: Queue
|
||||
private let account: Account
|
||||
@ -45,34 +81,47 @@ private final class PrefetchManagerImpl {
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let orderedPreloadMedia = account.viewTracker.orderedPreloadMedia
|
||||
|> mapToSignal { orderedPreloadMedia in
|
||||
return loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false)
|
||||
|> map { result -> [PrefetchMediaItem] in
|
||||
let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) }
|
||||
switch result {
|
||||
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 appConfiguration = account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> take(1)
|
||||
|> map { view in
|
||||
return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
|
||||
}
|
||||
|
||||
let orderedPreloadMedia = combineLatest(account.viewTracker.orderedPreloadMedia, loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false), appConfiguration)
|
||||
|> map { orderedPreloadMedia, stickerPack, appConfiguration -> [PrefetchMediaItem] in
|
||||
let emojiSounds = AnimatedEmojiSoundsConfiguration.with(appConfiguration: appConfiguration)
|
||||
let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) }
|
||||
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}", "👍", "😳", "😒", "🥳"]
|
||||
for emoji in popularEmoji {
|
||||
if let sticker = animatedEmojiStickers[emoji] {
|
||||
if let _ = account.postbox.mediaBox.completedResourcePath(sticker.file.resource) {
|
||||
} else {
|
||||
stickerItems.append(.animatedEmojiSticker(sticker.file))
|
||||
}
|
||||
}
|
||||
}
|
||||
return stickerItems + chatHistoryMediaItems
|
||||
default:
|
||||
return chatHistoryMediaItems
|
||||
}
|
||||
}
|
||||
return stickerItems
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
prefetchItems.append(contentsOf: chatHistoryMediaItems)
|
||||
prefetchItems.append(contentsOf: stickerItems)
|
||||
|
||||
return prefetchItems
|
||||
}
|
||||
|
||||
self.listDisposable = (combineLatest(orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType)
|
||||
|
Loading…
x
Reference in New Issue
Block a user