Various improvements

This commit is contained in:
Ilya Laktyushin 2020-10-21 02:29:08 +04:00
parent 047a0085c7
commit 8ca5750cb9
35 changed files with 4773 additions and 4441 deletions

View File

@ -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";

View File

@ -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,9 +961,15 @@ 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
@ -1021,9 +1038,15 @@ 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

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
{

View File

@ -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

View File

@ -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) {

View File

@ -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() {

View File

@ -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] = []
@ -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 {

View File

@ -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",

View File

@ -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",

View File

@ -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)] = []) {

View File

@ -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]) {

View File

@ -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

View 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",
],
)

View 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",
],
)

View File

@ -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
private let animationSize = CGSize(width: 184.0, height: 184.0)
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)
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
}
}

View File

@ -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 {

View File

@ -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 []
}

View File

@ -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
}()

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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()
}
}
}

View File

@ -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))
}
}

View File

@ -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
}

View File

@ -211,6 +211,7 @@ framework(
"//submodules/ChatInterfaceState:ChatInterfaceState",
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -208,6 +208,7 @@ swift_library(
"//submodules/ChatInterfaceState:ChatInterfaceState",
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
],
visibility = [
"//visibility:public",

View File

@ -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

View File

@ -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))
}
}
}
}

View File

@ -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()
})

View File

@ -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",

View File

@ -22,6 +22,7 @@ swift_library(
"//submodules/AppBundle:AppBundle",
"//submodules/StickerResources:StickerResources",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
],
visibility = [
"//visibility:public",

View File

@ -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
if dice.emoji == "🎰" {
let slotMachineNode = SlotMachineAnimationNode()
self.slotMachineNode = slotMachineNode
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
// 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))