mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
047a0085c7
commit
8ca5750cb9
@ -117,6 +117,10 @@
|
||||
"PUSH_MESSAGES_1" = "%1$@|sent you a message";
|
||||
"PUSH_MESSAGES_any" = "%1$@|sent you %2$d messages";
|
||||
"PUSH_ALBUM" = "%1$@|sent you an album";
|
||||
"PUSH_MESSAGE_DOCS" = "%1$@|sent you %2$d files";
|
||||
"PUSH_MESSAGE_DOCS_1" = "%1$@|sent you a file";
|
||||
"PUSH_MESSAGE_DOCS_any" = "%1$@|sent you %2$d files";
|
||||
|
||||
|
||||
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@";
|
||||
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message";
|
||||
@ -149,6 +153,9 @@
|
||||
"PUSH_CHANNEL_MESSAGES_1" = "%1$@|posted a message";
|
||||
"PUSH_CHANNEL_MESSAGES_any" = "%1$@|posted %2$d messages";
|
||||
"PUSH_CHANNEL_ALBUM" = "%1$@|posted an album";
|
||||
"PUSH_CHANNEL_MESSAGE_DOCS" = "%1$@|posted %2$d files";
|
||||
"PUSH_CHANNEL_MESSAGE_DOCS_1" = "%1$@|posted a file";
|
||||
"PUSH_CHANNEL_MESSAGE_DOCS_any" = "%1$@|posted %2$d files";
|
||||
|
||||
"PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@:%3$@";
|
||||
"PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message to the group";
|
||||
@ -191,6 +198,9 @@
|
||||
"PUSH_CHAT_MESSAGES_1" = "%2$@|%1$@ sent a message";
|
||||
"PUSH_CHAT_MESSAGES_any" = "%2$@|%1$@ sent %3$d messages";
|
||||
"PUSH_CHAT_ALBUM" = "%2$@|%1$@ sent an album";
|
||||
"PUSH_CHAT_MESSAGE_DOCS" = "%2$@|%1$@ sent %3$d files";
|
||||
"PUSH_CHAT_MESSAGE_DOCS_1" = "%2$@|%1$@ sent a file";
|
||||
"PUSH_CHAT_MESSAGE_DOCS_any" = "%2$@|%1$@ sent %3$d files";
|
||||
|
||||
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" ";
|
||||
"PUSH_PINNED_NOTEXT" = "%1$@|pinned a message";
|
||||
@ -5820,7 +5830,7 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"Conversation.ContextViewStats" = "View Statistics";
|
||||
|
||||
"ChatList.MessageMusic_1" = "1 Music File";
|
||||
"ChatList.MessageMusic_1" = "%@ Music File";
|
||||
"ChatList.MessageMusic_any" = "%@ Music Files";
|
||||
|
||||
"Conversation.PinOlderMessageAlertTitle" = "Pin Message";
|
||||
@ -5838,7 +5848,8 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"Conversation.Dice.u1F3B0" = "Send a slot machine emoji to try your luck.";
|
||||
|
||||
"Notification.ProximityReached" = "%1$@ is now within %2$@ from you";
|
||||
"Notification.ProximityReached" = "%1$@ is now within %2$@ from %3$@";
|
||||
"Notification.ProximityReachedYou" = "%1$@ is now within %2$@ from you";
|
||||
|
||||
"Location.ProximityNotification.Title" = "Notification";
|
||||
"Location.ProximityNotification.Notify" = "Notify me within %@";
|
||||
@ -5852,3 +5863,6 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"Location.ProximityTip" = "Alert when %@ is close";
|
||||
"Location.ProximityGroupTip" = "Alert when any group member is close";
|
||||
|
||||
"ChatList.MessageFiles_1" = "%@ File";
|
||||
"ChatList.MessageFiles_any" = "%@ Files";
|
||||
|
@ -743,6 +743,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
public var started: () -> Void = {}
|
||||
private var reportedStarted = false
|
||||
|
||||
public var completed: (Bool) -> Void = { _ in }
|
||||
|
||||
private let timer = Atomic<SwiftSignalKit.Timer?>(value: nil)
|
||||
private let frameSource = Atomic<QueueLocalObject<AnimatedStickerFrameSourceWrapper>?>(value: nil)
|
||||
|
||||
@ -760,6 +762,14 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
return self.playbackStatus.get()
|
||||
}
|
||||
|
||||
public var autoplay = true {
|
||||
didSet {
|
||||
if self.autoplay != oldValue {
|
||||
self.updateIsPlaying()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var visibility = false {
|
||||
didSet {
|
||||
if self.visibility != oldValue {
|
||||
@ -825,6 +835,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
strongSelf.directData = (directData, path, width, height, cachePathPrefix, source.fitzModifier)
|
||||
}
|
||||
if case let .still(position) = playbackMode {
|
||||
strongSelf.play(firstFrame: true)
|
||||
strongSelf.seekTo(position)
|
||||
} else if strongSelf.isPlaying {
|
||||
strongSelf.play()
|
||||
@ -877,7 +888,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
if self.isPlaying != isPlaying {
|
||||
self.isPlaying = isPlaying
|
||||
if isPlaying {
|
||||
self.play()
|
||||
self.play(firstFrame: !self.autoplay)
|
||||
} else{
|
||||
self.pause()
|
||||
}
|
||||
@ -950,11 +961,17 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
}
|
||||
})
|
||||
|
||||
if case .once = strongSelf.playbackMode, frame.isLastFrame {
|
||||
strongSelf.stop()
|
||||
strongSelf.isPlaying = false
|
||||
if frame.isLastFrame {
|
||||
var stopped = false
|
||||
if case .once = strongSelf.playbackMode {
|
||||
strongSelf.stop()
|
||||
strongSelf.isPlaying = false
|
||||
stopped = true
|
||||
}
|
||||
|
||||
strongSelf.completed(stopped)
|
||||
}
|
||||
|
||||
|
||||
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
|
||||
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
|
||||
}
|
||||
@ -1021,11 +1038,17 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
}
|
||||
})
|
||||
|
||||
if case .once = strongSelf.playbackMode, frame.isLastFrame {
|
||||
strongSelf.stop()
|
||||
strongSelf.isPlaying = false
|
||||
if frame.isLastFrame {
|
||||
var stopped = false
|
||||
if case .once = strongSelf.playbackMode {
|
||||
strongSelf.stop()
|
||||
strongSelf.isPlaying = false
|
||||
stopped = true
|
||||
}
|
||||
|
||||
strongSelf.completed(stopped)
|
||||
}
|
||||
|
||||
|
||||
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
|
||||
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ private enum MessageGroupType {
|
||||
case photos
|
||||
case videos
|
||||
case music
|
||||
case files
|
||||
case generic
|
||||
}
|
||||
|
||||
@ -25,6 +26,7 @@ private func singleMessageType(message: Message) -> MessageGroupType {
|
||||
if file.isVideo && !file.isInstantVideo {
|
||||
return .videos
|
||||
}
|
||||
return .files
|
||||
}
|
||||
}
|
||||
return .generic
|
||||
@ -91,6 +93,13 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
||||
messageText = strings.ChatList_MessageMusic(Int32(messages.count))
|
||||
textIsReady = true
|
||||
}
|
||||
case .files:
|
||||
if !messageText.isEmpty {
|
||||
textIsReady = true
|
||||
} else {
|
||||
messageText = strings.ChatList_MessageFiles(Int32(messages.count))
|
||||
textIsReady = true
|
||||
}
|
||||
case .generic:
|
||||
break
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import PresentationDataUtils
|
||||
enum ChatMediaGalleryThumbnail: Equatable {
|
||||
case image(ImageMediaReference)
|
||||
case video(FileMediaReference)
|
||||
case file(FileMediaReference)
|
||||
|
||||
static func ==(lhs: ChatMediaGalleryThumbnail, rhs: ChatMediaGalleryThumbnail) -> Bool {
|
||||
switch lhs {
|
||||
@ -33,6 +34,12 @@ enum ChatMediaGalleryThumbnail: Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .file(lhsFile):
|
||||
if case let .file(rhsFile) = rhs, lhsFile.media.isEqual(to: rhsFile.media) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,8 +52,12 @@ final class ChatMediaGalleryThumbnailItem: GalleryThumbnailItem {
|
||||
self.account = account
|
||||
if let imageReference = mediaReference.concrete(TelegramMediaImage.self) {
|
||||
self.thumbnail = .image(imageReference)
|
||||
} else if let fileReference = mediaReference.concrete(TelegramMediaFile.self), fileReference.media.isVideo {
|
||||
self.thumbnail = .video(fileReference)
|
||||
} else if let fileReference = mediaReference.concrete(TelegramMediaFile.self) {
|
||||
if fileReference.media.isVideo {
|
||||
self.thumbnail = .video(fileReference)
|
||||
} else {
|
||||
self.thumbnail = .file(fileReference)
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -74,6 +85,12 @@ final class ChatMediaGalleryThumbnailItem: GalleryThumbnailItem {
|
||||
} else {
|
||||
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
|
||||
}
|
||||
case let .file(fileReference):
|
||||
if let representation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||
return (chatWebpageSnippetFile(account: self.account, fileReference: fileReference, representation: representation), representation.dimensions.cgSize)
|
||||
} else {
|
||||
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,7 +170,7 @@ class ChatImageGalleryItem: GalleryItem {
|
||||
for m in self.message.media {
|
||||
if let m = m as? TelegramMediaImage {
|
||||
mediaReference = .message(message: MessageReference(self.message), media: m)
|
||||
} else if let m = m as? TelegramMediaFile, m.isVideo {
|
||||
} else if let m = m as? TelegramMediaFile {
|
||||
mediaReference = .message(message: MessageReference(self.message), media: m)
|
||||
}
|
||||
}
|
||||
|
@ -720,6 +720,23 @@
|
||||
|
||||
- (void)updateEditorButtonsForItem:(id<TGModernGalleryItem>)item animated:(bool)animated
|
||||
{
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
id<TGModernGalleryEditableItem> galleryEditableItem = (id<TGModernGalleryEditableItem>)item;
|
||||
if ([item conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
|
||||
{
|
||||
id<TGMediaEditableItem> editableMediaItem = [galleryEditableItem editableMediaItem];
|
||||
[_captionDisposable setDisposable:[[galleryEditableItem.editingContext captionSignalForItem:editableMediaItem] startWithNext:^(NSDictionary *captionWithEntities)
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
NSString *caption = captionWithEntities[@"caption"];
|
||||
NSArray *entities = captionWithEntities[@"entities"];
|
||||
[strongSelf->_captionMixin setCaption:caption entities:entities animated:animated];
|
||||
}]];
|
||||
}
|
||||
|
||||
if (_editingContext == nil || _editingContext.inhibitEditing)
|
||||
{
|
||||
[_portraitToolbarView setEditButtonsHidden:true animated:false];
|
||||
@ -757,12 +774,10 @@
|
||||
return;
|
||||
}
|
||||
|
||||
id<TGModernGalleryEditableItem> galleryEditableItem = (id<TGModernGalleryEditableItem>)item;
|
||||
if ([item conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
|
||||
{
|
||||
id<TGMediaEditableItem> editableMediaItem = [galleryEditableItem editableMediaItem];
|
||||
|
||||
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
|
||||
__weak id<TGModernGalleryEditableItem> weakGalleryEditableItem = galleryEditableItem;
|
||||
[_adjustmentsDisposable setDisposable:[[[[galleryEditableItem.editingContext adjustmentsSignalForItem:editableMediaItem] mapToSignal:^SSignal *(id<TGMediaEditAdjustments> adjustments) {
|
||||
__strong id<TGModernGalleryEditableItem> strongGalleryEditableItem = weakGalleryEditableItem;
|
||||
@ -803,17 +818,6 @@
|
||||
|
||||
[strongSelf updateEditorButtonsForAdjustments:adjustments dimensions:originalSize timer:timer];
|
||||
}]];
|
||||
|
||||
[_captionDisposable setDisposable:[[galleryEditableItem.editingContext captionSignalForItem:editableMediaItem] startWithNext:^(NSDictionary *captionWithEntities)
|
||||
{
|
||||
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
NSString *caption = captionWithEntities[@"caption"];
|
||||
NSArray *entities = captionWithEntities[@"entities"];
|
||||
[strongSelf->_captionMixin setCaption:caption entities:entities animated:animated];
|
||||
}]];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -314,9 +314,9 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
pickerView.selectRow(0, inComponent: 0, animated: false)
|
||||
|
||||
if self.usesMetricSystem() {
|
||||
pickerView.selectRow(30, inComponent: 1, animated: false)
|
||||
pickerView.selectRow(50, inComponent: 1, animated: false)
|
||||
} else {
|
||||
pickerView.selectRow(20, inComponent: 1, animated: false)
|
||||
pickerView.selectRow(30, inComponent: 1, animated: false)
|
||||
}
|
||||
self.contentContainerNode.view.addSubview(pickerView)
|
||||
self.pickerView = pickerView
|
||||
|
@ -168,7 +168,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
||||
context.setAlpha(alpha)
|
||||
|
||||
let path = UIBezierPath(rect: CGRect(x: mapRect.origin.x, y: mapRect.origin.y, width: mapRect.size.width, height: mapRect.size.height))
|
||||
let radiusInMap = overlay.radius * MKMapPointsPerMeterAtLatitude(overlay.coordinate.latitude)
|
||||
let radiusInMap = overlay.radius * MKMapPointsPerMeterAtLatitude(overlay.coordinate.latitude) * 2.0
|
||||
let mapSize: MKMapSize = MKMapSize(width: radiusInMap, height: radiusInMap)
|
||||
let regionOrigin = MKMapPoint(overlay.coordinate)
|
||||
var regionRect: MKMapRect = MKMapRect(origin: regionOrigin, size: mapSize)
|
||||
@ -724,15 +724,21 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
||||
}
|
||||
annotations.append(contentsOf: self.annotations)
|
||||
|
||||
var zoomRect: MKMapRect = MKMapRect()
|
||||
var zoomRect: MKMapRect?
|
||||
for annotation in annotations {
|
||||
let pointRegionRect = MKMapRect(region: MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 100, longitudinalMeters: 100))
|
||||
zoomRect = zoomRect.union(pointRegionRect)
|
||||
if let currentZoomRect = zoomRect {
|
||||
zoomRect = currentZoomRect.union(pointRegionRect)
|
||||
} else {
|
||||
zoomRect = pointRegionRect
|
||||
}
|
||||
}
|
||||
|
||||
let insets = UIEdgeInsets()
|
||||
zoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets)
|
||||
mapView.setVisibleMapRect(zoomRect, animated: animated)
|
||||
if let zoomRect = zoomRect {
|
||||
let insets = UIEdgeInsets(top: 0.0, left: 80.0, bottom: 0.0, right: 80.0)
|
||||
let fittedZoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets)
|
||||
mapView.setVisibleMapRect(fittedZoomRect, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize) {
|
||||
|
@ -65,8 +65,6 @@ class LocationViewInteraction {
|
||||
}
|
||||
}
|
||||
|
||||
var CURRENT_DISTANCE: Double? = nil
|
||||
|
||||
public final class LocationViewController: ViewController {
|
||||
private var controllerNode: LocationViewControllerNode {
|
||||
return self.displayNode as! LocationViewControllerNode
|
||||
@ -174,13 +172,9 @@ public final class LocationViewController: ViewController {
|
||||
}
|
||||
|
||||
if reset {
|
||||
strongSelf.controllerNode.updateState { state -> LocationViewState in
|
||||
var state = state
|
||||
state.proximityRadius = nil
|
||||
return state
|
||||
if let messageId = messageId {
|
||||
let _ = cancelProximityNotification(postbox: context.account.postbox, network: context.account.network, messageId: messageId).start()
|
||||
}
|
||||
|
||||
CURRENT_DISTANCE = nil
|
||||
} else {
|
||||
strongSelf.controllerNode.setProximityIndicator(radius: 0)
|
||||
|
||||
@ -196,15 +190,8 @@ public final class LocationViewController: ViewController {
|
||||
|
||||
if let messageId = messageId {
|
||||
completion()
|
||||
strongSelf.controllerNode.updateState { state -> LocationViewState in
|
||||
var state = state
|
||||
state.proximityRadius = Double(distance)
|
||||
return state
|
||||
}
|
||||
|
||||
|
||||
let _ = requestProximityNotification(postbox: context.account.postbox, network: context.account.network, messageId: messageId, distance: distance).start()
|
||||
|
||||
CURRENT_DISTANCE = Double(distance)
|
||||
} else if let coordinate = coordinate {
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_Title, text: strongSelf.presentationData.strings.Location_LiveLocationRequired_Description, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_ShareLocation, action: { [weak self] in
|
||||
completion()
|
||||
@ -250,12 +237,6 @@ public final class LocationViewController: ViewController {
|
||||
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
|
||||
|
||||
if let distance = distance {
|
||||
strongSelf.controllerNode.updateState { state -> LocationViewState in
|
||||
var state = state
|
||||
state.proximityRadius = Double(distance)
|
||||
return state
|
||||
}
|
||||
|
||||
strongSelf.controllerNode.ownLiveLocationStartedAction = { messageId in
|
||||
let _ = requestProximityNotification(postbox: context.account.postbox, network: context.account.network, messageId: messageId, distance: distance).start()
|
||||
}
|
||||
@ -330,12 +311,6 @@ public final class LocationViewController: ViewController {
|
||||
|
||||
self.displayNode = LocationViewControllerNode(context: self.context, presentationData: self.presentationData, subject: self.subject, interaction: interaction, locationManager: self.locationManager)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.controllerNode.updateState { state -> LocationViewState in
|
||||
var state = state
|
||||
state.proximityRadius = CURRENT_DISTANCE
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
private func updateRightBarButton() {
|
||||
|
@ -180,13 +180,11 @@ struct LocationViewState {
|
||||
var mapMode: LocationMapMode
|
||||
var displayingMapModeOptions: Bool
|
||||
var selectedLocation: LocationViewLocation
|
||||
var proximityRadius: Double?
|
||||
|
||||
init() {
|
||||
self.mapMode = .map
|
||||
self.displayingMapModeOptions = false
|
||||
self.selectedLocation = .initial
|
||||
self.proximityRadius = nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,8 +303,10 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
return transaction.getPeer(context.account.peerId)
|
||||
}
|
||||
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), selfPeer, liveLocations, self.headerNode.mapNode.userLocation, userLocation, address, eta)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, selfPeer, liveLocations, userLocation, distance, address, eta in
|
||||
let proximityNotificationState = proximityNotificationStoredState(account: context.account, peerId: subject.id.peerId)
|
||||
|
||||
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), selfPeer, liveLocations, self.headerNode.mapNode.userLocation, userLocation, address, eta, proximityNotificationState)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, selfPeer, liveLocations, userLocation, distance, address, eta, proximityNotificationState in
|
||||
if let strongSelf = self, let location = getLocation(from: subject) {
|
||||
var entries: [LocationViewEntry] = []
|
||||
var annotations: [LocationPinAnnotation] = []
|
||||
@ -388,7 +388,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
}
|
||||
effectiveLiveLocations = sortedLiveLocations
|
||||
}
|
||||
|
||||
|
||||
for message in effectiveLiveLocations {
|
||||
var liveBroadcastingTimeout: Int32 = 0
|
||||
if let location = getLocation(from: message), let timeout = location.liveBroadcastingTimeout {
|
||||
@ -396,7 +396,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
}
|
||||
let remainingTime = max(0, message.timestamp + liveBroadcastingTimeout - currentTime)
|
||||
if message.flags.contains(.Incoming) && remainingTime != 0 {
|
||||
proximityNotification = state.proximityRadius != nil
|
||||
proximityNotification = proximityNotificationState?.distance != nil
|
||||
}
|
||||
|
||||
let subjectLocation = CLLocation(latitude: location.latitude, longitude: location.longitude)
|
||||
@ -412,7 +412,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
}
|
||||
|
||||
if subject.id.peerId.namespace != Namespaces.Peer.CloudUser {
|
||||
proximityNotification = state.proximityRadius != nil
|
||||
proximityNotification = proximityNotificationState?.distance != nil
|
||||
}
|
||||
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
@ -428,7 +428,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
|
||||
let _ = (ApplicationSpecificNotice.getLocationProximityAlertTip(accountManager: context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
||||
if let strongSelf = self, counter < 3 {
|
||||
if let strongSelf = self, counter < 3000 {
|
||||
let _ = ApplicationSpecificNotice.incrementLocationProximityAlertTip(accountManager: context.sharedContext.accountManager).start()
|
||||
strongSelf.displayProximityAlertTooltip()
|
||||
}
|
||||
@ -462,8 +462,11 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
||||
strongSelf.headerNode.mapNode.annotations = annotations
|
||||
}
|
||||
|
||||
strongSelf.headerNode.mapNode.activeProximityRadius = state.proximityRadius
|
||||
|
||||
if let _ = proximityNotification {
|
||||
strongSelf.headerNode.mapNode.activeProximityRadius = (proximityNotificationState?.distance).flatMap { Double($0) }
|
||||
} else {
|
||||
strongSelf.headerNode.mapNode.activeProximityRadius = nil
|
||||
}
|
||||
let rightBarButtonAction: LocationViewRightBarButton
|
||||
if location.liveBroadcastingTimeout != nil {
|
||||
if liveLocations.count > 1 {
|
||||
|
@ -10,6 +10,7 @@ static_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
|
||||
"//submodules/Postbox:Postbox#shared",
|
||||
"//submodules/TelegramCore:TelegramCore#shared",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/rlottie:RLottieBinding",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
|
@ -11,6 +11,7 @@ swift_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/rlottie:RLottieBinding",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
|
@ -3,6 +3,7 @@ import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import RLottieBinding
|
||||
import AppBundle
|
||||
import GZip
|
||||
@ -72,7 +73,7 @@ public enum ManagedAnimationFrameRange: Equatable {
|
||||
|
||||
public enum ManagedAnimationSource: Equatable {
|
||||
case local(String)
|
||||
case resource(MediaBox, MediaResource)
|
||||
case resource(Account, MediaResource)
|
||||
|
||||
var cacheKey: String {
|
||||
switch self {
|
||||
@ -87,8 +88,8 @@ public enum ManagedAnimationSource: Equatable {
|
||||
switch self {
|
||||
case let .local(name):
|
||||
return getAppBundle().path(forResource: name, ofType: "tgs")
|
||||
case let .resource(mediaBox, resource):
|
||||
return mediaBox.completedResourcePath(resource)
|
||||
case let .resource(account, resource):
|
||||
return account.postbox.mediaBox.completedResourcePath(resource)
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,8 +101,8 @@ public enum ManagedAnimationSource: Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .resource(lhsMediaBox, lhsResource):
|
||||
if case let .resource(rhsMediaBox, rhsResource) = rhs, lhsMediaBox === rhsMediaBox, lhsResource.isEqual(to: rhsResource) {
|
||||
case let .resource(lhsAccount, lhsResource):
|
||||
if case let .resource(rhsAccount, rhsResource) = rhs, lhsAccount === rhsAccount, lhsResource.isEqual(to: rhsResource) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -112,9 +113,9 @@ public enum ManagedAnimationSource: Equatable {
|
||||
|
||||
public struct ManagedAnimationItem {
|
||||
public let source: ManagedAnimationSource
|
||||
var frames: ManagedAnimationFrameRange?
|
||||
var duration: Double?
|
||||
var loop: Bool
|
||||
public var frames: ManagedAnimationFrameRange?
|
||||
public var duration: Double?
|
||||
public var loop: Bool
|
||||
var callbacks: [(Int, () -> Void)]
|
||||
|
||||
public init(source: ManagedAnimationSource, frames: ManagedAnimationFrameRange? = nil, duration: Double? = nil, loop: Bool = false, callbacks: [(Int, () -> Void)] = []) {
|
||||
|
@ -2250,6 +2250,7 @@ public final class Postbox {
|
||||
|
||||
fileprivate func removeItemCacheEntry(id: ItemCacheEntryId) {
|
||||
self.itemCacheTable.remove(id: id, metaTable: self.itemCacheMetaTable)
|
||||
self.currentUpdatedCacheEntryKeys.insert(id)
|
||||
}
|
||||
|
||||
fileprivate func replaceGlobalMessageTagsHole(transaction: Transaction, globalTags: GlobalMessageTags, index: MessageIndex, with updatedIndex: MessageIndex?, messages: [StoreMessage]) {
|
||||
|
@ -153,22 +153,22 @@ final class PostboxTransaction {
|
||||
if !self.currentUpdatedPendingPeerNotificationSettings.isEmpty {
|
||||
return false
|
||||
}
|
||||
if replacedAdditionalChatListItems != nil {
|
||||
if self.replacedAdditionalChatListItems != nil {
|
||||
return false
|
||||
}
|
||||
if !updatedNoticeEntryKeys.isEmpty {
|
||||
if !self.updatedNoticeEntryKeys.isEmpty {
|
||||
return false
|
||||
}
|
||||
if !updatedCacheEntryKeys.isEmpty {
|
||||
if !self.updatedCacheEntryKeys.isEmpty {
|
||||
return false
|
||||
}
|
||||
if !updatedFailedMessagePeerIds.isEmpty {
|
||||
if !self.updatedFailedMessagePeerIds.isEmpty {
|
||||
return false
|
||||
}
|
||||
if !updatedFailedMessageIds.isEmpty {
|
||||
if !self.updatedFailedMessageIds.isEmpty {
|
||||
return false
|
||||
}
|
||||
if updatedGlobalNotificationSettings {
|
||||
if self.updatedGlobalNotificationSettings {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
26
submodules/SlotMachineAnimationNode/BUCK
Normal file
26
submodules/SlotMachineAnimationNode/BUCK
Normal file
@ -0,0 +1,26 @@
|
||||
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||
|
||||
static_library(
|
||||
name = "SlotMachineAnimationNode",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
|
||||
"//submodules/Display:Display#shared",
|
||||
"//submodules/Postbox:Postbox#shared",
|
||||
"//submodules/SyncCore:SyncCore#shared",
|
||||
"//submodules/TelegramCore:TelegramCore#shared",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
||||
],
|
||||
)
|
26
submodules/SlotMachineAnimationNode/BUILD
Normal file
26
submodules/SlotMachineAnimationNode/BUILD
Normal file
@ -0,0 +1,26 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "SlotMachineAnimationNode",
|
||||
module_name = "SlotMachineAnimationNode",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/SyncCore:SyncCore",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -5,9 +5,11 @@ import Postbox
|
||||
import SyncCore
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import StickerResources
|
||||
import ManagedAnimationNode
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import AppBundle
|
||||
|
||||
private struct SlotMachineValue {
|
||||
enum ReelValue {
|
||||
@ -137,27 +139,29 @@ private func rightReelAnimationItem(value: SlotMachineValue.ReelValue, immediate
|
||||
}
|
||||
}
|
||||
|
||||
final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode {
|
||||
private let context: AccountContext
|
||||
public enum ManagedSlotMachineAnimationState: Equatable {
|
||||
case rolling
|
||||
case value(Int32, Bool)
|
||||
}
|
||||
|
||||
public final class SlotMachineAnimationNode: ASDisplayNode {
|
||||
private let backNode: ManagedAnimationNode
|
||||
private let leftReelNode: ManagedAnimationNode
|
||||
private let centerReelNode: ManagedAnimationNode
|
||||
private let rightReelNode: ManagedAnimationNode
|
||||
private let frontNode: ManagedAnimationNode
|
||||
|
||||
private var diceState: ManagedDiceAnimationState? = nil
|
||||
private var diceState: ManagedSlotMachineAnimationState? = nil
|
||||
private let disposables = DisposableSet()
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.context = context
|
||||
|
||||
let size = CGSize(width: 184.0, height: 184.0)
|
||||
self.backNode = ManagedAnimationNode(size: size)
|
||||
self.leftReelNode = ManagedAnimationNode(size: size)
|
||||
self.centerReelNode = ManagedAnimationNode(size: size)
|
||||
self.rightReelNode = ManagedAnimationNode(size: size)
|
||||
self.frontNode = ManagedAnimationNode(size: size)
|
||||
private let animationSize = CGSize(width: 184.0, height: 184.0)
|
||||
|
||||
public override init() {
|
||||
self.backNode = ManagedAnimationNode(size: self.animationSize)
|
||||
self.leftReelNode = ManagedAnimationNode(size: self.animationSize)
|
||||
self.centerReelNode = ManagedAnimationNode(size: self.animationSize)
|
||||
self.rightReelNode = ManagedAnimationNode(size: self.animationSize)
|
||||
self.frontNode = ManagedAnimationNode(size: self.animationSize)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -172,7 +176,7 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
||||
self.disposables.dispose()
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
public override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.backNode.frame = self.bounds
|
||||
@ -182,7 +186,7 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
||||
self.frontNode.frame = self.bounds
|
||||
}
|
||||
|
||||
func setState(_ diceState: ManagedDiceAnimationState) {
|
||||
public func setState(_ diceState: ManagedSlotMachineAnimationState) {
|
||||
let previousState = self.diceState
|
||||
self.diceState = diceState
|
||||
|
||||
@ -219,10 +223,10 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||
|
||||
switch diceState {
|
||||
case let .value(value, immediate):
|
||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||
|
||||
let slotValue = SlotMachineValue(rawValue: value)
|
||||
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: slotValue.left, immediate: immediate))
|
||||
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: slotValue.center, immediate: immediate))
|
||||
@ -231,7 +235,6 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
||||
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
||||
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: frames, loop: false))
|
||||
case .rolling:
|
||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
|
||||
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
|
||||
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
|
||||
@ -240,3 +243,97 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DiceAnimatedStickerNode: ASDisplayNode {
|
||||
public let intrinsicSize: CGSize
|
||||
|
||||
private let animationNode: AnimatedStickerNode
|
||||
|
||||
public var state: ManagedAnimationState?
|
||||
public var trackStack: [ManagedAnimationItem] = []
|
||||
public var didTryAdvancingState = false
|
||||
|
||||
init(size: CGSize) {
|
||||
self.intrinsicSize = size
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode.visibility = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.animationNode)
|
||||
|
||||
self.animationNode.completed = { [weak self] willStop in
|
||||
guard let strongSelf = self, !strongSelf.didTryAdvancingState, let state = strongSelf.state else {
|
||||
return
|
||||
}
|
||||
|
||||
if state.item.loop && strongSelf.trackStack.isEmpty {
|
||||
|
||||
} else {
|
||||
strongSelf.didTryAdvancingState = true
|
||||
strongSelf.advanceState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func advanceState() {
|
||||
guard !self.trackStack.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let item = self.trackStack.removeFirst()
|
||||
|
||||
if let state = self.state, state.item.source == item.source {
|
||||
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: state)
|
||||
} else {
|
||||
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: nil)
|
||||
}
|
||||
|
||||
var source: AnimatedStickerNodeSource?
|
||||
switch item.source {
|
||||
case let .local(animationName):
|
||||
if let path = getAppBundle().path(forResource: animationName, ofType: "tgs") {
|
||||
source = AnimatedStickerNodeLocalFileSource(path: path)
|
||||
}
|
||||
case let .resource(account, resource):
|
||||
source = AnimatedStickerResourceSource(account: account, resource: resource)
|
||||
}
|
||||
|
||||
let playbackMode: AnimatedStickerPlaybackMode
|
||||
if item.loop {
|
||||
playbackMode = .loop
|
||||
} else if let frames = item.frames, case let .still(position) = frames {
|
||||
playbackMode = .still(position == .start ? .start : .end)
|
||||
} else {
|
||||
playbackMode = .once
|
||||
}
|
||||
|
||||
if let source = source {
|
||||
self.animationNode.setup(source: source, width: Int(self.intrinsicSize.width), height: Int(self.intrinsicSize.height), playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
|
||||
self.didTryAdvancingState = false
|
||||
}
|
||||
|
||||
func trackTo(item: ManagedAnimationItem) {
|
||||
if let currentItem = self.state?.item {
|
||||
if currentItem.source == item.source && currentItem.frames == item.frames && currentItem.loop == item.loop {
|
||||
return
|
||||
}
|
||||
}
|
||||
self.trackStack.append(item)
|
||||
self.didTryAdvancingState = false
|
||||
|
||||
if !self.animationNode.isPlaying {
|
||||
self.advanceState()
|
||||
}
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.animationNode.updateLayout(size: self.bounds.size)
|
||||
self.animationNode.frame = self.bounds
|
||||
}
|
||||
}
|
@ -72,6 +72,7 @@ public struct Namespaces {
|
||||
public static let cachedThemesConfiguration: Int8 = 8
|
||||
public static let cachedPollResults: Int8 = 9
|
||||
public static let cachedContextResults: Int8 = 10
|
||||
public static let proximityNotificationStoredState: Int8 = 11
|
||||
}
|
||||
|
||||
public struct UnorderedItemList {
|
||||
|
@ -45,7 +45,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
case botSentSecureValues(types: [SentSecureValueType])
|
||||
case peerJoined
|
||||
case phoneNumberRequest
|
||||
case geoProximityReached(distance: Int32)
|
||||
case geoProximityReached(from: PeerId, to: PeerId, distance: Int32)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
|
||||
@ -97,7 +97,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
case 20:
|
||||
self = .phoneNumberRequest
|
||||
case 21:
|
||||
self = .geoProximityReached(distance: (decoder.decodeInt32ForKey("dst", orElse: 0)))
|
||||
self = .geoProximityReached(from: PeerId(decoder.decodeInt64ForKey("fromId", orElse: 0)), to: PeerId(decoder.decodeInt64ForKey("toId", orElse: 0)), distance: (decoder.decodeInt32ForKey("dst", orElse: 0)))
|
||||
default:
|
||||
self = .unknown
|
||||
}
|
||||
@ -183,8 +183,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
encoder.encodeInt32(19, forKey: "_rawValue")
|
||||
case .phoneNumberRequest:
|
||||
encoder.encodeInt32(20, forKey: "_rawValue")
|
||||
case let .geoProximityReached(distance):
|
||||
case let .geoProximityReached(from, to, distance):
|
||||
encoder.encodeInt32(21, forKey: "_rawValue")
|
||||
encoder.encodeInt64(from.toInt64(), forKey: "fromId")
|
||||
encoder.encodeInt64(to.toInt64(), forKey: "toId")
|
||||
encoder.encodeInt32(distance, forKey: "dst")
|
||||
}
|
||||
}
|
||||
@ -201,6 +203,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
return [groupId]
|
||||
case let .groupMigratedToChannel(channelId):
|
||||
return [channelId]
|
||||
case let .geoProximityReached(from, to, _):
|
||||
return [from, to]
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
@ -168,6 +168,7 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(Country.CountryCode.self, f: { Country.CountryCode(decoder: $0) })
|
||||
declareEncodable(CountriesList.self, f: { CountriesList(decoder: $0) })
|
||||
declareEncodable(ValidationMessageAttribute.self, f: { ValidationMessageAttribute(decoder: $0) })
|
||||
declareEncodable(ProximityNotificationStoredState.self, f: { ProximityNotificationStoredState(decoder: $0) })
|
||||
|
||||
return
|
||||
}()
|
||||
|
@ -2327,6 +2327,14 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
}
|
||||
}
|
||||
}
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case .geoProximityReached = action.action {
|
||||
if id.peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
let _ = updateProximityNotificationStoredStateInteractively(postbox: postbox, peerId: id.peerId, state: nil).start()
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
|
||||
import SyncCore
|
||||
|
||||
@ -9,7 +10,7 @@ public func topPeerActiveLiveLocationMessages(viewTracker: AccountViewTracker, a
|
||||
|> map { (view, _, _) -> (Peer?, [Message]) in
|
||||
var accountPeer: Peer?
|
||||
for entry in view.additionalData {
|
||||
if case let .peer(id, peer) = entry {
|
||||
if case let .peer(_, peer) = entry {
|
||||
accountPeer = peer
|
||||
break
|
||||
}
|
||||
@ -31,3 +32,93 @@ public func topPeerActiveLiveLocationMessages(viewTracker: AccountViewTracker, a
|
||||
return (accountPeer, result)
|
||||
}
|
||||
}
|
||||
|
||||
public func requestProximityNotification(postbox: Postbox, network: Network, messageId: MessageId, distance: Int32) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Void, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
let flags: Int32 = 1 << 0
|
||||
return network.request(Api.functions.messages.requestProximityNotification(flags: flags, peer: inputPeer, msgId: messageId.id, maxDistance: distance))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return updateProximityNotificationStoredStateInteractively(postbox: postbox, peerId: messageId.peerId, state: ProximityNotificationStoredState(messageId: messageId, distance: distance))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func cancelProximityNotification(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Void, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
return network.request(Api.functions.messages.requestProximityNotification(flags: 0, peer: inputPeer, msgId: messageId.id, maxDistance: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return updateProximityNotificationStoredStateInteractively(postbox: postbox, peerId: messageId.peerId, state: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ProximityNotificationStoredState: PostboxCoding {
|
||||
public let messageId: MessageId
|
||||
public let distance: Int32
|
||||
|
||||
public init(messageId: MessageId, distance: Int32) {
|
||||
self.messageId = messageId
|
||||
self.distance = distance
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("id.peerId", orElse: 0)), namespace: decoder.decodeInt32ForKey("id.namespace", orElse: 0), id: decoder.decodeInt32ForKey("id.id", orElse: 0))
|
||||
self.distance = decoder.decodeInt32ForKey("distance", orElse: 0)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt64(self.messageId.peerId.toInt64(), forKey: "id.peerId")
|
||||
encoder.encodeInt32(self.messageId.namespace, forKey: "id.namespace")
|
||||
encoder.encodeInt32(self.messageId.id, forKey: "id.id")
|
||||
encoder.encodeInt32(self.distance, forKey: "distance")
|
||||
}
|
||||
}
|
||||
|
||||
private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 25, highWaterItemCount: 50)
|
||||
|
||||
public func updateProximityNotificationStoredStateInteractively(postbox: Postbox, peerId: PeerId, state: ProximityNotificationStoredState?) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let key = ValueBoxKey(length: 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
let id = ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.proximityNotificationStoredState, key: key)
|
||||
if let state = state {
|
||||
transaction.putItemCacheEntry(id: id, entry: state, collectionSpec: collectionSpec)
|
||||
} else {
|
||||
transaction.removeItemCacheEntry(id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
public func proximityNotificationStoredState(account: Account, peerId: PeerId) -> Signal<ProximityNotificationStoredState?, NoError> {
|
||||
let key = ValueBoxKey(length: 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
let id = ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.proximityNotificationStoredState, key: key)
|
||||
let viewKey = PostboxViewKey.cachedItem(id)
|
||||
return account.postbox.combinedView(keys: [viewKey])
|
||||
|> map { views -> ProximityNotificationStoredState? in
|
||||
if let value = (views.views[viewKey] as? CachedItemView)?.value as? ProximityNotificationStoredState {
|
||||
return value
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,42 +311,3 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func requestProximityNotification(postbox: Postbox, network: Network, messageId: MessageId, distance: Int32) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Void, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
let flags: Int32 = 1 << 0
|
||||
return network.request(Api.functions.messages.requestProximityNotification(flags: flags, peer: inputPeer, msgId: messageId.id, maxDistance: distance))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func cancelProximityNotification(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Void, NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
return network.request(Api.functions.messages.requestProximityNotification(flags: 1 << 1, peer: inputPeer, msgId: messageId.id, maxDistance: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
case .messageActionContactSignUp:
|
||||
return TelegramMediaAction(action: .peerJoined)
|
||||
case let .messageActionGeoProximityReached(fromId, toId, distance):
|
||||
return TelegramMediaAction(action: .geoProximityReached(distance: distance))
|
||||
return TelegramMediaAction(action: .geoProximityReached(from: fromId.peerId, to: toId.peerId, distance: distance))
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -442,9 +442,13 @@ 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(distance):
|
||||
case let .geoProximityReached(_, toId, distance):
|
||||
let distanceString = stringForDistance(strings: strings, distance: Double(distance))
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(authorName, distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
|
||||
if toId == accountPeerId {
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(authorName, 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)]))
|
||||
}
|
||||
case .unknown:
|
||||
attributedString = nil
|
||||
}
|
||||
|
@ -211,6 +211,7 @@ framework(
|
||||
"//submodules/ChatInterfaceState:ChatInterfaceState",
|
||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -208,6 +208,7 @@ swift_library(
|
||||
"//submodules/ChatInterfaceState:ChatInterfaceState",
|
||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
Binary file not shown.
@ -361,7 +361,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
var processed = false
|
||||
for media in firstMessage.media {
|
||||
if let action = media as? TelegramMediaAction, case let .geoProximityReached(distance) = action.action {
|
||||
if let action = media as? TelegramMediaAction, case let .geoProximityReached(fromId, toId, distance) = action.action {
|
||||
strongSelf.context.sharedContext.openLocationScreen(context: strongSelf.context, messageId: firstMessage.id, navigationController: strongSelf.rootController)
|
||||
processed = true
|
||||
break
|
||||
|
@ -19,6 +19,7 @@ import TelegramAnimatedStickerNode
|
||||
import Emoji
|
||||
import Markdown
|
||||
import ManagedAnimationNode
|
||||
import SlotMachineAnimationNode
|
||||
|
||||
private let nameFont = Font.medium(14.0)
|
||||
private let inlineBotPrefixFont = Font.regular(14.0)
|
||||
@ -32,6 +33,10 @@ extension AnimatedStickerNode: GenericAnimatedStickerNode {
|
||||
|
||||
}
|
||||
|
||||
extension SlotMachineAnimationNode: GenericAnimatedStickerNode {
|
||||
|
||||
}
|
||||
|
||||
class ChatMessageShareButton: HighlightableButtonNode {
|
||||
private let backgroundNode: ASImageNode
|
||||
private let iconNode: ASImageNode
|
||||
@ -328,7 +333,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
if let telegramDice = self.telegramDice {
|
||||
if telegramDice.emoji == "🎰" {
|
||||
let animationNode = SlotMachineAnimationNode(context: item.context)
|
||||
let animationNode = SlotMachineAnimationNode()
|
||||
self.animationNode = animationNode
|
||||
} else {
|
||||
let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
|
||||
@ -1458,10 +1463,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode, let item = self.item, item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
if let telegramDice = self.telegramDice, let item = self.item, item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
if let value = telegramDice.value, value != 0 {
|
||||
diceNode.setState(.rolling)
|
||||
diceNode.setState(.value(value, false))
|
||||
if let diceNode = self.animationNode as? ManagedDiceAnimationNode {
|
||||
diceNode.setState(.rolling)
|
||||
diceNode.setState(.value(value, false))
|
||||
} else if let diceNode = self.animationNode as? SlotMachineAnimationNode {
|
||||
diceNode.setState(.rolling)
|
||||
diceNode.setState(.value(value, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ private func animationItem(account: Account, emojis: Signal<[TelegramMediaFile],
|
||||
|
||||
if let _ = account.postbox.mediaBox.completedResourcePath(file.resource) {
|
||||
if immediate {
|
||||
return .single(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), frames: .still(.end), duration: 0))
|
||||
return .single(ManagedAnimationItem(source: .resource(account, file.resource), frames: .still(.end), duration: 0))
|
||||
} else {
|
||||
return .single(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), loop: loop, callbacks: callbacks))
|
||||
return .single(ManagedAnimationItem(source: .resource(account, file.resource), loop: loop, callbacks: callbacks))
|
||||
}
|
||||
} else {
|
||||
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
@ -45,7 +45,7 @@ private func animationItem(account: Account, emojis: Signal<[TelegramMediaFile],
|
||||
|> filter { data in
|
||||
return data.complete
|
||||
}).start(next: { next in
|
||||
subscriber.putNext(ManagedAnimationItem(source: .resource(account.postbox.mediaBox, file.resource), loop: loop, callbacks: callbacks))
|
||||
subscriber.putNext(ManagedAnimationItem(source: .resource(account, file.resource), loop: loop, callbacks: callbacks))
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
||||
|
@ -21,6 +21,7 @@ static_library(
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -22,6 +22,7 @@ swift_library(
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -10,6 +10,7 @@ import RadialStatusNode
|
||||
import AppBundle
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import SlotMachineAnimationNode
|
||||
import AnimationUI
|
||||
import SyncCore
|
||||
import Postbox
|
||||
@ -24,6 +25,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
private let iconCheckNode: RadialStatusNode?
|
||||
private let animationNode: AnimationNode?
|
||||
private var animatedStickerNode: AnimatedStickerNode?
|
||||
private var slotMachineNode: SlotMachineAnimationNode?
|
||||
private var stillStickerNode: TransformImageNode?
|
||||
private var stickerImageSize: CGSize?
|
||||
private var stickerOffset: CGPoint?
|
||||
@ -339,23 +341,31 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
break
|
||||
}
|
||||
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
|
||||
let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .dice(dice.emoji), forceActualized: false)
|
||||
|> deliverOnMainQueue).start(next: { stickerPack in
|
||||
if let value = dice.value {
|
||||
switch stickerPack {
|
||||
case let .result(_, items, _):
|
||||
let item = items[Int(value)]
|
||||
if let item = item as? StickerPackItem {
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: 120, height: 120, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
default:
|
||||
break
|
||||
if dice.emoji == "🎰" {
|
||||
let slotMachineNode = SlotMachineAnimationNode()
|
||||
self.slotMachineNode = slotMachineNode
|
||||
|
||||
// slotMachineNode.setState(.rolling)
|
||||
// slotMachineNode.setState(.value(value, true))
|
||||
} else {
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
|
||||
let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .dice(dice.emoji), forceActualized: false)
|
||||
|> deliverOnMainQueue).start(next: { stickerPack in
|
||||
if let value = dice.value {
|
||||
switch stickerPack {
|
||||
case let .result(_, items, _):
|
||||
let item = items[Int(value)]
|
||||
if let item = item as? StickerPackItem {
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: 120, height: 120, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
self.remainingSeconds = self.originalRemainingSeconds
|
||||
@ -397,6 +407,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.animationNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||
self.stillStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||
self.panelWrapperNode.addSubnode(self.titleNode)
|
||||
self.panelWrapperNode.addSubnode(self.textNode)
|
||||
self.panelWrapperNode.addSubnode(self.buttonNode)
|
||||
@ -593,6 +604,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
|
||||
animatedStickerNode.updateLayout(size: iconFrame.size)
|
||||
transition.updateFrame(node: animatedStickerNode, frame: iconFrame)
|
||||
} else if let slotMachineNode = self.slotMachineNode {
|
||||
let iconSize = CGSize(width: 32.0, height: 32.0)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0)), size: iconSize)
|
||||
transition.updateFrame(node: slotMachineNode, frame: iconFrame)
|
||||
}
|
||||
|
||||
let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
|
||||
|
Loading…
x
Reference in New Issue
Block a user