diff --git a/submodules/LocationUI/Sources/LocationActionListItem.swift b/submodules/LocationUI/Sources/LocationActionListItem.swift index 8f07e26896..2f7f37a838 100644 --- a/submodules/LocationUI/Sources/LocationActionListItem.swift +++ b/submodules/LocationUI/Sources/LocationActionListItem.swift @@ -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) diff --git a/submodules/LocationUI/Sources/LocationAnnotation.swift b/submodules/LocationUI/Sources/LocationAnnotation.swift index d9c23c0de8..0a66824904 100644 --- a/submodules/LocationUI/Sources/LocationAnnotation.swift +++ b/submodules/LocationUI/Sources/LocationAnnotation.swift @@ -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) } diff --git a/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift b/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift index 19fa40042d..cfd204770a 100644 --- a/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift +++ b/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift @@ -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() { diff --git a/submodules/LocationUI/Sources/LocationLiveListItem.swift b/submodules/LocationUI/Sources/LocationLiveListItem.swift index 9c0b27d8a6..90df67ffb0 100644 --- a/submodules/LocationUI/Sources/LocationLiveListItem.swift +++ b/submodules/LocationUI/Sources/LocationLiveListItem.swift @@ -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() + } } }) }) diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift index 159fd014ce..b4ad0df4b7 100644 --- a/submodules/LocationUI/Sources/LocationMapNode.swift +++ b/submodules/LocationUI/Sources/LocationMapNode.swift @@ -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) } diff --git a/submodules/LocationUI/Sources/LocationViewControllerNode.swift b/submodules/LocationUI/Sources/LocationViewControllerNode.swift index a0f7ee57b1..50b495e55e 100644 --- a/submodules/LocationUI/Sources/LocationViewControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationViewControllerNode.swift @@ -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) diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index a9a1d681d9..35ea342569 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2465,8 +2465,8 @@ public func chatWebFileImage(account: Account, file: TelegramMediaWebFile) -> Si private let precomposedSmallAlbumArt = Atomic(value: nil) -private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource) -> Signal { - let thumbnailResource = postbox.mediaBox.resourceData(thumbnail) +private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource, attemptSynchronously: Bool = false) -> Signal { + let thumbnailResource = postbox.mediaBox.resourceData(thumbnail, attemptSynchronously: attemptSynchronously) let signal = thumbnailResource |> take(1) |> mapToSignal { maybeData -> Signal 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 = .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 { 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) } diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index fae7e30d7c..b88219a336 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -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) { diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index ecac490d78..d46e2278ca 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9caf1c0821..ed6075ae4b 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 4606d3d9fc..3b60c2fe21 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 686d95a78b..182fcddcea 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 1fbd931715..51f7610a7f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/PrefetchManager.swift b/submodules/TelegramUI/Sources/PrefetchManager.swift index 314a814da1..5b6fe6204f 100644 --- a/submodules/TelegramUI/Sources/PrefetchManager.swift +++ b/submodules/TelegramUI/Sources/PrefetchManager.swift @@ -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)