diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 3222a2a7a5..bbb9b2e384 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1451,6 +1451,7 @@ public protocol AccountContext: AnyObject { var downloadedMediaStoreManager: DownloadedMediaStoreManager { get } var peerChannelMemberCategoriesContextsManager: PeerChannelMemberCategoriesContextsManager { get } var wallpaperUploadManager: WallpaperUploadManager? { get } + var watchManager: WatchManager? { get } var inAppPurchaseManager: InAppPurchaseManager? { get } var starsContext: StarsContext? { get } var tonContext: StarsContext? { get } diff --git a/submodules/AccountContext/Sources/WatchManager.swift b/submodules/AccountContext/Sources/WatchManager.swift new file mode 100644 index 0000000000..ce2e97c3ef --- /dev/null +++ b/submodules/AccountContext/Sources/WatchManager.swift @@ -0,0 +1,23 @@ +import Foundation +import SwiftSignalKit +import TelegramCore + +public struct WatchRunningTasks: Equatable { + public let running: Bool + public let version: Int32 + + public init(running: Bool, version: Int32) { + self.running = running + self.version = version + } + + public static func ==(lhs: WatchRunningTasks, rhs: WatchRunningTasks) -> Bool { + return lhs.running == rhs.running && lhs.version == rhs.version + } +} + +public protocol WatchManager: AnyObject { + var watchAppInstalled: Signal { get } + var navigateToMessageRequested: Signal { get } + var runningTasks: Signal { get } +} diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 6e70f69596..92ac289279 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -895,6 +895,9 @@ private func languageSearchableItems(context: AccountContext, localizations: [Lo } func settingsSearchableItems(context: AccountContext, notificationExceptionsList: Signal, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, privacySettings: Signal, hasTwoStepAuth: Signal, twoStepAuthData: Signal, activeSessionsContext: Signal, webSessionsContext: Signal) -> Signal<[SettingsSearchableItem], NoError> { + let watchAppInstalled = (context.watchManager?.watchAppInstalled ?? .single(false)) + |> take(1) + let canAddAccount = activeAccountsAndPeers(context: context) |> take(1) |> map { accountsAndPeers -> Bool in @@ -988,8 +991,8 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList } } - return combineLatest(canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext) - |> map { canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext in + return combineLatest(watchAppInstalled, canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext) + |> map { watchAppInstalled, canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext in let strings = context.sharedContext.currentPresentationData.with { $0 }.strings var allItems: [SettingsSearchableItem] = [] @@ -1040,6 +1043,13 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList let storiesItems = storiesSearchableItems(context: context) allItems.append(contentsOf: storiesItems) + if watchAppInstalled { + let watch = SettingsSearchableItem(id: .watch(0), title: strings.Settings_AppleWatch, alternate: synonyms(strings.SettingsSearch_Synonyms_Watch), icon: .watch, breadcrumbs: [], present: { context, _, present in + present(.push, watchSettingsController(context: context)) + }) + allItems.append(watch) + } + if let hasTwoStepAuth = hasTwoStepAuth, hasTwoStepAuth { let passport = SettingsSearchableItem(id: .passport(0), title: strings.Settings_Passport, alternate: synonyms(strings.SettingsSearch_Synonyms_Passport), icon: .passport, breadcrumbs: [], present: { context, _, present in present(.modal, SecureIdAuthController(context: context, mode: .list)) diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 3605675c74..dd6a7409de 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -96,6 +96,7 @@ swift_library( "//submodules/TelegramVoip:TelegramVoip", "//submodules/DeviceAccess:DeviceAccess", "//submodules/Utils/DeviceModel", + "//submodules/WatchCommon/Host:WatchCommon", "//submodules/BuildConfig:BuildConfig", "//submodules/BuildConfigExtra:BuildConfigExtra", "//submodules/rlottie:RLottieBinding", @@ -217,6 +218,8 @@ swift_library( "//submodules/RaiseToListen:RaiseToListen", "//submodules/OpusBinding:OpusBinding", "//third-party/opus:opus", + "//submodules/WatchBridgeAudio:WatchBridgeAudio", + "//submodules/WatchBridge:WatchBridge", "//submodules/ShareItems:ShareItems", "//submodules/ShareItems/Impl:ShareItemsImpl", "//submodules/SettingsUI:SettingsUI", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 6d553af3e0..cc6b95a0f5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -914,6 +914,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, combineLatest(notificationExceptions, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get()), combineLatest(context.account.viewTracker.featuredStickerPacks(), archivedStickerPacks), hasPassport, + (context.watchManager?.watchAppInstalled ?? .single(false)), context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]), context.engine.notices.getServerProvidedSuggestions(), context.engine.data.get( @@ -932,7 +933,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, starsState, tonState ) - |> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel, starsState, tonState -> PeerInfoScreenData in + |> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel, starsState, tonState -> PeerInfoScreenData in let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications let (featuredStickerPacks, archivedStickerPacks) = stickerPacks @@ -979,7 +980,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, userLimits: peer?.isPremium == true ? limits.1 : limits.0, bots: bots, hasPassport: hasPassport, - hasWatchApp: false, + hasWatchApp: hasWatchApp, enableQRLogin: enableQRLogin ) diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index 7f736a425d..134d999623 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -159,6 +159,8 @@ public final class AccountContextImpl: AccountContext { return self._countriesConfiguration.get() } + public var watchManager: WatchManager? + private var storedPassword: (String, CFAbsoluteTime, SwiftSignalKit.Timer)? private var limitsConfigurationDisposable: Disposable? private var contentSettingsDisposable: Disposable? diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 89e0a091e3..86c95f779f 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -31,6 +31,7 @@ import OverlayStatusController import UndoUI import LegacyUI import PassportUI +import WatchBridge import SettingsUI import AppBundle import UrlHandling diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 5a62cb22bd..6484cdae2f 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -22,6 +22,7 @@ import TelegramPermissionsUI import PasscodeUI import ImageBlur import FastBlur +import WatchBridge import SettingsUI import AppLock import AccountUtils @@ -813,6 +814,54 @@ final class AuthorizedApplicationContext { } }) + let _ = (watchManagerArguments + |> deliverOnMainQueue).start(next: { [weak self] arguments in + guard let strongSelf = self else { + return + } + + let watchManager = WatchManagerImpl(arguments: arguments) + strongSelf.context.watchManager = watchManager + + strongSelf.watchNavigateToMessageDisposable.set((strongSelf.context.sharedContext.applicationBindings.applicationInForeground |> mapToSignal({ applicationInForeground -> Signal<(Bool, MessageId), NoError> in + return watchManager.navigateToMessageRequested + |> map { messageId in + return (applicationInForeground, messageId) + } + |> deliverOnMainQueue + })).start(next: { [weak self] applicationInForeground, messageId in + if let strongSelf = self { + if applicationInForeground { + var chatIsVisible = false + if let controller = strongSelf.rootController.viewControllers.last as? ChatControllerImpl, case .peer(messageId.peerId) = controller.chatLocation { + chatIsVisible = true + } + + let navigateToMessage = { + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId)) + |> deliverOnMainQueue).start(next: { peer in + guard let peer = peer else { + return + } + + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(peer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false))) + }) + } + + if chatIsVisible { + navigateToMessage() + } else { + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let controller = textAlertController(context: strongSelf.context, title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)]) + (strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root)) + } + } else { + //strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId) + } + } + })) + }) + self.rootController.setForceInCallStatusBar((self.context.sharedContext as! SharedAccountContextImpl).currentCallStatusBarNode) if let groupCallController = self.context.sharedContext.currentGroupCallController as? VoiceChatController { if let overlayController = groupCallController.currentOverlayController { diff --git a/submodules/TelegramUI/Sources/WatchManager.swift b/submodules/TelegramUI/Sources/WatchManager.swift new file mode 100644 index 0000000000..daebf13070 --- /dev/null +++ b/submodules/TelegramUI/Sources/WatchManager.swift @@ -0,0 +1,25 @@ +import Foundation +import SwiftSignalKit +import TelegramCore +import AccountContext +import WatchBridge + +public final class WatchManagerImpl: WatchManager { + private let arguments: WatchManagerArguments? + + public init(arguments: WatchManagerArguments?) { + self.arguments = arguments + } + + public var watchAppInstalled: Signal { + return self.arguments?.appInstalled ?? .single(false) + } + + public var navigateToMessageRequested: Signal { + return self.arguments?.navigateToMessageRequested ?? .never() + } + + public var runningTasks: Signal { + return self.arguments?.runningTasks ?? .single(nil) + } +} diff --git a/submodules/WatchBridge/BUILD b/submodules/WatchBridge/BUILD new file mode 100644 index 0000000000..5766afcb8f --- /dev/null +++ b/submodules/WatchBridge/BUILD @@ -0,0 +1,33 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "WatchBridge", + module_name = "WatchBridge", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/SSignalKit/SSignalKit:SSignalKit", + "//submodules/Postbox:Postbox", + "//submodules/TelegramCore:TelegramCore", + "//submodules/WatchCommon/Host:WatchCommon", + "//submodules/WatchBridgeAudio:WatchBridgeAudio", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/TelegramUIPreferences:TelegramUIPreferences", + "//submodules/AccountContext:AccountContext", + "//submodules/AvatarNode:AvatarNode", + "//submodules/StickerResources:StickerResources", + "//submodules/PhotoResources:PhotoResources", + "//submodules/LegacyComponents:LegacyComponents", + "//submodules/LegacyUI:LegacyUI", + "//submodules/PhoneNumberFormat:PhoneNumberFormat", + "//submodules/WatchBridge/Impl:WatchBridgeImpl", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WatchBridge/Impl/BUILD b/submodules/WatchBridge/Impl/BUILD new file mode 100644 index 0000000000..764f26875a --- /dev/null +++ b/submodules/WatchBridge/Impl/BUILD @@ -0,0 +1,27 @@ + +objc_library( + name = "WatchBridgeImpl", + enable_modules = True, + module_name = "WatchBridgeImpl", + srcs = glob([ + "Sources/**/*.m", + "Sources/**/*.h", + ], allow_empty=True), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + ], + deps = [ + "//submodules/LegacyComponents:LegacyComponents", + "//submodules/WatchCommon/Host:WatchCommon", + ], + sdk_frameworks = [ + "Foundation", + "WatchConnectivity", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WatchBridge/Impl/PublicHeaders/WatchBridgeImpl/TGBridgeServer.h b/submodules/WatchBridge/Impl/PublicHeaders/WatchBridgeImpl/TGBridgeServer.h new file mode 100644 index 0000000000..d28a8b0320 --- /dev/null +++ b/submodules/WatchBridge/Impl/PublicHeaders/WatchBridgeImpl/TGBridgeServer.h @@ -0,0 +1,29 @@ +#import +#import + +@class TGBridgeSubscription; + +@interface TGBridgeServer : NSObject + +@property (nonatomic, readonly) NSURL * _Nullable temporaryFilesURL; + +@property (nonatomic, readonly) bool isRunning; + +- (instancetype _Nonnull)initWithHandler:(SSignal * _Nullable (^ _Nonnull)(TGBridgeSubscription * _Nullable))handler fileHandler:(void (^ _Nonnull)(NSString * _Nullable, NSDictionary * _Nullable))fileHandler dispatchOnQueue:(void (^ _Nonnull)(void (^ _Nonnull)(void)))dispatchOnQueue logFunction:(void (^ _Nonnull)(NSString * _Nullable))logFunction allowBackgroundTimeExtension:(void (^ _Nonnull)())allowBackgroundTimeExtension; +- (void)startRunning; + +- (SSignal * _Nonnull)watchAppInstalledSignal; +- (SSignal * _Nonnull)runningRequestsSignal; + +- (void)setAuthorized:(bool)authorized userId:(int64_t)userId; +- (void)setMicAccessAllowed:(bool)allowed; +- (void)setStartupData:(NSDictionary * _Nullable)data; +- (void)pushContext; + +- (void)sendFileWithURL:(NSURL * _Nonnull)url metadata:(NSDictionary * _Nullable)metadata asMessageData:(bool)asMessageData; +- (void)sendFileWithData:(NSData * _Nonnull)data metadata:(NSDictionary * _Nullable)metadata errorHandler:(void (^ _Nullable)(void))errorHandler; + +- (NSInteger)wakeupNetwork; +- (void)suspendNetworkIfReady:(NSInteger)token; + +@end diff --git a/submodules/WatchBridge/Impl/PublicHeaders/WatchBridgeImpl/WatchBridgeImpl.h b/submodules/WatchBridge/Impl/PublicHeaders/WatchBridgeImpl/WatchBridgeImpl.h new file mode 100644 index 0000000000..8ed2f162d1 --- /dev/null +++ b/submodules/WatchBridge/Impl/PublicHeaders/WatchBridgeImpl/WatchBridgeImpl.h @@ -0,0 +1,3 @@ +#import + +#import diff --git a/submodules/WatchBridge/Impl/Sources/TGBridgeServer.m b/submodules/WatchBridge/Impl/Sources/TGBridgeServer.m new file mode 100644 index 0000000000..5664ac12af --- /dev/null +++ b/submodules/WatchBridge/Impl/Sources/TGBridgeServer.m @@ -0,0 +1,770 @@ +#import + +#import +#import +#import +#import + +@interface TGBridgeSignalManager : NSObject + +- (bool)startSignalForKey:(NSString *)key producer:(SSignal *(^)())producer; +- (void)haltSignalForKey:(NSString *)key; +- (void)haltAllSignals; + +@end + +@interface TGBridgeServer () +{ + SSignal *(^_handler)(TGBridgeSubscription *); + void (^_fileHandler)(NSString *, NSDictionary *); + void (^_logFunction)(NSString *); + void (^_dispatch)(void (^)(void)); + + bool _pendingStart; + + bool _processingNotification; + + int32_t _sessionId; + volatile int32_t _tasksVersion; + + TGBridgeContext *_activeContext; + + TGBridgeSignalManager *_signalManager; + + os_unfair_lock _incomingQueueLock; + NSMutableArray *_incomingMessageQueue; + + bool _requestSubscriptionList; + NSArray *_initialSubscriptionList; + + os_unfair_lock _outgoingQueueLock; + NSMutableArray *_outgoingMessageQueue; + + os_unfair_lock _replyHandlerMapLock; + NSMutableDictionary *_replyHandlerMap; + + SPipe *_appInstalled; + + NSMutableDictionary *_runningTasks; + SVariable *_hasRunningTasks; + + void (^_allowBackgroundTimeExtension)(); +} + +@property (nonatomic, readonly) WCSession *session; + +@end + +@implementation TGBridgeServer + +- (instancetype)initWithHandler:(SSignal *(^)(TGBridgeSubscription *))handler fileHandler:(void (^)(NSString *, NSDictionary *))fileHandler dispatchOnQueue:(void (^)(void (^)(void)))dispatchOnQueue logFunction:(void (^)(NSString *))logFunction allowBackgroundTimeExtension:(void (^)())allowBackgroundTimeExtension +{ + self = [super init]; + if (self != nil) + { + _handler = [handler copy]; + _fileHandler = [fileHandler copy]; + _dispatch = [dispatchOnQueue copy]; + _logFunction = [logFunction copy]; + _allowBackgroundTimeExtension = [allowBackgroundTimeExtension copy]; + + _runningTasks = [[NSMutableDictionary alloc] init]; + _hasRunningTasks = [[SVariable alloc] init]; + [_hasRunningTasks set:[SSignal single:@false]]; + + _signalManager = [[TGBridgeSignalManager alloc] init]; + _incomingMessageQueue = [[NSMutableArray alloc] init]; + + self.session.delegate = self; + [self.session activateSession]; + + _replyHandlerMap = [[NSMutableDictionary alloc] init]; + + _appInstalled = [[SPipe alloc] init]; + + _activeContext = [[TGBridgeContext alloc] initWithDictionary:[self.session applicationContext]]; + } + return self; +} + +- (void)log:(NSString *)message +{ + _logFunction(message); +} + +- (void)dispatch:(void (^)(void))action +{ + _dispatch(action); +} + +- (void)startRunning +{ + if (self.isRunning) + return; + + os_unfair_lock_lock(&_incomingQueueLock); + _isRunning = true; + + for (id message in _incomingMessageQueue) + [self handleMessage:message replyHandler:nil finishTask:nil completion:nil]; + + [_incomingMessageQueue removeAllObjects]; + os_unfair_lock_unlock(&_incomingQueueLock); + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self dispatch:^{ + _appInstalled.sink(@(self.session.isWatchAppInstalled)); + }]; + }); +} + +- (NSURL *)temporaryFilesURL +{ + return self.session.watchDirectoryURL; +} + +- (SSignal *)watchAppInstalledSignal +{ + return [[SSignal single:@(self.session.watchAppInstalled)] then:_appInstalled.signalProducer()]; +} + +- (SSignal *)runningRequestsSignal +{ + return _hasRunningTasks.signal; +} + +#pragma mark - + +- (void)setAuthorized:(bool)authorized userId:(int64_t)userId +{ + _activeContext = [_activeContext updatedWithAuthorized:authorized peerId:userId]; +} + +- (void)setMicAccessAllowed:(bool)allowed +{ + _activeContext = [_activeContext updatedWithMicAccessAllowed:allowed]; +} + +- (void)setStartupData:(NSDictionary *)data +{ + _activeContext = [_activeContext updatedWithPreheatData:data]; +} + +- (void)pushContext +{ + NSError *error; + [self.session updateApplicationContext:[_activeContext dictionary] error:&error]; + + //if (error != nil) + //TGLog(@"[BridgeServer][ERROR] Failed to push active application context: %@", error.localizedDescription); +} + +#pragma mark - + +- (void)handleMessageData:(NSData *)messageData task:(id)task replyHandler:(void (^)(NSData *))replyHandler completion:(void (^)(void))completion +{ + if (_allowBackgroundTimeExtension) { + _allowBackgroundTimeExtension(); + } + + __block id runningTask = task; + void (^finishTask)(NSTimeInterval) = ^(NSTimeInterval delay) + { + if (runningTask == nil) + return; + + void (^block)(void) = ^ + { + [self dispatch:^{ + [runningTask dispose]; + //TGLog(@"[BridgeServer]: ended taskid: %d", runningTask); + runningTask = nil; + }]; + }; + + if (delay > DBL_EPSILON) + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((delay) * NSEC_PER_SEC)), dispatch_get_main_queue(), block); + else + block(); + }; + + id message = [NSKeyedUnarchiver unarchiveObjectWithData:messageData]; + os_unfair_lock_lock(&_incomingQueueLock); + if (!self.isRunning) + { + [_incomingMessageQueue addObject:message]; + + if (replyHandler != nil) + replyHandler([NSData data]); + + finishTask(4.0); + + os_unfair_lock_unlock(&_incomingQueueLock); + return; + } + os_unfair_lock_unlock(&_incomingQueueLock); + + [self handleMessage:message replyHandler:replyHandler finishTask:finishTask completion:completion]; +} + +- (void)handleMessage:(id)message replyHandler:(void (^)(NSData *))replyHandler finishTask:(void (^)(NSTimeInterval))finishTask completion:(void (^)(void))completion +{ + if ([message isKindOfClass:[TGBridgeSubscription class]]) + { + TGBridgeSubscription *subcription = (TGBridgeSubscription *)message; + [self _createSubscription:subcription replyHandler:replyHandler finishTask:finishTask completion:completion]; + + //TGLog(@"[BridgeServer] Create subscription: %@", subcription); + } + else if ([message isKindOfClass:[TGBridgeDisposal class]]) + { + TGBridgeDisposal *disposal = (TGBridgeDisposal *)message; + [_signalManager haltSignalForKey:[NSString stringWithFormat:@"%lld", disposal.identifier]]; + + if (replyHandler != nil) + replyHandler([NSData data]); + + if (completion != nil) + completion(); + + //TGLog(@"[BridgeServer] Dispose subscription %lld", disposal.identifier); + + if (finishTask != nil) + finishTask(0); + } + else if ([message isKindOfClass:[TGBridgeSubscriptionList class]]) + { + TGBridgeSubscriptionList *list = (TGBridgeSubscriptionList *)message; + for (TGBridgeSubscription *subscription in list.subscriptions) + [self _createSubscription:subscription replyHandler:nil finishTask:nil completion:nil]; + + //TGLog(@"[BridgeServer] Received subscription list, applying"); + + if (replyHandler != nil) + replyHandler([NSData data]); + + if (finishTask != nil) + finishTask(4.0); + + if (completion != nil) + completion(); + } + else if ([message isKindOfClass:[TGBridgePing class]]) + { + TGBridgePing *ping = (TGBridgePing *)message; + if (_sessionId != ping.sessionId) + { + //TGLog(@"[BridgeServer] Session id mismatch"); + + if (_sessionId != 0) + { + //TGLog(@"[BridgeServer] Halt all active subscriptions"); + [_signalManager haltAllSignals]; + + os_unfair_lock_lock(&_outgoingQueueLock); + [_outgoingMessageQueue removeAllObjects]; + os_unfair_lock_unlock(&_outgoingQueueLock); + } + + _sessionId = ping.sessionId; + + if (self.session.isReachable) + [self _requestSubscriptionList]; + else + _requestSubscriptionList = true; + } + else + { + if (_requestSubscriptionList) + { + _requestSubscriptionList = false; + [self _requestSubscriptionList]; + } + + [self _sendQueuedResponses]; + + if (replyHandler != nil) + replyHandler([NSData data]); + } + + if (completion != nil) + completion(); + + if (finishTask != nil) + finishTask(4.0); + } + else + { + if (completion != nil) + completion(); + if (finishTask != nil) + finishTask(1.0); + } +} + +- (void)_createSubscription:(TGBridgeSubscription *)subscription replyHandler:(void (^)(NSData *))replyHandler finishTask:(void (^)(NSTimeInterval))finishTask completion:(void (^)(void))completion +{ + SSignal *subscriptionHandler = _handler(subscription); + if (replyHandler != nil) + { + os_unfair_lock_lock(&_replyHandlerMapLock); + _replyHandlerMap[@(subscription.identifier)] = replyHandler; + os_unfair_lock_unlock(&_replyHandlerMapLock); + } + + if (subscriptionHandler != nil) + { + [_signalManager startSignalForKey:[NSString stringWithFormat:@"%lld", subscription.identifier] producer:^SSignal * + { + STimer *timer = [[STimer alloc] initWithTimeout:2.0 repeat:false completion:^(__unused STimer *timer) + { + os_unfair_lock_lock(&_replyHandlerMapLock); + void (^reply)(NSData *) = _replyHandlerMap[@(subscription.identifier)]; + if (reply == nil) + { + os_unfair_lock_unlock(&_replyHandlerMapLock); + + if (finishTask != nil) + finishTask(2.0); + return; + } + + reply([NSData data]); + [_replyHandlerMap removeObjectForKey:@(subscription.identifier)]; + os_unfair_lock_unlock(&_replyHandlerMapLock); + + if (finishTask != nil) + finishTask(4.0); + + //TGLog(@"[BridgeServer]: subscription 0x%x hit 2.0s timeout, releasing reply handler", subscription.identifier); + } queue:[SQueue mainQueue]]; + [timer start]; + + return [[SSignal alloc] initWithGenerator:^id(__unused SSubscriber *subscriber) + { + return [subscriptionHandler startWithNext:^(id next) + { + [timer invalidate]; + [self _responseToSubscription:subscription message:next type:TGBridgeResponseTypeNext completion:completion]; + + if (finishTask != nil) + finishTask(4.0); + } error:^(id error) + { + [timer invalidate]; + [self _responseToSubscription:subscription message:error type:TGBridgeResponseTypeFailed completion:completion]; + + if (finishTask != nil) + finishTask(4.0); + } completed:^ + { + [timer invalidate]; + [self _responseToSubscription:subscription message:nil type:TGBridgeResponseTypeCompleted completion:completion]; + + if (finishTask != nil) + finishTask(4.0); + }]; + }]; + }]; + } + else + { + os_unfair_lock_lock(&_replyHandlerMapLock); + void (^reply)(NSData *) = _replyHandlerMap[@(subscription.identifier)]; + if (reply == nil) + { + os_unfair_lock_unlock(&_replyHandlerMapLock); + + if (finishTask != nil) + finishTask(2.0); + return; + } + + reply([NSData data]); + [_replyHandlerMap removeObjectForKey:@(subscription.identifier)]; + os_unfair_lock_unlock(&_replyHandlerMapLock); + + if (finishTask != nil) + finishTask(2.0); + } +} + +- (void)_responseToSubscription:(TGBridgeSubscription *)subscription message:(id)message type:(TGBridgeResponseType)type completion:(void (^)(void))completion +{ + TGBridgeResponse *response = nil; + switch (type) + { + case TGBridgeResponseTypeNext: + response = [TGBridgeResponse single:message forSubscription:subscription]; + break; + + case TGBridgeResponseTypeFailed: + response = [TGBridgeResponse fail:message forSubscription:subscription]; + break; + + case TGBridgeResponseTypeCompleted: + response = [TGBridgeResponse completeForSubscription:subscription]; + break; + + default: + break; + } + + os_unfair_lock_lock(&_replyHandlerMapLock); + void (^reply)(NSData *) = _replyHandlerMap[@(subscription.identifier)]; + if (reply != nil) + [_replyHandlerMap removeObjectForKey:@(subscription.identifier)]; + os_unfair_lock_unlock(&_replyHandlerMapLock); + + if (_processingNotification) + { + [self _enqueueResponse:response forSubscription:subscription]; + + if (completion != nil) + completion(); + + return; + } + + NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:response]; + if (reply != nil && messageData.length < 64000) + { + reply(messageData); + + if (completion != nil) + completion(); + } + else + { + if (reply != nil) + reply([NSData data]); + + if (self.session.isReachable) + { + [self.session sendMessageData:messageData replyHandler:nil errorHandler:^(NSError *error) + { + //if (error != nil) + // TGLog(@"[BridgeServer]: send response for subscription %lld failed with error %@", subscription.identifier, error); + }]; + } + else + { + //TGLog(@"[BridgeServer]: client out of reach, queueing response for subscription %lld", subscription.identifier); + [self _enqueueResponse:response forSubscription:subscription]; + } + + if (completion != nil) + completion(); + } +} + +- (void)_enqueueResponse:(TGBridgeResponse *)response forSubscription:(TGBridgeSubscription *)subscription +{ + os_unfair_lock_lock(&_outgoingQueueLock); + NSMutableArray *updatedResponses = (_outgoingMessageQueue != nil) ? [_outgoingMessageQueue mutableCopy] : [[NSMutableArray alloc] init]; + + if (subscription.dropPreviouslyQueued) + { + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init]; + + [updatedResponses enumerateObjectsUsingBlock:^(TGBridgeResponse *queuedResponse, NSUInteger index, __unused BOOL *stop) + { + if (queuedResponse.subscriptionIdentifier == subscription.identifier) + [indexSet addIndex:index]; + }]; + + [updatedResponses removeObjectsAtIndexes:indexSet]; + } + + [updatedResponses addObject:response]; + + _outgoingMessageQueue = updatedResponses; + os_unfair_lock_unlock(&_outgoingQueueLock); +} + +- (void)_sendQueuedResponses +{ + if (_processingNotification) + return; + + os_unfair_lock_lock(&_outgoingQueueLock); + + if (_outgoingMessageQueue.count > 0) + { + //TGLog(@"[BridgeServer] Sending queued responses"); + + for (TGBridgeResponse *response in _outgoingMessageQueue) + { + NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:response]; + [self.session sendMessageData:messageData replyHandler:nil errorHandler:nil]; + } + + [_outgoingMessageQueue removeAllObjects]; + } + os_unfair_lock_unlock(&_outgoingQueueLock); +} + +- (void)_requestSubscriptionList +{ + TGBridgeSubscriptionListRequest *request = [[TGBridgeSubscriptionListRequest alloc] initWithSessionId:_sessionId]; + NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:request]; + [self.session sendMessageData:messageData replyHandler:nil errorHandler:nil]; +} + +- (void)sendFileWithURL:(NSURL *)url metadata:(NSDictionary *)metadata asMessageData:(bool)asMessageData +{ + //TGLog(@"[BridgeServer] Sent file with metadata %@", metadata); + if (asMessageData && self.session.isReachable) { + NSData *data = [NSData dataWithContentsOfURL:url]; + [self sendFileWithData:data metadata:metadata errorHandler:^{ + [self.session transferFile:url metadata:metadata]; + }]; + } else { + [self.session transferFile:url metadata:metadata]; + } +} + +- (void)sendFileWithData:(NSData *)data metadata:(NSDictionary *)metadata errorHandler:(void (^)(void))errorHandler +{ + TGBridgeFile *file = [[TGBridgeFile alloc] initWithData:data metadata:metadata]; + NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:file]; + [self.session sendMessageData:messageData replyHandler:nil errorHandler:^(NSError *error) { + if (errorHandler != nil) + errorHandler(); + }]; +} + +#pragma mark - Tasks + +- (id)beginTask +{ + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + NSNumber *taskId = @(randomId); + + _runningTasks[taskId] = @true; + [_hasRunningTasks set:[SSignal single:@{@"version": @(_tasksVersion++), @"running": @true}]]; + + SBlockDisposable *taskDisposable = [[SBlockDisposable alloc] initWithBlock:^{ + [_runningTasks removeObjectForKey:taskId]; + [_hasRunningTasks set:[SSignal single:@{@"version": @(_tasksVersion++), @"running": @(_runningTasks.count > 0)}]]; + }]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((4.0) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self dispatch:^{ + [taskDisposable dispose]; + }]; + }); + + return taskDisposable; +} + +#pragma mark - Session Delegate + +- (void)handleReceivedData:(NSData *)messageData replyHandler:(void (^)(NSData *))replyHandler +{ + if (messageData.length == 0) + { + if (replyHandler != nil) + replyHandler([NSData data]); + return; + } + +// __block UIBackgroundTaskIdentifier backgroundTask; +// backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ +// { +// if (replyHandler != nil) +// replyHandler([NSData data]); +// [[UIApplication sharedApplication] endBackgroundTask:backgroundTask]; +// }]; +// + + [self handleMessageData:messageData task:[self beginTask] replyHandler:replyHandler completion:^{}]; +} + +- (void)session:(WCSession *)__unused session didReceiveMessageData:(NSData *)messageData +{ + [self dispatch:^{ + [self handleReceivedData:messageData replyHandler:nil]; + }]; +} + +- (void)session:(WCSession *)__unused session didReceiveMessageData:(NSData *)messageData replyHandler:(void (^)(NSData *))replyHandler +{ + [self dispatch:^{ + [self handleReceivedData:messageData replyHandler:replyHandler]; + }]; +} + +- (void)session:(WCSession *)__unused session didReceiveFile:(WCSessionFile *)file +{ + NSDictionary *metadata = file.metadata; + if (metadata == nil || ![metadata[TGBridgeIncomingFileTypeKey] isEqualToString:TGBridgeIncomingFileTypeAudio]) + return; + + NSError *error; + NSURL *tempURL = [NSURL URLWithString:file.fileURL.lastPathComponent relativeToURL:self.temporaryFilesURL]; + [[NSFileManager defaultManager] createDirectoryAtPath:self.temporaryFilesURL.path withIntermediateDirectories:true attributes:nil error:&error]; + [[NSFileManager defaultManager] moveItemAtURL:file.fileURL toURL:tempURL error:&error]; + + [self dispatch:^{ + _fileHandler(tempURL.path, file.metadata); + }]; +} + +- (void)session:(WCSession *)__unused session didFinishFileTransfer:(WCSessionFileTransfer *)__unused fileTransfer error:(NSError *)__unused error +{ + +} + +- (void)session:(nonnull WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error { + +} + + +- (void)sessionDidBecomeInactive:(nonnull WCSession *)session { + +} + + +- (void)sessionDidDeactivate:(nonnull WCSession *)session { + +} + +- (void)sessionWatchStateDidChange:(WCSession *)session +{ + [self dispatch:^{ + if (session.isWatchAppInstalled) + [self pushContext]; + + _appInstalled.sink(@(session.isWatchAppInstalled)); + }]; +} + +- (void)sessionReachabilityDidChange:(WCSession *)session +{ + NSLog(@"[TGBridgeServer] Reachability changed: %d", session.isReachable); +} + +#pragma mark - + +- (NSInteger)wakeupNetwork +{ + return 0; +} + +- (void)suspendNetworkIfReady:(NSInteger)token +{ +} + +#pragma mark - + +- (WCSession *)session +{ + return [WCSession defaultSession]; +} + +@end + + +@interface TGBridgeSignalManager() +{ + os_unfair_lock _lock; + NSMutableDictionary *_disposables; +} +@end + +@implementation TGBridgeSignalManager + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + _disposables = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)dealloc +{ + NSArray *disposables = nil; + os_unfair_lock_lock(&_lock); + disposables = [_disposables allValues]; + os_unfair_lock_unlock(&_lock); + + for (id disposable in disposables) + { + [disposable dispose]; + } +} + +- (bool)startSignalForKey:(NSString *)key producer:(SSignal *(^)())producer +{ + if (key == nil) + return false; + + bool produce = false; + os_unfair_lock_lock(&_lock); + if (_disposables[key] == nil) + { + _disposables[key] = [[SMetaDisposable alloc] init]; + produce = true; + } + os_unfair_lock_unlock(&_lock); + + if (produce) + { + __weak TGBridgeSignalManager *weakSelf = self; + id disposable = [producer() startWithNext:nil error:^(__unused id error) + { + __strong TGBridgeSignalManager *strongSelf = weakSelf; + if (strongSelf != nil) + { + os_unfair_lock_lock(&strongSelf->_lock); + [strongSelf->_disposables removeObjectForKey:key]; + os_unfair_lock_unlock(&strongSelf->_lock); + } + } completed:^ + { + __strong TGBridgeSignalManager *strongSelf = weakSelf; + if (strongSelf != nil) + { + os_unfair_lock_lock(&strongSelf->_lock); + [strongSelf->_disposables removeObjectForKey:key]; + os_unfair_lock_unlock(&strongSelf->_lock); + } + }]; + + os_unfair_lock_lock(&_lock); + [(SMetaDisposable *)_disposables[key] setDisposable:disposable]; + os_unfair_lock_unlock(&_lock); + } + + return produce; +} + +- (void)haltSignalForKey:(NSString *)key +{ + if (key == nil) + return; + + os_unfair_lock_lock(&_lock); + if (_disposables[key] != nil) + { + [_disposables[key] dispose]; + [_disposables removeObjectForKey:key]; + } + os_unfair_lock_unlock(&_lock); +} + +- (void)haltAllSignals +{ + os_unfair_lock_lock(&_lock); + for (NSObject *disposable in _disposables.allValues) + [disposable dispose]; + [_disposables removeAllObjects]; + os_unfair_lock_unlock(&_lock); +} + +@end diff --git a/submodules/WatchBridge/Sources/WatchCommunicationManager.swift b/submodules/WatchBridge/Sources/WatchCommunicationManager.swift new file mode 100644 index 0000000000..1c69d158e5 --- /dev/null +++ b/submodules/WatchBridge/Sources/WatchCommunicationManager.swift @@ -0,0 +1,219 @@ +import Foundation +import SwiftSignalKit +import Postbox +import TelegramCore +import WatchCommon +import SSignalKit +import TelegramUIPreferences +import AccountContext +import WatchBridgeImpl + +public final class WatchCommunicationManagerContext { + public let context: AccountContext + + public init(context: AccountContext) { + self.context = context + } +} + +public final class WatchManagerArguments { + public let appInstalled: Signal + public let navigateToMessageRequested: Signal + public let runningTasks: Signal + + public init(appInstalled: Signal, navigateToMessageRequested: Signal, runningTasks: Signal) { + self.appInstalled = appInstalled + self.navigateToMessageRequested = navigateToMessageRequested + self.runningTasks = runningTasks + } +} + +public final class WatchCommunicationManager { + private let queue: Queue + private let allowBackgroundTimeExtension: (Double) -> Void + + private var server: TGBridgeServer! + + private let contextDisposable = MetaDisposable() + private let presetsDisposable = MetaDisposable() + + let accountContext = Promise(nil) + private let presets = Promise(nil) + private let navigateToMessagePipe = ValuePipe() + + public init(queue: Queue, context: Signal, allowBackgroundTimeExtension: @escaping (Double) -> Void) { + self.queue = queue + self.allowBackgroundTimeExtension = allowBackgroundTimeExtension + + let handlers = allWatchRequestHandlers.reduce([String : AnyClass]()) { (map, handler) -> [String : AnyClass] in + var map = map + if let handler = handler as? WatchRequestHandler.Type { + for case let subscription as TGBridgeSubscription.Type in handler.handledSubscriptions { + if let name = subscription.subscriptionName() { + map[name] = handler + } + } + } + return map + } + + self.server = TGBridgeServer(handler: { [weak self] subscription -> SSignal? in + guard let strongSelf = self, let subscription = subscription, let handler = handlers[subscription.name] as? WatchRequestHandler.Type else { + return nil + } + return handler.handle(subscription: subscription, manager: strongSelf) + }, fileHandler: { [weak self] path, metadata in + guard let strongSelf = self, let path = path, let metadata = metadata as? [String : Any] else { + return + } + if metadata[TGBridgeIncomingFileTypeKey] as? String == TGBridgeIncomingFileTypeAudio { + let _ = WatchAudioHandler.handleFile(path: path, metadata: metadata, manager: strongSelf).start() + } + }, dispatchOnQueue: { [weak self] block in + if let strongSelf = self { + strongSelf.queue.justDispatch(block) + } + }, logFunction: { value in + if let value = value { + Logger.shared.log("WatchBridge", value) + } + }, allowBackgroundTimeExtension: { + allowBackgroundTimeExtension(4.0) + }) + self.server.startRunning() + + self.contextDisposable.set((combineLatest(self.watchAppInstalled, context |> deliverOn(self.queue))).start(next: { [weak self] appInstalled, appContext in + guard let strongSelf = self, appInstalled else { + return + } + if let context = appContext { + strongSelf.accountContext.set(.single(context.context)) + strongSelf.server.setAuthorized(true, userId: context.context.account.peerId.id._internalGetInt64Value()) + strongSelf.server.setMicAccessAllowed(false) + strongSelf.server.pushContext() + strongSelf.server.setMicAccessAllowed(true) + strongSelf.server.pushContext() + + strongSelf.presets.set(context.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.watchPresetSettings]) + |> map({ sharedData -> WatchPresetSettings in + return sharedData.entries[ApplicationSpecificSharedDataKeys.watchPresetSettings]?.get(WatchPresetSettings.self) ?? WatchPresetSettings.defaultSettings + })) + } else { + strongSelf.accountContext.set(.single(nil)) + strongSelf.server.setAuthorized(false, userId: 0) + strongSelf.server.pushContext() + + strongSelf.presets.set(.single(nil)) + } + })) + + self.presetsDisposable.set((combineLatest(self.watchAppInstalled, self.presets.get() |> distinctUntilChanged |> deliverOn(self.queue), context |> deliverOn(self.queue))).start(next: { [weak self] appInstalled, presets, appContext in + guard let strongSelf = self, let presets = presets, let context = appContext, appInstalled, let tempPath = strongSelf.watchTemporaryStorePath else { + return + } + let presentationData = context.context.sharedContext.currentPresentationData.with { $0 } + let defaultSuggestions: [String : String] = [ + "OK": presentationData.strings.Watch_Suggestion_OK, + "Thanks": presentationData.strings.Watch_Suggestion_Thanks, + "WhatsUp": presentationData.strings.Watch_Suggestion_WhatsUp, + "TalkLater": presentationData.strings.Watch_Suggestion_TalkLater, + "CantTalk": presentationData.strings.Watch_Suggestion_CantTalk, + "HoldOn": presentationData.strings.Watch_Suggestion_HoldOn, + "BRB": presentationData.strings.Watch_Suggestion_BRB, + "OnMyWay": presentationData.strings.Watch_Suggestion_OnMyWay + ] + + var suggestions: [String : String] = [:] + for (key, defaultValue) in defaultSuggestions { + suggestions[key] = presets.customPresets[key] ?? defaultValue + } + + let fileManager = FileManager.default + let presetsFileUrl = URL(fileURLWithPath: tempPath + "/presets.dat") + + if fileManager.fileExists(atPath: presetsFileUrl.path) { + try? fileManager.removeItem(atPath: presetsFileUrl.path) + } + let data = try? NSKeyedArchiver.archivedData(withRootObject: suggestions, requiringSecureCoding: false) + try? data?.write(to: presetsFileUrl) + + let _ = strongSelf.sendFile(url: presetsFileUrl, metadata: [TGBridgeIncomingFileIdentifierKey: "presets"]).start() + })) + } + + deinit { + self.contextDisposable.dispose() + self.presetsDisposable.dispose() + } + + public var arguments: WatchManagerArguments { + return WatchManagerArguments(appInstalled: self.watchAppInstalled, navigateToMessageRequested: self.navigateToMessagePipe.signal(), runningTasks: self.runningTasks) + } + + public func requestNavigateToMessage(messageId: MessageId) { + self.navigateToMessagePipe.putNext(messageId) + } + + private var watchAppInstalled: Signal { + return Signal { subscriber in + let disposable = self.server.watchAppInstalledSignal().start(next: { value in + if let value = value as? NSNumber { + subscriber.putNext(value.boolValue) + } + }) + return ActionDisposable { + disposable?.dispose() + } + } |> deliverOn(self.queue) + } + + private var runningTasks: Signal { + return Signal { subscriber in + let disposable = self.server.runningRequestsSignal().start(next: { value in + if let value = value as? Dictionary { + if let running = value["running"] as? Bool, let version = value["version"] as? Int32 { + subscriber.putNext(WatchRunningTasks(running: running, version: version)) + } + } + }) + return ActionDisposable { + disposable?.dispose() + } + } |> deliverOn(self.queue) + } + + public var watchTemporaryStorePath: String? { + return self.server.temporaryFilesURL?.path + } + + public func sendFile(url: URL, metadata: Dictionary, asMessageData: Bool = false) -> Signal { + return Signal { subscriber in + self.server.sendFile(with: url, metadata: metadata, asMessageData: asMessageData) + subscriber.putCompletion() + return EmptyDisposable + } |> runOn(self.queue) + } + + public func sendFile(data: Data, metadata: Dictionary) -> Signal { + return Signal { subscriber in + self.server.sendFile(with: data, metadata: metadata, errorHandler: {}) + subscriber.putCompletion() + return EmptyDisposable + } |> runOn(self.queue) + } +} + +public func watchCommunicationManager(context: Signal, allowBackgroundTimeExtension: @escaping (Double) -> Void) -> Signal { + return Signal { subscriber in + let queue = Queue() + queue.async { + if #available(iOSApplicationExtension 9.0, *) { + subscriber.putNext(WatchCommunicationManager(queue: queue, context: context, allowBackgroundTimeExtension: allowBackgroundTimeExtension)) + } else { + subscriber.putNext(nil) + } + subscriber.putCompletion() + } + return EmptyDisposable + } +} diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift new file mode 100644 index 0000000000..2292d590eb --- /dev/null +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -0,0 +1,884 @@ +import Foundation +import SwiftSignalKit +import Postbox +import Display +import TelegramCore +import LegacyComponents +import WatchCommon +import TelegramPresentationData +import AvatarNode +import StickerResources +import PhotoResources +import AccountContext +import WatchBridgeAudio + +let allWatchRequestHandlers: [AnyClass] = [ + WatchChatListHandler.self, + WatchChatMessagesHandler.self, + WatchSendMessageHandler.self, + WatchPeerInfoHandler.self, + WatchMediaHandler.self, + WatchStickersHandler.self, + WatchAudioHandler.self, + WatchLocationHandler.self, + WatchPeerSettingsHandler.self, + WatchContinuationHandler.self, +] + +protocol WatchRequestHandler: AnyObject { + static var handledSubscriptions: [Any] { get } + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal +} + +final class WatchChatListHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [TGBridgeChatListSubscription.self] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeChatListSubscription { + let limit = Int(args.limit) + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal<(ChatListView, PresentationData), NoError> in + if let context = context { + return context.account.viewTracker.tailChatListView(groupId: .root, count: limit) + |> map { chatListView, _ -> (ChatListView, PresentationData) in + return (chatListView, context.sharedContext.currentPresentationData.with { $0 }) + } + } else { + return .complete() + } + }) + let disposable = signal.start(next: { chatListView, presentationData in + var chats: [TGBridgeChat] = [] + var users: [Int64 : TGBridgeUser] = [:] + for entry in chatListView.entries.reversed() { + if let (chat, chatUsers) = makeBridgeChat(entry, strings: presentationData.strings) { + chats.append(chat) + users = users.merging(chatUsers, uniquingKeysWith: { (_, last) in last }) + } + } + subscriber.putNext([ TGBridgeChatsArrayKey: chats, TGBridgeUsersDictionaryKey: users ] as [String: Any]) + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else { + return SSignal.fail(nil) + } + } +} + + +final class WatchChatMessagesHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [ + TGBridgeChatMessageListSubscription.self, + TGBridgeChatMessageSubscription.self, + TGBridgeReadChatMessageListSubscription.self + ] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeChatMessageListSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + return SSignal { subscriber in + let limit = Int(args.rangeMessageCount) + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal<(MessageHistoryView, Bool, PresentationData), NoError> in + if let context = context { + return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil) + |> map { messageHistoryView, _, _ -> (MessageHistoryView, Bool, PresentationData) in + return (messageHistoryView, peerId == context.account.peerId, context.sharedContext.currentPresentationData.with { $0 }) + } + } else { + return .complete() + } + }) + let disposable = signal.start(next: { messageHistoryView, savedMessages, presentationData in + var messages: [TGBridgeMessage] = [] + var users: [Int64 : TGBridgeUser] = [:] + for entry in messageHistoryView.entries.reversed() { + if let (message, messageUsers) = makeBridgeMessage(entry, strings: presentationData.strings) { + messages.append(message) + users = users.merging(messageUsers, uniquingKeysWith: { (_, last) in last }) + } + } + subscriber.putNext([ TGBridgeMessagesArrayKey: messages, TGBridgeUsersDictionaryKey: users ] as [String: Any]) + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else if let args = subscription as? TGBridgeReadChatMessageListSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context { + let messageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId) + return context.engine.messages.applyMaxReadIndexInteractively(index: MessageIndex(id: messageId, timestamp: 0)) + } else { + return .complete() + } + }) + let disposable = signal.start(next: { _ in + subscriber.putNext(true) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else if let args = subscription as? TGBridgeChatMessageSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal<(Message, PresentationData)?, NoError> in + if let context = context { + let messageSignal = context.engine.messages.downloadMessage(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId)) + |> map { message -> (Message, PresentationData)? in + if let message = message { + return (message, context.sharedContext.currentPresentationData.with { $0 }) + } else { + return nil + } + } + return messageSignal |> timeout(3.5, queue: Queue.concurrentDefaultQueue(), alternate: .single(nil)) + } else { + return .single(nil) + } + }) + let disposable = signal.start(next: { messageAndPresentationData in + if let (message, presentationData) = messageAndPresentationData, let bridgeMessage = makeBridgeMessage(message, strings: presentationData.strings) { + let peers = makeBridgePeers(message) + var response: [String : Any] = [TGBridgeMessageKey: bridgeMessage, TGBridgeUsersDictionaryKey: peers] + if peerId.namespace != Namespaces.Peer.CloudUser { + response[TGBridgeChatKey] = peers[makeBridgeIdentifier(peerId)] + } + subscriber.putNext(response) + } + subscriber.putCompletion() + }) + return SBlockDisposable { + disposable.dispose() + } + } + } + return SSignal.fail(nil) + } +} + +final class WatchSendMessageHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [ + TGBridgeSendTextMessageSubscription.self, + TGBridgeSendLocationMessageSubscription.self, + TGBridgeSendStickerMessageSubscription.self, + TGBridgeSendForwardedMessageSubscription.self + ] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context { + var messageSignal: Signal<(EnqueueMessage?, PeerId?), NoError>? + if let args = subscription as? TGBridgeSendTextMessageSubscription { + let peerId = makePeerIdFromBridgeIdentifier(args.peerId) + var replyMessageId: MessageId? + if args.replyToMid != 0, let peerId = peerId { + replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.replyToMid) + } + messageSignal = .single((.message(text: args.text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []), peerId)) + } else if let args = subscription as? TGBridgeSendLocationMessageSubscription, let location = args.location { + let peerId = makePeerIdFromBridgeIdentifier(args.peerId) + let map = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, venue: makeVenue(from: location.venue), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil) + messageSignal = .single((.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: map), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []), peerId)) + } else if let args = subscription as? TGBridgeSendStickerMessageSubscription { + let peerId = makePeerIdFromBridgeIdentifier(args.peerId) + messageSignal = mediaForSticker(documentId: args.document.documentId, account: context.account) + |> map({ media -> (EnqueueMessage?, PeerId?) in + if let media = media { + return (.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []), peerId) + } else { + return (nil, nil) + } + }) + } else if let args = subscription as? TGBridgeSendForwardedMessageSubscription { + let peerId = makePeerIdFromBridgeIdentifier(args.targetPeerId) + if let forwardPeerId = makePeerIdFromBridgeIdentifier(args.peerId) { + messageSignal = .single((.forward(source: MessageId(peerId: forwardPeerId, namespace: Namespaces.Message.Cloud, id: args.messageId), threadId: nil, grouping: .none, attributes: [], correlationId: nil), peerId)) + } + } + + if let messageSignal = messageSignal { + return messageSignal |> mapToSignal({ message, peerId -> Signal in + if let message = message, let peerId = peerId { + return enqueueMessages(account: context.account, peerId: peerId, messages: [message]) |> mapToSignal({ _ in + return .single(true) + }) + } else { + return .complete() + } + }) + } + } + return .complete() + }) + + let disposable = signal.start(next: { _ in + subscriber.putNext(true) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } +} + +final class WatchPeerInfoHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [ + TGBridgeUserInfoSubscription.self, + TGBridgeUserBotInfoSubscription.self, + TGBridgeConversationSubscription.self + ] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeUserInfoSubscription { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context, let userId = args.userIds.first as? Int64, let peerId = makePeerIdFromBridgeIdentifier(userId) { + return context.account.viewTracker.peerView(peerId) + } else { + return .complete() + } + }) + let disposable = signal.start(next: { view in + if let user = makeBridgeUser(peerViewMainPeer(view), presence: view.peerPresences[view.peerId], cachedData: view.cachedData) { + subscriber.putNext([user.identifier: user]) + } else { + subscriber.putCompletion() + } + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else if let _ = subscription as? TGBridgeUserBotInfoSubscription { + return SSignal.complete() + } else if let args = subscription as? TGBridgeConversationSubscription { + return SSignal { subscriber in + let signal = manager.accountContext.get() |> take(1) |> mapToSignal({ context -> Signal in + if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + return context.account.viewTracker.peerView(peerId) + } else { + return .complete() + } + }) + let disposable = signal.start(next: { view in + let (chat, users) = makeBridgeChat(peerViewMainPeer(view), view: view) + subscriber.putNext([ TGBridgeChatKey: chat, TGBridgeUsersDictionaryKey: users ] as [String: Any]) + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } + return SSignal.fail(nil) + } +} + +private func mediaForSticker(documentId: Int64, account: Account) -> Signal { + return account.postbox.itemCollectionsView(orderedItemListCollectionIds: [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers], namespaces: [Namespaces.ItemCollection.CloudStickerPacks], aroundIndex: nil, count: 50) + |> take(1) + |> map { view -> TelegramMediaFile? in + for view in view.orderedItemListsViews { + for entry in view.items { + if let file = entry.contents.get(SavedStickerItem.self)?.file { + if file.id.id == documentId { + return file._parse() + } + } else if let file = entry.contents.get(RecentMediaItem.self)?.media { + if file.id.id == documentId { + return file._parse() + } + } + } + } + return nil + } +} + +private let roundCorners = { () -> UIImage in + let diameter: CGFloat = 44.0 + UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0) + let context = UIGraphicsGetCurrentContext()! + context.setBlendMode(.copy) + context.setFillColor(UIColor.black.cgColor) + context.fill(CGRect(origin: CGPoint(), size: CGSize(width: diameter, height: diameter))) + context.setBlendMode(.clear) + context.setFillColor(UIColor.clear.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: diameter, height: diameter))) + let image = UIGraphicsGetImageFromCurrentImageContext()!.stretchableImage(withLeftCapWidth: Int(diameter / 2.0), topCapHeight: Int(diameter / 2.0)) + UIGraphicsEndImageContext() + return image +}() + +private func sendData(manager: WatchCommunicationManager, data: Data, key: String, ext: String, type: String, forceAsData: Bool = false) { + if let tempPath = manager.watchTemporaryStorePath, !forceAsData { + let tempFileUrl = URL(fileURLWithPath: tempPath + "/\(key)\(ext)") + let _ = try? data.write(to: tempFileUrl) + let _ = manager.sendFile(url: tempFileUrl, metadata: [TGBridgeIncomingFileTypeKey: type, TGBridgeIncomingFileIdentifierKey: key]).start() + } else { + let _ = manager.sendFile(data: data, metadata: [TGBridgeIncomingFileTypeKey: type, TGBridgeIncomingFileIdentifierKey: key]).start() + } +} + +final class WatchMediaHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [ + TGBridgeMediaThumbnailSubscription.self, + TGBridgeMediaAvatarSubscription.self, + TGBridgeMediaStickerSubscription.self + ] + } + + static private let disposable = DisposableSet() + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeMediaAvatarSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + let key = "\(args.url!)_\(args.type.rawValue)" + let targetSize: CGSize + var compressionRate: CGFloat = 0.5 + var round = false + switch args.type { + case .small: + targetSize = CGSize(width: 19, height: 19); + compressionRate = 0.5 + case .profile: + targetSize = CGSize(width: 44, height: 44); + round = true + case .large: + targetSize = CGSize(width: 150, height: 150); + @unknown default: + fatalError() + } + + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context { + return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> mapToSignal { peer -> Signal in + if let peer = peer, case let .secretChat(secretChat) = peer { + return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: secretChat.regularPeerId)) + } else { + return .single(peer) + } + } + |> mapToSignal({ peer -> Signal in + if let peer = peer, let representation = peer.smallProfileImage { + let imageData = peerAvatarImageData(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: representation, synchronousLoad: false) + if let imageData = imageData { + return imageData + |> map { data -> UIImage? in + if let (data, _) = data, let image = generateImage(targetSize, contextGenerator: { size, context -> Void in + if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) { + context.setBlendMode(.copy) + context.draw(dataImage, in: CGRect(origin: CGPoint(), size: targetSize)) + if round { + context.setBlendMode(.normal) + context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: targetSize)) + } + } + }, scale: 2.0) { + return image + } + return nil + } + } + } + return .single(nil) + }) + } else { + return .complete() + } + }) + + let disposable = signal.start(next: { image in + if let image = image, let imageData = image.jpegData(compressionQuality: compressionRate) { + sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: true) + } + subscriber.putNext(key) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else if let args = subscription as? TGBridgeMediaStickerSubscription { + let key = "sticker_\(args.documentId)_\(Int(args.size.width))x\(Int(args.size.height))_\(args.notification ? 1 : 0)" + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context { + var mediaSignal: Signal<(TelegramMediaFile, FileMediaReference)?, NoError>? = nil + if args.stickerPackId != 0 { + mediaSignal = mediaForSticker(documentId: args.documentId, account: context.account) + |> map { media -> (TelegramMediaFile, FileMediaReference)? in + if let media = media { + return (media, .standalone(media: media)) + } else { + return nil + } + } + } else if args.stickerPeerId != 0, let peerId = makePeerIdFromBridgeIdentifier(args.stickerPeerId) { + mediaSignal = context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.stickerMessageId))) + |> map { message -> (TelegramMediaFile, FileMediaReference)? in + if let message = message { + for media in message.media { + if let media = media as? TelegramMediaFile { + return (media, .message(message: MessageReference(message._asMessage()), media: media)) + } + } + } + return nil + } + } + var size: CGSize = args.size + if let mediaSignal = mediaSignal { + return mediaSignal + |> mapToSignal { mediaAndFileReference -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in + if let (media, fileReference) = mediaAndFileReference { + if let dimensions = media.dimensions { + size = dimensions.cgSize + } + self.disposable.add(freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).start()) + return chatMessageSticker(account: context.account, userLocation: .other, file: media, small: false, fetched: true, onlyFullSize: true) + } + return .complete() + } + |> map{ f -> UIImage? in + let context = f(TransformImageArguments(corners: ImageCorners(), imageSize: size.fitted(args.size), boundingSize: args.size, intrinsicInsets: UIEdgeInsets(), emptyColor: args.notification ? UIColor(rgb: 0xe5e5ea) : .black, scale: 2.0)) + return context?.generateImage() + } + } + } + return .complete() + }) + + let disposable = signal.start(next: { image in + if let image = image, let imageData = image.jpegData(compressionQuality: 0.2) { + sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: args.notification) + } + subscriber.putNext(key) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else if let args = subscription as? TGBridgeMediaThumbnailSubscription { + let key = "\(args.peerId)_\(args.messageId)" + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + var roundVideo = false + return context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId))) + |> mapToSignal { message -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in + if let message = message, !message._asMessage().containsSecretMedia { + var imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? + var updatedMediaReference: AnyMediaReference? + var candidateMediaReference: AnyMediaReference? + var imageDimensions: CGSize? + for media in message.media { + if let image = media as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource { + self.disposable.add(messageMediaImageInteractiveFetched(context: context, message: message._asMessage(), image: image, resource: resource, storeToDownloadsPeerId: nil).start()) + candidateMediaReference = .message(message: MessageReference(message._asMessage()), media: media) + break + } else if let _ = media as? TelegramMediaFile { + candidateMediaReference = .message(message: MessageReference(message._asMessage()), media: media) + break + } else if let webPage = media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content, let image = content.image, let resource = largestImageRepresentation(image.representations)?.resource { + self.disposable.add(messageMediaImageInteractiveFetched(context: context, message: message._asMessage(), image: image, resource: resource, storeToDownloadsPeerId: nil).start()) + candidateMediaReference = .webPage(webPage: WebpageReference(webPage), media: image) + break + } + } + if let imageReference = candidateMediaReference?.concrete(TelegramMediaImage.self) { + updatedMediaReference = imageReference.abstract + if let representation = largestRepresentationForPhoto(imageReference.media) { + imageDimensions = representation.dimensions.cgSize + } + } else if let fileReference = candidateMediaReference?.concrete(TelegramMediaFile.self) { + updatedMediaReference = fileReference.abstract + if let representation = largestImageRepresentation(fileReference.media.previewRepresentations), !fileReference.media.isSticker { + imageDimensions = representation.dimensions.cgSize + } + } + if let updatedMediaReference = updatedMediaReference, imageDimensions != nil { + if let imageReference = updatedMediaReference.concrete(TelegramMediaImage.self) { + imageSignal = chatMessagePhotoThumbnail(account: context.account, userLocation: .other, photoReference: imageReference, onlyFullSize: true) + } else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) { + if fileReference.media.isVideo { + imageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .other, fileReference: fileReference) + roundVideo = fileReference.media.isInstantVideo + } else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) { + imageSignal = chatWebpageSnippetFile(account: context.account, userLocation: .other, mediaReference: fileReference.abstract, representation: iconImageRepresentation) + } + } + } + if let signal = imageSignal { + return signal + } + } + return .complete() + } |> map{ f -> UIImage? in + var insets = UIEdgeInsets() + if roundVideo { + insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2) + } + let context = f(TransformImageArguments(corners: ImageCorners(), imageSize: args.size, boundingSize: args.size, intrinsicInsets: insets, scale: 2.0)) + return context?.generateImage() + } + } else { + return .complete() + } + }) + + let disposable = signal.start(next: { image in + if let image = image, let imageData = image.jpegData(compressionQuality: 0.5) { + sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: args.notification) + } + subscriber.putNext(key) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } + return SSignal.fail(nil) + } +} + +final class WatchStickersHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [TGBridgeRecentStickersSubscription.self] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeRecentStickersSubscription { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context { + return context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers], namespaces: [Namespaces.ItemCollection.CloudStickerPacks], aroundIndex: nil, count: 50) |> take(1) + } else { + return .complete() + } + }) + let disposable = signal.start(next: { view in + var stickers: [TGBridgeDocumentMediaAttachment] = [] + var added: Set = [] + outer: for view in view.orderedItemListsViews { + for entry in view.items { + if let file = entry.contents.get(SavedStickerItem.self)?.file { + if let sticker = makeBridgeDocument(file._parse()), !added.contains(sticker.documentId) { + stickers.append(sticker) + added.insert(sticker.documentId) + } + } else if let file = entry.contents.get(RecentMediaItem.self)?.media { + if let sticker = makeBridgeDocument(file._parse()), !added.contains(sticker.documentId) { + stickers.append(sticker) + added.insert(sticker.documentId) + } + } + if stickers.count == args.limit { + break outer + } + } + } + subscriber.putNext(stickers) + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } + return SSignal.fail(nil) + } +} + +final class WatchAudioHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [ + TGBridgeAudioSubscription.self, + TGBridgeAudioSentSubscription.self + ] + } + + static private let disposable = DisposableSet() + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeAudioSubscription { + let key = "audio_\(args.peerId)_\(args.messageId)" + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + return context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId))) + |> mapToSignal { message -> Signal in + if let message = message { + for media in message.media { + if let file = media as? TelegramMediaFile { + self.disposable.add(messageMediaFileInteractiveFetched(context: context, message: message._asMessage(), file: file, userInitiated: true).start()) + return context.account.postbox.mediaBox.resourceData(file.resource) + |> mapToSignal({ data -> Signal in + if let tempPath = manager.watchTemporaryStorePath, data.complete { + let outputPath = tempPath + "/\(key).m4a" + return legacyDecodeOpusAudio(path: data.path, outputPath: outputPath) + } else { + return .complete() + } + }) + } + } + } + return .complete() + } + } else { + return .complete() + } + }) + + let disposable = signal.start(next: { path in + let _ = manager.sendFile(url: URL(fileURLWithPath: path), metadata: [TGBridgeIncomingFileTypeKey: TGBridgeIncomingFileTypeAudio, TGBridgeIncomingFileIdentifierKey: key]).start() + subscriber.putNext(key) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + //let outputPath = manager.watchTemporaryStorePath + "/\(key).opus" + } else if let _ = subscription as? TGBridgeAudioSentSubscription { + + } + return SSignal.fail(nil) + } + + static func handleFile(path: String, metadata: Dictionary, manager: WatchCommunicationManager) -> Signal { + let randomId = metadata[TGBridgeIncomingFileRandomIdKey] as? Int64 + let peerId = metadata[TGBridgeIncomingFilePeerIdKey] as? Int64 + let replyToMid = metadata[TGBridgeIncomingFileReplyToMidKey] as? Int32 + + if let randomId = randomId, let id = peerId, let peerId = makePeerIdFromBridgeIdentifier(id) { + return combineLatest(manager.accountContext.get() |> take(1), legacyEncodeOpusAudio(path: path)) + |> map({ context, pathAndDuration -> Void in + let (path, duration) = pathAndDuration + if let context = context, let path = path, let data = try? Data(contentsOf: URL(fileURLWithPath: path)) { + let resource = LocalFileMediaResource(fileId: randomId) + context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) + + var replyMessageId: MessageId? = nil + if let replyToMid = replyToMid, replyToMid != 0 { + replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMid) + } + + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.count), attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)], alternativeRepresentations: [])), threadId: nil, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).start() + } + }) + } else { + return .complete() + } + } +} + +final class WatchLocationHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [TGBridgeNearbyVenuesSubscription.self] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeNearbyVenuesSubscription { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal<[ChatContextResultMessage], NoError> in + if let context = context { + return context.engine.peers.resolvePeerByName(name: "foursquare", referrer: nil) + |> mapToSignal { result -> Signal in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + |> take(1) + |> mapToSignal { peer -> Signal in + guard let peer = peer?._asPeer() else { + return .single(nil) + } + return context.engine.messages.requestChatContextResults(botId: peer.id, peerId: context.account.peerId, query: "", location: .single((args.coordinate.latitude, args.coordinate.longitude)), offset: "") + |> map { results -> ChatContextResultCollection? in + return results?.results + } + |> `catch` { error -> Signal in + return .single(nil) + } + } + |> mapToSignal { contextResult -> Signal<[ChatContextResultMessage], NoError> in + guard let contextResult = contextResult else { + return .single([]) + } + return .single(contextResult.results.map { $0.message }) + } + } else { + return .complete() + } + }) + + let disposable = signal.start(next: { results in + var venues: [TGBridgeLocationVenue] = [] + for result in results { + if let venue = makeBridgeLocationVenue(result) { + venues.append(venue) + } + } + subscriber.putNext(venues) + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } + return SSignal.fail(nil) + } +} + +final class WatchPeerSettingsHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [ + TGBridgePeerSettingsSubscription.self, + TGBridgePeerUpdateNotificationSettingsSubscription.self, + TGBridgePeerUpdateBlockStatusSubscription.self + ] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgePeerSettingsSubscription { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + return context.account.viewTracker.peerView(peerId) + } else { + return .complete() + } + }) + let disposable = signal.start(next: { view in + var muted = false + var blocked = false + + if let notificationSettings = view.notificationSettings as? TelegramPeerNotificationSettings, case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + muted = true + } + if let cachedData = view.cachedData as? CachedUserData { + blocked = cachedData.isBlocked + } + + subscriber.putNext([ "muted": muted, "blocked": blocked ]) + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } else { + return SSignal { subscriber in + let signal = manager.accountContext.get() + |> take(1) + |> mapToSignal({ context -> Signal in + if let context = context { + var signal: Signal? + + if let args = subscription as? TGBridgePeerUpdateNotificationSettingsSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + signal = context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil) + } else if let args = subscription as? TGBridgePeerUpdateBlockStatusSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + signal = context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peerId, isBlocked: args.blocked) + } + + if let signal = signal { + return signal |> mapToSignal({ _ in + return .single(true) + }) + } else { + return .complete() + } + } else { + return .complete() + } + }) + + let disposable = signal.start(next: { _ in + subscriber.putNext(true) + }, completed: { + subscriber.putCompletion() + }) + + return SBlockDisposable { + disposable.dispose() + } + } + } + } +} + +final class WatchContinuationHandler: WatchRequestHandler { + static var handledSubscriptions: [Any] { + return [TGBridgeRemoteSubscription.self] + } + + static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal { + if let args = subscription as? TGBridgeRemoteSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) { + manager.requestNavigateToMessage(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId)) + } + return SSignal.fail(nil) + } +} diff --git a/submodules/WatchBridgeAudio/BUILD b/submodules/WatchBridgeAudio/BUILD new file mode 100644 index 0000000000..e3994dc62d --- /dev/null +++ b/submodules/WatchBridgeAudio/BUILD @@ -0,0 +1,19 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "WatchBridgeAudio", + module_name = "WatchBridgeAudio", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/WatchBridgeAudio/Impl:WatchBridgeAudioImpl", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WatchBridgeAudio/Impl/BUILD b/submodules/WatchBridgeAudio/Impl/BUILD new file mode 100644 index 0000000000..b1290acaa1 --- /dev/null +++ b/submodules/WatchBridgeAudio/Impl/BUILD @@ -0,0 +1,27 @@ + +objc_library( + name = "WatchBridgeAudioImpl", + enable_modules = True, + module_name = "WatchBridgeAudioImpl", + srcs = glob([ + "Sources/**/*.m", + "Sources/**/*.mm", + "Sources/**/*.h", + ], allow_empty=True), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + ], + deps = [ + "//submodules/SSignalKit/SSignalKit:SSignalKit", + "//submodules/OpusBinding:OpusBinding", + ], + sdk_frameworks = [ + "Foundation", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/TGBridgeAudioDecoder.h b/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/TGBridgeAudioDecoder.h new file mode 100644 index 0000000000..9332bf611b --- /dev/null +++ b/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/TGBridgeAudioDecoder.h @@ -0,0 +1,8 @@ +#import + +@interface TGBridgeAudioDecoder : NSObject + +- (instancetype)initWithURL:(NSURL *)url outputUrl:(NSURL *)outputURL; +- (void)startWithCompletion:(void (^)(void))completion; + +@end diff --git a/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/TGBridgeAudioEncoder.h b/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/TGBridgeAudioEncoder.h new file mode 100644 index 0000000000..7c186daff4 --- /dev/null +++ b/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/TGBridgeAudioEncoder.h @@ -0,0 +1,8 @@ +#import + +@interface TGBridgeAudioEncoder : NSObject + +- (instancetype)initWithURL:(NSURL *)url; +- (void)startWithCompletion:(void (^)(NSString *, int32_t))completion; + +@end diff --git a/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/WatchBridgeAudioImpl.h b/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/WatchBridgeAudioImpl.h new file mode 100644 index 0000000000..03950d978a --- /dev/null +++ b/submodules/WatchBridgeAudio/Impl/PublicHeaders/WatchBridgeAudioImpl/WatchBridgeAudioImpl.h @@ -0,0 +1,6 @@ +#import + +#import +#import + + diff --git a/submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioDecoder.mm b/submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioDecoder.mm new file mode 100644 index 0000000000..c1d30b80f1 --- /dev/null +++ b/submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioDecoder.mm @@ -0,0 +1,197 @@ +#import + +#import +#import + +#import + +#import + +const NSInteger TGBridgeAudioDecoderInputSampleRate = 48000; +const NSInteger TGBridgeAudioDecoderResultSampleRate = 24000; +const NSUInteger TGBridgeAudioDecoderBufferSize = 32768; + +#define checkResult(result,operation) (_checkResultLite((result),(operation),__FILE__,__LINE__)) + +struct TGAudioBuffer +{ + NSUInteger capacity; + uint8_t *data; + NSUInteger size; + int64_t pcmOffset; +}; + +inline TGAudioBuffer *TGAudioBufferWithCapacity(NSUInteger capacity) +{ + TGAudioBuffer *audioBuffer = (TGAudioBuffer *)malloc(sizeof(TGAudioBuffer)); + audioBuffer->capacity = capacity; + audioBuffer->data = (uint8_t *)malloc(capacity); + audioBuffer->size = 0; + audioBuffer->pcmOffset = 0; + return audioBuffer; +} + +inline void TGAudioBufferDispose(TGAudioBuffer *audioBuffer) +{ + if (audioBuffer != NULL) + { + free(audioBuffer->data); + free(audioBuffer); + } +} + +static inline bool _checkResultLite(OSStatus result, const char *operation, const char* file, int line) +{ + if ( result != noErr ) + { + NSLog(@"%s:%d: %s result %d %08X %4.4s\n", file, line, operation, (int)result, (int)result, (char*)&result); + return NO; + } + return YES; +} + +@interface TGBridgeAudioDecoder () +{ + NSURL *_url; + NSURL *_resultURL; + + OggOpusReader *_opusReader; + + bool _finished; + bool _cancelled; +} +@end + +@implementation TGBridgeAudioDecoder + +- (instancetype)initWithURL:(NSURL *)url outputUrl:(NSURL *)outputUrl +{ + self = [super init]; + if (self != nil) + { + _url = url; + + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + _resultURL = outputUrl; + } + return self; +} + +- (void)startWithCompletion:(void (^)(void))completion +{ + [[TGBridgeAudioDecoder processingQueue] dispatch:^ + { + _opusReader = [[OggOpusReader alloc] initWithPath:_url.path]; + if (_opusReader == NULL) { + return; + } + + AudioStreamBasicDescription sourceFormat; + sourceFormat.mSampleRate = TGBridgeAudioDecoderInputSampleRate; + sourceFormat.mFormatID = kAudioFormatLinearPCM; + sourceFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + sourceFormat.mFramesPerPacket = 1; + sourceFormat.mChannelsPerFrame = 1; + sourceFormat.mBitsPerChannel = 16; + sourceFormat.mBytesPerPacket = 2; + sourceFormat.mBytesPerFrame = 2; + + AudioStreamBasicDescription destFormat; + memset(&destFormat, 0, sizeof(destFormat)); + destFormat.mChannelsPerFrame = sourceFormat.mChannelsPerFrame; + destFormat.mFormatID = kAudioFormatMPEG4AAC; + destFormat.mSampleRate = TGBridgeAudioDecoderResultSampleRate; + UInt32 size = sizeof(destFormat); + if (!checkResult(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destFormat), + "AudioFormatGetProperty(kAudioFormatProperty_FormatInfo)")) + { + return; + } + + ExtAudioFileRef destinationFile; + if (!checkResult(ExtAudioFileCreateWithURL((__bridge CFURLRef)_resultURL, kAudioFileM4AType, &destFormat, NULL, kAudioFileFlags_EraseFile, &destinationFile), "ExtAudioFileCreateWithURL")) + { + return; + } + + if (!checkResult(ExtAudioFileSetProperty(destinationFile, kExtAudioFileProperty_ClientDataFormat, size, &sourceFormat), + "ExtAudioFileSetProperty(destinationFile, kExtAudioFileProperty_ClientDataFormat")) + { + return; + } + + bool canResumeAfterInterruption = false; + AudioConverterRef converter; + size = sizeof(converter); + if (checkResult(ExtAudioFileGetProperty(destinationFile, kExtAudioFileProperty_AudioConverter, &size, &converter), + "ExtAudioFileGetProperty(kExtAudioFileProperty_AudioConverter;)")) + { + UInt32 canResume = 0; + size = sizeof(canResume); + if (AudioConverterGetProperty(converter, kAudioConverterPropertyCanResumeFromInterruption, &size, &canResume) == noErr) + canResumeAfterInterruption = canResume; + } + + uint8_t srcBuffer[TGBridgeAudioDecoderBufferSize]; + while (!_cancelled) + { + AudioBufferList bufferList; + bufferList.mNumberBuffers = 1; + bufferList.mBuffers[0].mNumberChannels = sourceFormat.mChannelsPerFrame; + bufferList.mBuffers[0].mDataByteSize = TGBridgeAudioDecoderBufferSize; + bufferList.mBuffers[0].mData = srcBuffer; + + uint32_t writtenOutputBytes = 0; + while (writtenOutputBytes < TGBridgeAudioDecoderBufferSize) + { + int32_t readSamples = [_opusReader read:(uint16_t *)(srcBuffer + writtenOutputBytes) bufSize:(TGBridgeAudioDecoderBufferSize - writtenOutputBytes) / sourceFormat.mBytesPerFrame]; + + if (readSamples > 0) + writtenOutputBytes += readSamples * sourceFormat.mBytesPerFrame; + else + break; + } + bufferList.mBuffers[0].mDataByteSize = writtenOutputBytes; + int32_t nFrames = writtenOutputBytes / sourceFormat.mBytesPerFrame; + + if (nFrames == 0) + break; + + OSStatus status = ExtAudioFileWrite(destinationFile, nFrames, &bufferList); + if (status == kExtAudioFileError_CodecUnavailableInputConsumed) + { + //TGLog(@"1"); + } + else if (status == kExtAudioFileError_CodecUnavailableInputNotConsumed) + { + //TGLog(@"2"); + } + else if (!checkResult(status, "ExtAudioFileWrite")) + { + //TGLog(@"3"); + } + } + + ExtAudioFileDispose(destinationFile); + + if (completion != nil) + completion(); + }]; +} + ++ (SQueue *)processingQueue +{ + static SQueue *queue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + static const char *queueSpecific = "org.telegram.opusAudioDecoderQueue"; + dispatch_queue_t dispatchQueue = dispatch_queue_create("org.telegram.opusAudioDecoderQueue", DISPATCH_QUEUE_SERIAL); + dispatch_queue_set_specific(dispatchQueue, queueSpecific, (void *)queueSpecific, NULL); + queue = [SQueue wrapConcurrentNativeQueue:dispatchQueue]; + }); + return queue; +} + +@end diff --git a/submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioEncoder.m b/submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioEncoder.m new file mode 100644 index 0000000000..2bf80914d3 --- /dev/null +++ b/submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioEncoder.m @@ -0,0 +1,564 @@ +#import +#import + +#import + +static const char *AMQueueSpecific = "AMQueueSpecific"; + +const NSInteger TGBridgeAudioEncoderSampleRate = 48000; + +typedef enum { + ATQueuePriorityLow, + ATQueuePriorityDefault, + ATQueuePriorityHigh +} ATQueuePriority; + +@interface ATQueue : NSObject + ++ (ATQueue *)mainQueue; ++ (ATQueue *)concurrentDefaultQueue; ++ (ATQueue *)concurrentBackgroundQueue; + +- (instancetype)init; +- (instancetype)initWithName:(NSString *)name; +- (instancetype)initWithPriority:(ATQueuePriority)priority; + +- (void)dispatch:(dispatch_block_t)block; +- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous; +- (void)dispatchAfter:(NSTimeInterval)seconds block:(dispatch_block_t)block; + +- (dispatch_queue_t)nativeQueue; + +@end + +@interface TGFileDataItem : TGDataItem + +- (instancetype)initWithTempFile; + +- (void)appendData:(NSData *)data; +- (NSData *)readDataAtOffset:(NSUInteger)offset length:(NSUInteger)length; +- (NSUInteger)length; + +- (NSString *)path; + +@end + +@interface TGBridgeAudioEncoder () +{ + AVAssetReader *_assetReader; + AVAssetReaderOutput *_readerOutput; + + NSMutableData *_audioBuffer; + TGFileDataItem *_tempFileItem; + TGOggOpusWriter *_oggWriter; + + int _tailLength; +} +@end + +@implementation TGBridgeAudioEncoder + +- (instancetype)initWithURL:(NSURL *)url +{ + self = [super init]; + if (self != nil) + { + AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil]; + if (asset == nil || asset.tracks.count == 0) + { + return nil; + } + + NSError *error; + _assetReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; + + NSDictionary *outputSettings = @ + { + AVFormatIDKey: @(kAudioFormatLinearPCM), + AVSampleRateKey: @(TGBridgeAudioEncoderSampleRate), + AVNumberOfChannelsKey: @1, + AVLinearPCMBitDepthKey: @16, + AVLinearPCMIsFloatKey: @false, + AVLinearPCMIsBigEndianKey: @false, + AVLinearPCMIsNonInterleaved: @false + }; + + _readerOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:asset.tracks audioSettings:outputSettings]; + + [_assetReader addOutput:_readerOutput]; + + _tempFileItem = [[TGFileDataItem alloc] initWithTempFile]; + } + return self; +} + +- (void)dealloc +{ + [self cleanup]; +} + +- (void)cleanup +{ + _oggWriter = nil; +} + ++ (ATQueue *)processingQueue +{ + static ATQueue *queue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + queue = [[ATQueue alloc] initWithName:@"org.telegram.opusAudioEncoderQueue"]; + }); + + return queue; +} + +static const int encoderPacketSizeInBytes = 16000 / 1000 * 60 * 2; + +- (void)startWithCompletion:(void (^)(NSString *, int32_t))completion +{ + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + _oggWriter = [[TGOggOpusWriter alloc] init]; + if (![_oggWriter beginWithDataItem:_tempFileItem]) + { + [self cleanup]; + return; + } + + [_assetReader startReading]; + + while (_assetReader.status != AVAssetReaderStatusCompleted) + { + if (_assetReader.status == AVAssetReaderStatusReading) + { + CMSampleBufferRef nextBuffer = [_readerOutput copyNextSampleBuffer]; + if (nextBuffer) + { + AudioBufferList abl; + CMBlockBufferRef blockBuffer; + CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); + + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + [self _processBuffer:&abl.mBuffers[0]]; + + CFRelease(nextBuffer); + CFRelease(blockBuffer); + }]; + } + else + { + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + if (_tailLength > 0) { + [_oggWriter writeFrame:(uint8_t *)_audioBuffer.bytes frameByteCount:(NSUInteger)_tailLength]; + } + }]; + break; + } + } + } + + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + TGFileDataItem *dataItemResult = nil; + NSTimeInterval durationResult = 0.0; + + NSUInteger totalBytes = 0; + + if (_assetReader.status == AVAssetReaderStatusCompleted) + { + NSLog(@"finished"); + if (_oggWriter != nil && [_oggWriter writeFrame:NULL frameByteCount:0]) + { + dataItemResult = _tempFileItem; + durationResult = [_oggWriter encodedDuration]; + totalBytes = [_oggWriter encodedBytes]; + } + + [self cleanup]; + } + + //TGLog(@"[TGBridgeAudioEncoder#%x convert time: %f ms]", self, (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); + + if (completion != nil) + completion(dataItemResult.path, (int32_t)durationResult); + }]; + }]; +} + +- (void)_processBuffer:(AudioBuffer const *)buffer +{ + @autoreleasepool + { + if (_oggWriter == nil) + return; + + unsigned char currentEncoderPacket[encoderPacketSizeInBytes]; + + int bufferOffset = 0; + + while (true) + { + int currentEncoderPacketSize = 0; + + while (currentEncoderPacketSize < encoderPacketSizeInBytes) + { + if (_audioBuffer.length != 0) + { + int takenBytes = MIN((int)_audioBuffer.length, encoderPacketSizeInBytes - currentEncoderPacketSize); + if (takenBytes != 0) + { + memcpy(currentEncoderPacket + currentEncoderPacketSize, _audioBuffer.bytes, takenBytes); + [_audioBuffer replaceBytesInRange:NSMakeRange(0, takenBytes) withBytes:NULL length:0]; + currentEncoderPacketSize += takenBytes; + } + } + else if (bufferOffset < (int)buffer->mDataByteSize) + { + int takenBytes = MIN((int)buffer->mDataByteSize - bufferOffset, encoderPacketSizeInBytes - currentEncoderPacketSize); + if (takenBytes != 0) + { + memcpy(currentEncoderPacket + currentEncoderPacketSize, ((const char *)buffer->mData) + bufferOffset, takenBytes); + bufferOffset += takenBytes; + currentEncoderPacketSize += takenBytes; + } + } + else { + break; + } + } + _tailLength = currentEncoderPacketSize; + if (currentEncoderPacketSize < encoderPacketSizeInBytes) + { + if (_audioBuffer == nil) + _audioBuffer = [[NSMutableData alloc] initWithCapacity:encoderPacketSizeInBytes]; + [_audioBuffer appendBytes:currentEncoderPacket length:currentEncoderPacketSize]; + break; + } + else + { + [_oggWriter writeFrame:currentEncoderPacket frameByteCount:(NSUInteger)currentEncoderPacketSize]; + _tailLength = 0; + } + } + } +} + +@end + +@interface TGFileDataItem () +{ + NSUInteger _length; + + NSString *_fileName; + bool _fileExists; + + NSMutableData *_data; +} + +@end + +@implementation TGFileDataItem +{ + ATQueue *_queue; +} + +- (void)_commonInit +{ + _queue = [[ATQueue alloc] initWithPriority:ATQueuePriorityLow]; + _data = [[NSMutableData alloc] init]; +} + +- (instancetype)initWithTempFile +{ + self = [super init]; + if (self != nil) + { + [self _commonInit]; + + [_queue dispatch:^ + { + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + _fileName = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%" PRIx64 "", randomId]]; + _fileExists = false; + }]; + } + return self; +} + +- (instancetype)initWithFilePath:(NSString *)filePath +{ + self = [super init]; + if (self != nil) + { + [self _commonInit]; + + + [_queue dispatch:^ + { + _fileName = filePath; + _length = [[[NSFileManager defaultManager] attributesOfItemAtPath:_fileName error:nil][NSFileSize] unsignedIntegerValue]; + _fileExists = [[NSFileManager defaultManager] fileExistsAtPath:_fileName]; + }]; + } + return self; +} + +- (void)noop +{ +} + +- (void)moveToPath:(NSString *)path +{ + [_queue dispatch:^ + { + [[NSFileManager defaultManager] moveItemAtPath:_fileName toPath:path error:nil]; + _fileName = path; + }]; +} + +- (void)remove +{ + [_queue dispatch:^ + { + [[NSFileManager defaultManager] removeItemAtPath:_fileName error:nil]; + }]; +} + +- (void)appendData:(NSData *)data +{ + [_queue dispatch:^ + { + if (!_fileExists) + { + [[NSFileManager defaultManager] createFileAtPath:_fileName contents:nil attributes:nil]; + _fileExists = true; + } + NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName]; + [file seekToEndOfFile]; + [file writeData:data]; + [file synchronizeFile]; + [file closeFile]; + _length += data.length; + + [_data appendData:data]; + }]; +} + +- (NSData *)readDataAtOffset:(NSUInteger)offset length:(NSUInteger)length +{ + __block NSData *data = nil; + + [_queue dispatch:^ + { + NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName]; + [file seekToFileOffset:(unsigned long long)offset]; + data = [file readDataOfLength:length]; + if (data.length != length) + //TGLog(@"Read data length mismatch"); + [file closeFile]; + } synchronous:true]; + + return data; +} + +- (NSUInteger)length +{ + __block NSUInteger result = 0; + [_queue dispatch:^ + { + result = _length; + } synchronous:true]; + + return result; +} + +- (NSString *)path { + return _fileName; +} + +@end + + +@interface ATQueue () +{ + dispatch_queue_t _nativeQueue; + bool _isMainQueue; + + int32_t _noop; +} + +@end + +@implementation ATQueue + ++ (NSString *)applicationPrefix +{ + static NSString *prefix = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + prefix = [[NSBundle mainBundle] bundleIdentifier]; + }); + + return prefix; +} + ++ (ATQueue *)mainQueue +{ + static ATQueue *queue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + queue = [[ATQueue alloc] init]; + queue->_nativeQueue = dispatch_get_main_queue(); + queue->_isMainQueue = true; + }); + + return queue; +} + ++ (ATQueue *)concurrentDefaultQueue +{ + static ATQueue *queue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; + }); + + return queue; +} + ++ (ATQueue *)concurrentBackgroundQueue +{ + static ATQueue *queue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)]; + }); + + return queue; +} + +- (instancetype)init +{ + return [self initWithName:[[ATQueue applicationPrefix] stringByAppendingFormat:@".%ld", lrand48()]]; +} + +- (instancetype)initWithName:(NSString *)name +{ + self = [super init]; + if (self != nil) + { + _nativeQueue = dispatch_queue_create([name UTF8String], DISPATCH_QUEUE_SERIAL); + dispatch_queue_set_specific(_nativeQueue, AMQueueSpecific, (__bridge void *)self, NULL); + } + return self; +} + +- (instancetype)initWithPriority:(ATQueuePriority)priority +{ + self = [super init]; + if (self != nil) + { + _nativeQueue = dispatch_queue_create([[[ATQueue applicationPrefix] stringByAppendingFormat:@".%ld", lrand48()] UTF8String], DISPATCH_QUEUE_SERIAL); + long targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_DEFAULT; + switch (priority) + { + case ATQueuePriorityLow: + targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_LOW; + break; + case ATQueuePriorityDefault: + targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_DEFAULT; + break; + case ATQueuePriorityHigh: + targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_HIGH; + break; + } + dispatch_set_target_queue(_nativeQueue, dispatch_get_global_queue(targetQueueIdentifier, 0)); + dispatch_queue_set_specific(_nativeQueue, AMQueueSpecific, (__bridge void *)self, NULL); + } + return self; +} + +- (instancetype)initWithNativeQueue:(dispatch_queue_t)queue +{ + self = [super init]; + if (self != nil) + { +#if !OS_OBJECT_USE_OBJC + _nativeQueue = dispatch_retain(queue); +#else + _nativeQueue = queue; +#endif + } + return self; +} + +- (void)dealloc +{ + if (_nativeQueue != nil) + { +#if !OS_OBJECT_USE_OBJC + dispatch_release(_nativeQueue); +#endif + _nativeQueue = nil; + } +} + +- (void)dispatch:(dispatch_block_t)block +{ + [self dispatch:block synchronous:false]; +} + +- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous +{ + __block ATQueue *strongSelf = self; + dispatch_block_t blockWithSelf = ^ + { + block(); + [strongSelf noop]; + strongSelf = nil; + }; + + if (_isMainQueue) + { + if ([NSThread isMainThread]) + blockWithSelf(); + else if (synchronous) + dispatch_sync(_nativeQueue, blockWithSelf); + else + dispatch_async(_nativeQueue, blockWithSelf); + } + else + { + if (dispatch_get_specific(AMQueueSpecific) == (__bridge void *)self) + block(); + else if (synchronous) + dispatch_sync(_nativeQueue, blockWithSelf); + else + dispatch_async(_nativeQueue, blockWithSelf); + } +} + +- (void)dispatchAfter:(NSTimeInterval)seconds block:(dispatch_block_t)block +{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)), _nativeQueue, block); +} + +- (dispatch_queue_t)nativeQueue +{ + return _nativeQueue; +} + +- (void)noop +{ +} + +@end diff --git a/submodules/WatchBridgeAudio/Sources/LegacyBridgeAudio.swift b/submodules/WatchBridgeAudio/Sources/LegacyBridgeAudio.swift new file mode 100644 index 0000000000..ab80d4f2a0 --- /dev/null +++ b/submodules/WatchBridgeAudio/Sources/LegacyBridgeAudio.swift @@ -0,0 +1,25 @@ +import Foundation +import SwiftSignalKit +import WatchBridgeAudioImpl + +public func legacyDecodeOpusAudio(path: String, outputPath: String) -> Signal { + return Signal { subscriber in + let decoder = TGBridgeAudioDecoder(url: URL(fileURLWithPath: path), outputUrl: URL(fileURLWithPath: outputPath)) + decoder?.start(completion: { + subscriber.putNext(outputPath) + subscriber.putCompletion() + }) + return EmptyDisposable + } +} + +public func legacyEncodeOpusAudio(path: String) -> Signal<(String?, Int32), NoError> { + return Signal { subscriber in + let encoder = TGBridgeAudioEncoder(url: URL(fileURLWithPath: path)) + encoder?.start(completion: { (path, duration) in + subscriber.putNext((path, duration)) + subscriber.putCompletion() + }) + return EmptyDisposable + } +} diff --git a/submodules/WatchCommon/BUILD b/submodules/WatchCommon/BUILD new file mode 100644 index 0000000000..e69de29bb2 diff --git a/submodules/WatchCommon/Host/BUILD b/submodules/WatchCommon/Host/BUILD new file mode 100644 index 0000000000..043309a973 --- /dev/null +++ b/submodules/WatchCommon/Host/BUILD @@ -0,0 +1,25 @@ + +objc_library( + name = "WatchCommon", + enable_modules = True, + module_name = "WatchCommon", + srcs = glob([ + "Sources/**/*.m", + "Sources/**/*.h", + ], allow_empty=True), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + copts = [ + "-I{}/PublicHeaders/WatchCommon".format(package_name()), + ], + includes = [ + "PublicHeaders", + ], + sdk_frameworks = [ + "Foundation", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeActionMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeActionMediaAttachment.h new file mode 100644 index 0000000000..bb18757fc6 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeActionMediaAttachment.h @@ -0,0 +1,36 @@ +#import + +typedef NS_ENUM(NSUInteger, TGBridgeMessageAction) { + TGBridgeMessageActionNone = 0, + TGBridgeMessageActionChatEditTitle = 1, + TGBridgeMessageActionChatAddMember = 2, + TGBridgeMessageActionChatDeleteMember = 3, + TGBridgeMessageActionCreateChat = 4, + TGBridgeMessageActionChatEditPhoto = 5, + TGBridgeMessageActionContactRequest = 6, + TGBridgeMessageActionAcceptContactRequest = 7, + TGBridgeMessageActionContactRegistered = 8, + TGBridgeMessageActionUserChangedPhoto = 9, + TGBridgeMessageActionEncryptedChatRequest = 10, + TGBridgeMessageActionEncryptedChatAccept = 11, + TGBridgeMessageActionEncryptedChatDecline = 12, + TGBridgeMessageActionEncryptedChatMessageLifetime = 13, + TGBridgeMessageActionEncryptedChatScreenshot = 14, + TGBridgeMessageActionEncryptedChatMessageScreenshot = 15, + TGBridgeMessageActionCreateBroadcastList = 16, + TGBridgeMessageActionJoinedByLink = 17, + TGBridgeMessageActionChannelCreated = 18, + TGBridgeMessageActionChannelCommentsStatusChanged = 19, + TGBridgeMessageActionChannelInviter = 20, + TGBridgeMessageActionGroupMigratedTo = 21, + TGBridgeMessageActionGroupDeactivated = 22, + TGBridgeMessageActionGroupActivated = 23, + TGBridgeMessageActionChannelMigratedFrom = 24 +}; + +@interface TGBridgeActionMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) TGBridgeMessageAction actionType; +@property (nonatomic, strong) NSDictionary *actionData; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeAudioMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeAudioMediaAttachment.h new file mode 100644 index 0000000000..ffdb864568 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeAudioMediaAttachment.h @@ -0,0 +1,16 @@ +#import + +@interface TGBridgeAudioMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t audioId; +@property (nonatomic, assign) int64_t accessHash; +@property (nonatomic, assign) int32_t datacenterId; + +@property (nonatomic, assign) int64_t localAudioId; + +@property (nonatomic, assign) int32_t duration; +@property (nonatomic, assign) int32_t fileSize; + +- (int64_t)identifier; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeBotCommandInfo.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeBotCommandInfo.h new file mode 100644 index 0000000000..fe6f72e1a0 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeBotCommandInfo.h @@ -0,0 +1,12 @@ +#import + +@interface TGBridgeBotCommandInfo : NSObject +{ + NSString *_command; + NSString *_commandDescription; +} + +@property (nonatomic, readonly) NSString *command; +@property (nonatomic, readonly) NSString *commandDescription; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeBotInfo.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeBotInfo.h new file mode 100644 index 0000000000..0dafae5cef --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeBotInfo.h @@ -0,0 +1,12 @@ +#import + +@interface TGBridgeBotInfo : NSObject +{ + NSString *_shortDescription; + NSArray *_commandList; +} + +@property (nonatomic, readonly) NSString *shortDescription; +@property (nonatomic, readonly) NSArray *commandList; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeChat.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeChat.h new file mode 100644 index 0000000000..fa56be05f2 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeChat.h @@ -0,0 +1,46 @@ +#import +#import + +@interface TGBridgeChat : NSObject + +@property (nonatomic) int64_t identifier; +@property (nonatomic) NSTimeInterval date; +@property (nonatomic) int32_t fromUid; +@property (nonatomic, strong) NSString *text; + +@property (nonatomic, strong) NSArray *media; + +@property (nonatomic) bool outgoing; +@property (nonatomic) bool unread; +@property (nonatomic) bool deliveryError; +@property (nonatomic) TGBridgeMessageDeliveryState deliveryState; + +@property (nonatomic) int32_t unreadCount; + +@property (nonatomic) bool isBroadcast; + +@property (nonatomic, strong) NSString *groupTitle; +@property (nonatomic, strong) NSString *groupPhotoSmall; +@property (nonatomic, strong) NSString *groupPhotoBig; + +@property (nonatomic) bool isGroup; +@property (nonatomic) bool hasLeftGroup; +@property (nonatomic) bool isKickedFromGroup; + +@property (nonatomic) bool isChannel; +@property (nonatomic) bool isChannelGroup; + +@property (nonatomic, strong) NSString *userName; +@property (nonatomic, strong) NSString *about; +@property (nonatomic) bool verified; + +@property (nonatomic) int32_t participantsCount; +@property (nonatomic, strong) NSArray *participants; + +- (NSArray *)involvedUserIds; +- (NSArray *)participantsUserIds; + +@end + +extern NSString *const TGBridgeChatKey; +extern NSString *const TGBridgeChatsArrayKey; diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeChatMessages.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeChatMessages.h new file mode 100644 index 0000000000..9c539758d4 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeChatMessages.h @@ -0,0 +1,14 @@ +#import + +@class SSignal; + +@interface TGBridgeChatMessages : NSObject +{ + NSArray *_messages; +} + +@property (nonatomic, readonly) NSArray *messages; + +@end + +extern NSString *const TGBridgeChatMessageListViewKey; diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeCommon.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeCommon.h new file mode 100644 index 0000000000..fe0510c041 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeCommon.h @@ -0,0 +1,95 @@ +#import + +extern NSString *const TGBridgeIncomingFileTypeKey; +extern NSString *const TGBridgeIncomingFileIdentifierKey; +extern NSString *const TGBridgeIncomingFileRandomIdKey; +extern NSString *const TGBridgeIncomingFilePeerIdKey; +extern NSString *const TGBridgeIncomingFileReplyToMidKey; + +extern NSString *const TGBridgeIncomingFileTypeAudio; +extern NSString *const TGBridgeIncomingFileTypeImage; + +@interface TGBridgeSubscription : NSObject + +@property (nonatomic, readonly) int64_t identifier; +@property (nonatomic, readonly, strong) NSString *name; + +@property (nonatomic, readonly) bool isOneTime; +@property (nonatomic, readonly) bool renewable; +@property (nonatomic, readonly) bool dropPreviouslyQueued; +@property (nonatomic, readonly) bool synchronous; + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder; +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder; + ++ (NSString *)subscriptionName; + +@end + + +@interface TGBridgeDisposal : NSObject + +@property (nonatomic, readonly) int64_t identifier; + +- (instancetype)initWithIdentifier:(int64_t)identifier; + +@end + + +@interface TGBridgeFile : NSObject + +@property (nonatomic, readonly, strong) NSData *data; +@property (nonatomic, readonly, strong) NSDictionary *metadata; + +- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata; + +@end + + +@interface TGBridgePing : NSObject + +@property (nonatomic, readonly) int32_t sessionId; + +- (instancetype)initWithSessionId:(int32_t)sessionId; + +@end + + +@interface TGBridgeSubscriptionListRequest : NSObject + +@property (nonatomic, readonly) int32_t sessionId; + +- (instancetype)initWithSessionId:(int32_t)sessionId; + +@end + + +@interface TGBridgeSubscriptionList : NSObject + +@property (nonatomic, readonly, strong) NSArray *subscriptions; + +- (instancetype)initWithArray:(NSArray *)array; + +@end + + +typedef NS_ENUM(int32_t, TGBridgeResponseType) { + TGBridgeResponseTypeUndefined, + TGBridgeResponseTypeNext, + TGBridgeResponseTypeFailed, + TGBridgeResponseTypeCompleted +}; + +@interface TGBridgeResponse : NSObject + +@property (nonatomic, readonly) int64_t subscriptionIdentifier; + +@property (nonatomic, readonly) TGBridgeResponseType type; +@property (nonatomic, readonly, strong) id next; +@property (nonatomic, readonly, strong) NSString *error; + ++ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription; ++ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription; ++ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeContactMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeContactMediaAttachment.h new file mode 100644 index 0000000000..052293134a --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeContactMediaAttachment.h @@ -0,0 +1,13 @@ +#import + +@interface TGBridgeContactMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int32_t uid; +@property (nonatomic, strong) NSString *firstName; +@property (nonatomic, strong) NSString *lastName; +@property (nonatomic, strong) NSString *phoneNumber; +@property (nonatomic, strong) NSString *prettyPhoneNumber; + +- (NSString *)displayName; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeContext.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeContext.h new file mode 100644 index 0000000000..45225a04f6 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeContext.h @@ -0,0 +1,18 @@ +#import + +@interface TGBridgeContext : NSObject + +@property (nonatomic, readonly) bool authorized; +@property (nonatomic, readonly) int32_t userId; +@property (nonatomic, readonly) bool micAccessAllowed; +@property (nonatomic, readonly) NSDictionary *preheatData; +@property (nonatomic, readonly) NSInteger preheatVersion; + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; +- (NSDictionary *)dictionary; + +- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId; +- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data; +- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeDocumentMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeDocumentMediaAttachment.h new file mode 100644 index 0000000000..020a50fa47 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeDocumentMediaAttachment.h @@ -0,0 +1,23 @@ +#import + +@interface TGBridgeDocumentMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t documentId; +@property (nonatomic, assign) int64_t localDocumentId; +@property (nonatomic, assign) int32_t fileSize; + +@property (nonatomic, strong) NSString *fileName; +@property (nonatomic, strong) NSValue *imageSize; +@property (nonatomic, assign) bool isAnimated; +@property (nonatomic, assign) bool isSticker; +@property (nonatomic, strong) NSString *stickerAlt; +@property (nonatomic, assign) int64_t stickerPackId; +@property (nonatomic, assign) int64_t stickerPackAccessHash; + +@property (nonatomic, assign) bool isVoice; +@property (nonatomic, assign) bool isAudio; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *performer; +@property (nonatomic, assign) int32_t duration; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeForwardedMessageMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeForwardedMessageMediaAttachment.h new file mode 100644 index 0000000000..f57651e4e7 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeForwardedMessageMediaAttachment.h @@ -0,0 +1,9 @@ +#import + +@interface TGBridgeForwardedMessageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t peerId; +@property (nonatomic, assign) int32_t mid; +@property (nonatomic, assign) int32_t date; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeImageMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeImageMediaAttachment.h new file mode 100644 index 0000000000..f35b6623d3 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeImageMediaAttachment.h @@ -0,0 +1,10 @@ +#import + +#import + +@interface TGBridgeImageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t imageId; +@property (nonatomic, assign) CGSize dimensions; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeLocationMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeLocationMediaAttachment.h new file mode 100644 index 0000000000..0108154651 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeLocationMediaAttachment.h @@ -0,0 +1,19 @@ +#import + +@interface TGBridgeVenueAttachment : NSObject + +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *address; +@property (nonatomic, strong) NSString *provider; +@property (nonatomic, strong) NSString *venueId; + +@end + +@interface TGBridgeLocationMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) double latitude; +@property (nonatomic, assign) double longitude; + +@property (nonatomic, strong) TGBridgeVenueAttachment *venue; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeLocationVenue.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeLocationVenue.h new file mode 100644 index 0000000000..c626f76f9c --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeLocationVenue.h @@ -0,0 +1,15 @@ +#import + +@class TGBridgeLocationMediaAttachment; + +@interface TGBridgeLocationVenue : NSObject + +@property (nonatomic) CLLocationCoordinate2D coordinate; +@property (nonatomic, strong) NSString *identifier; +@property (nonatomic, strong) NSString *provider; +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSString *address; + +- (TGBridgeLocationMediaAttachment *)locationAttachment; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMediaAttachment.h new file mode 100644 index 0000000000..a814ea5008 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMediaAttachment.h @@ -0,0 +1,11 @@ +#import + +@interface TGBridgeMediaAttachment : NSObject + +@property (nonatomic, readonly) NSInteger mediaType; + ++ (NSInteger)mediaType; + +@end + +extern NSString *const TGBridgeMediaAttachmentTypeKey; diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessage.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessage.h new file mode 100644 index 0000000000..a55e149121 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessage.h @@ -0,0 +1,65 @@ +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +typedef enum { + TGBridgeTextCheckingResultTypeUndefined, + TGBridgeTextCheckingResultTypeBold, + TGBridgeTextCheckingResultTypeItalic, + TGBridgeTextCheckingResultTypeCode, + TGBridgeTextCheckingResultTypePre +} TGBridgeTextCheckingResultType; + +@interface TGBridgeTextCheckingResult : NSObject + +@property (nonatomic, assign) TGBridgeTextCheckingResultType type; +@property (nonatomic, assign) NSRange range; + +@end + + +typedef NS_ENUM(NSUInteger, TGBridgeMessageDeliveryState) { + TGBridgeMessageDeliveryStateDelivered = 0, + TGBridgeMessageDeliveryStatePending = 1, + TGBridgeMessageDeliveryStateFailed = 2 +}; + +@interface TGBridgeMessage : NSObject + +@property (nonatomic) int32_t identifier; +@property (nonatomic) NSTimeInterval date; +@property (nonatomic) int64_t randomId; +@property (nonatomic) bool unread; +@property (nonatomic) bool deliveryError; +@property (nonatomic) TGBridgeMessageDeliveryState deliveryState; +@property (nonatomic) bool outgoing; +@property (nonatomic) int64_t fromUid; +@property (nonatomic) int64_t toUid; +@property (nonatomic) int64_t cid; +@property (nonatomic, strong) NSString *text; +@property (nonatomic, strong) NSArray *media; +@property (nonatomic) bool forceReply; + +- (NSArray *)involvedUserIds; +- (NSArray *)textCheckingResults; + ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId; ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId replyToMessage:(TGBridgeMessage *)replyToMessage; ++ (instancetype)temporaryNewMessageForSticker:(TGBridgeDocumentMediaAttachment *)sticker userId:(int32_t)userId; ++ (instancetype)temporaryNewMessageForLocation:(TGBridgeLocationMediaAttachment *)location userId:(int32_t)userId; ++ (instancetype)temporaryNewMessageForAudioWithDuration:(int32_t)duration userId:(int32_t)userId localAudioId:(int64_t)localAudioId; + +@end + +extern NSString *const TGBridgeMessageKey; +extern NSString *const TGBridgeMessagesArrayKey; diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessageEntities.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessageEntities.h new file mode 100644 index 0000000000..669ff3b21d --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessageEntities.h @@ -0,0 +1,59 @@ +#import + +@interface TGBridgeMessageEntity : NSObject + +@property (nonatomic, assign) NSRange range; + ++ (instancetype)entitityWithRange:(NSRange)range; + +@end + + +@interface TGBridgeMessageEntityUrl : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityEmail : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityTextUrl : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityMention : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityHashtag : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityBotCommand : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityBold : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityItalic : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityCode : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityPre : TGBridgeMessageEntity + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessageEntitiesAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessageEntitiesAttachment.h new file mode 100644 index 0000000000..8f32fd038c --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeMessageEntitiesAttachment.h @@ -0,0 +1,8 @@ +#import +#import + +@interface TGBridgeMessageEntitiesAttachment : TGBridgeMediaAttachment + +@property (nonatomic, strong) NSArray *entities; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgePeerNotificationSettings.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgePeerNotificationSettings.h new file mode 100644 index 0000000000..fce723cecb --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgePeerNotificationSettings.h @@ -0,0 +1,7 @@ +#import + +@interface TGBridgePeerNotificationSettings : NSObject + +@property (nonatomic, assign) int32_t muteFor; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeReplyMarkupMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeReplyMarkupMediaAttachment.h new file mode 100644 index 0000000000..ef8a600aee --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeReplyMarkupMediaAttachment.h @@ -0,0 +1,9 @@ +#import + +@class TGBridgeBotReplyMarkup; + +@interface TGBridgeReplyMarkupMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, strong) TGBridgeBotReplyMarkup *replyMarkup; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeReplyMessageMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeReplyMessageMediaAttachment.h new file mode 100644 index 0000000000..50353e214d --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeReplyMessageMediaAttachment.h @@ -0,0 +1,10 @@ +#import + +@class TGBridgeMessage; + +@interface TGBridgeReplyMessageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int32_t mid; +@property (nonatomic, strong) TGBridgeMessage *message; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeSubscriptions.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeSubscriptions.h new file mode 100644 index 0000000000..609c2d019d --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeSubscriptions.h @@ -0,0 +1,268 @@ +#import + +#import +#import + +@class TGBridgeMediaAttachment; +@class TGBridgeImageMediaAttachment; +@class TGBridgeVideoMediaAttachment; +@class TGBridgeDocumentMediaAttachment; +@class TGBridgeLocationMediaAttachment; +@class TGBridgePeerNotificationSettings; + +@interface TGBridgeAudioSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) TGBridgeMediaAttachment *attachment; +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; + +- (instancetype)initWithAttachment:(TGBridgeMediaAttachment *)attachment peerId:(int64_t)peerId messageId:(int32_t)messageId; + +@end + + +@interface TGBridgeAudioSentSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t conversationId; + +- (instancetype)initWithConversationId:(int64_t)conversationId; + +@end + + +@interface TGBridgeChatListSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int32_t limit; + +- (instancetype)initWithLimit:(int32_t)limit; + +@end + + +@interface TGBridgeChatMessageListSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t atMessageId; +@property (nonatomic, readonly) NSUInteger rangeMessageCount; + +- (instancetype)initWithPeerId:(int64_t)peerId atMessageId:(int32_t)messageId rangeMessageCount:(NSUInteger)rangeMessageCount; + +@end + + +@interface TGBridgeChatMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId; + +@end + + +@interface TGBridgeReadChatMessageListSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId; + +@end + + +@interface TGBridgeContactsSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) NSString *query; + +- (instancetype)initWithQuery:(NSString *)query; + +@end + + +@interface TGBridgeConversationSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end + + +@interface TGBridgeNearbyVenuesSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; +@property (nonatomic, readonly) int32_t limit; + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate limit:(int32_t)limit; + +@end + + +@interface TGBridgeMediaThumbnailSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, readonly) CGSize size; +@property (nonatomic, readonly) bool notification; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId size:(CGSize)size notification:(bool)notification; + +@end + + +typedef NS_ENUM(NSUInteger, TGBridgeMediaAvatarType) { + TGBridgeMediaAvatarTypeSmall, + TGBridgeMediaAvatarTypeProfile, + TGBridgeMediaAvatarTypeLarge +}; + +@interface TGBridgeMediaAvatarSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) NSString *url; +@property (nonatomic, readonly) TGBridgeMediaAvatarType type; + +- (instancetype)initWithPeerId:(int64_t)peerId url:(NSString *)url type:(TGBridgeMediaAvatarType)type; + +@end + +@interface TGBridgeMediaStickerSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t documentId; +@property (nonatomic, readonly) int64_t stickerPackId; +@property (nonatomic, readonly) int64_t stickerPackAccessHash; +@property (nonatomic, readonly) int64_t stickerPeerId; +@property (nonatomic, readonly) int32_t stickerMessageId; +@property (nonatomic, readonly) bool notification; +@property (nonatomic, readonly) CGSize size; + +- (instancetype)initWithDocumentId:(int64_t)documentId stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash stickerPeerId:(int64_t)stickerPeerId stickerMessageId:(int32_t)stickerMessageId notification:(bool)notification size:(CGSize)size; + +@end + + +@interface TGBridgePeerSettingsSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end + +@interface TGBridgePeerUpdateNotificationSettingsSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end + +@interface TGBridgePeerUpdateBlockStatusSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) bool blocked; + +- (instancetype)initWithPeerId:(int64_t)peerId blocked:(bool)blocked; + +@end + + +@interface TGBridgeRemoteSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, readonly) int32_t type; +@property (nonatomic, readonly) bool autoPlay; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId type:(int32_t)type autoPlay:(bool)autoPlay; + +@end + + +@interface TGBridgeSendTextMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) NSString *text; +@property (nonatomic, readonly) int32_t replyToMid; + +- (instancetype)initWithPeerId:(int64_t)peerId text:(NSString *)text replyToMid:(int32_t)replyToMid; + +@end + + +@interface TGBridgeSendStickerMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) TGBridgeDocumentMediaAttachment *document; +@property (nonatomic, readonly) int32_t replyToMid; + +- (instancetype)initWithPeerId:(int64_t)peerId document:(TGBridgeDocumentMediaAttachment *)document replyToMid:(int32_t)replyToMid; + +@end + + +@interface TGBridgeSendLocationMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) TGBridgeLocationMediaAttachment *location; +@property (nonatomic, readonly) int32_t replyToMid; + +- (instancetype)initWithPeerId:(int64_t)peerId location:(TGBridgeLocationMediaAttachment *)location replyToMid:(int32_t)replyToMid; + +@end + + +@interface TGBridgeSendForwardedMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, readonly) int64_t targetPeerId; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId targetPeerId:(int64_t)targetPeerId; + +@end + + +@interface TGBridgeStateSubscription : TGBridgeSubscription + +@end + + +@interface TGBridgeStickerPacksSubscription : TGBridgeSubscription + +@end + + +@interface TGBridgeRecentStickersSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int32_t limit; + +- (instancetype)initWithLimit:(int32_t)limit; + +@end + + +@interface TGBridgeUserInfoSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) NSArray *userIds; + +- (instancetype)initWithUserIds:(NSArray *)userIds; + +@end + + +@interface TGBridgeUserBotInfoSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) NSArray *userIds; + +- (instancetype)initWithUserIds:(NSArray *)userIds; + +@end + +@interface TGBridgeBotReplyMarkupSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeUnsupportedMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeUnsupportedMediaAttachment.h new file mode 100644 index 0000000000..5b81ed1f57 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeUnsupportedMediaAttachment.h @@ -0,0 +1,9 @@ +#import + +@interface TGBridgeUnsupportedMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, strong) NSString *compactTitle; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *subtitle; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeUser.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeUser.h new file mode 100644 index 0000000000..632d934db4 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeUser.h @@ -0,0 +1,59 @@ +#import + +@class TGBridgeBotInfo; +@class TGBridgeUserChange; + +typedef NS_ENUM(NSUInteger, TGBridgeUserKind) { + TGBridgeUserKindGeneric, + TGBridgeUserKindBot, + TGBridgeUserKindSmartBot +}; + +typedef NS_ENUM(NSUInteger, TGBridgeBotKind) { + TGBridgeBotKindGeneric, + TGBridgeBotKindPrivate +}; + +@interface TGBridgeUser : NSObject + +@property (nonatomic) int64_t identifier; +@property (nonatomic, strong) NSString *firstName; +@property (nonatomic, strong) NSString *lastName; +@property (nonatomic, strong) NSString *userName; +@property (nonatomic, strong) NSString *phoneNumber; +@property (nonatomic, strong) NSString *prettyPhoneNumber; +@property (nonatomic, strong) NSString *about; + +@property (nonatomic) bool online; +@property (nonatomic) NSTimeInterval lastSeen; + +@property (nonatomic, strong) NSString *photoSmall; +@property (nonatomic, strong) NSString *photoBig; + +@property (nonatomic) TGBridgeUserKind kind; +@property (nonatomic) TGBridgeBotKind botKind; +@property (nonatomic) int32_t botVersion; + +@property (nonatomic) bool verified; + +@property (nonatomic) int32_t userVersion; + +- (NSString *)displayName; +- (TGBridgeUserChange *)changeFromUser:(TGBridgeUser *)user; +- (TGBridgeUser *)userByApplyingChange:(TGBridgeUserChange *)change; + +- (bool)isBot; + +@end + + +@interface TGBridgeUserChange : NSObject + +@property (nonatomic, readonly) int32_t userIdentifier; +@property (nonatomic, readonly) NSDictionary *fields; + +- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields; + +@end + +extern NSString *const TGBridgeUsersDictionaryKey; diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeVideoMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeVideoMediaAttachment.h new file mode 100644 index 0000000000..8d54027ce4 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeVideoMediaAttachment.h @@ -0,0 +1,12 @@ +#import + +#import + +@interface TGBridgeVideoMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t videoId; +@property (nonatomic, assign) int32_t duration; +@property (nonatomic, assign) CGSize dimensions; +@property (nonatomic, assign) bool round; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeWebPageMediaAttachment.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeWebPageMediaAttachment.h new file mode 100644 index 0000000000..6e20ee3f96 --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/TGBridgeWebPageMediaAttachment.h @@ -0,0 +1,23 @@ +#import + +#import + +@class TGBridgeImageMediaAttachment; + +@interface TGBridgeWebPageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t webPageId; +@property (nonatomic, strong) NSString *url; +@property (nonatomic, strong) NSString *displayUrl; +@property (nonatomic, strong) NSString *pageType; +@property (nonatomic, strong) NSString *siteName; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *pageDescription; +@property (nonatomic, strong) TGBridgeImageMediaAttachment *photo; +@property (nonatomic, strong) NSString *embedUrl; +@property (nonatomic, strong) NSString *embedType; +@property (nonatomic, assign) CGSize embedSize; +@property (nonatomic, strong) NSNumber *duration; +@property (nonatomic, strong) NSString *author; + +@end diff --git a/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/WatchCommon.h b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/WatchCommon.h new file mode 100644 index 0000000000..27b37d2d7b --- /dev/null +++ b/submodules/WatchCommon/Host/PublicHeaders/WatchCommon/WatchCommon.h @@ -0,0 +1,29 @@ +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeActionMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeActionMediaAttachment.m new file mode 100644 index 0000000000..763cf8a1eb --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeActionMediaAttachment.m @@ -0,0 +1,33 @@ +#import "TGBridgeActionMediaAttachment.h" +#import "TGBridgeImageMediaAttachment.h" + +const NSInteger TGBridgeActionMediaAttachmentType = 0x1167E28B; + +NSString *const TGBridgeActionMediaTypeKey = @"actionType"; +NSString *const TGBridgeActionMediaDataKey = @"actionData"; + +@implementation TGBridgeActionMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _actionType = (TGBridgeMessageAction)[aDecoder decodeInt32ForKey:TGBridgeActionMediaTypeKey]; + _actionData = [aDecoder decodeObjectForKey:TGBridgeActionMediaDataKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.actionType forKey:TGBridgeActionMediaTypeKey]; + [aCoder encodeObject:self.actionData forKey:TGBridgeActionMediaDataKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeActionMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeAudioMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeAudioMediaAttachment.m new file mode 100644 index 0000000000..3f4096a897 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeAudioMediaAttachment.m @@ -0,0 +1,65 @@ +#import "TGBridgeAudioMediaAttachment.h" + +const NSInteger TGBridgeAudioMediaAttachmentType = 0x3A0E7A32; + +NSString *const TGBridgeAudioMediaAudioIdKey = @"audioId"; +NSString *const TGBridgeAudioMediaAccessHashKey = @"accessHash"; +NSString *const TGBridgeAudioMediaLocalIdKey = @"localId"; +NSString *const TGBridgeAudioMediaDatacenterIdKey = @"datacenterId"; +NSString *const TGBridgeAudioMediaDurationKey = @"duration"; +NSString *const TGBridgeAudioMediaFileSizeKey = @"fileSize"; + +@implementation TGBridgeAudioMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _audioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAudioIdKey]; + _accessHash = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAccessHashKey]; + _localAudioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaLocalIdKey]; + _datacenterId = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDatacenterIdKey]; + _duration = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDurationKey]; + _fileSize = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaFileSizeKey]; + } + return self; +} + +- (void)encodeWithCoder:(nonnull NSCoder *)aCoder +{ + [aCoder encodeInt64:self.audioId forKey:TGBridgeAudioMediaAudioIdKey]; + [aCoder encodeInt64:self.accessHash forKey:TGBridgeAudioMediaAccessHashKey]; + [aCoder encodeInt64:self.localAudioId forKey:TGBridgeAudioMediaLocalIdKey]; + [aCoder encodeInt32:self.datacenterId forKey:TGBridgeAudioMediaDatacenterIdKey]; + [aCoder encodeInt32:self.duration forKey:TGBridgeAudioMediaDurationKey]; + [aCoder encodeInt32:self.fileSize forKey:TGBridgeAudioMediaFileSizeKey]; +} + +- (int64_t)identifier +{ + if (self.localAudioId != 0) + return self.localAudioId; + + return self.audioId; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeAudioMediaAttachment *audio = (TGBridgeAudioMediaAttachment *)object; + + return (self.audioId == audio.audioId || self.localAudioId == audio.localAudioId); +} + ++ (NSInteger)mediaType +{ + return TGBridgeAudioMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeBotCommandInfo.m b/submodules/WatchCommon/Host/Sources/TGBridgeBotCommandInfo.m new file mode 100644 index 0000000000..0f1e005861 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeBotCommandInfo.m @@ -0,0 +1,25 @@ +#import "TGBridgeBotCommandInfo.h" + +NSString *const TGBridgeBotCommandInfoCommandKey = @"command"; +NSString *const TGBridgeBotCommandDescriptionKey = @"commandDescription"; + +@implementation TGBridgeBotCommandInfo + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _command = [aDecoder decodeObjectForKey:TGBridgeBotCommandInfoCommandKey]; + _commandDescription = [aDecoder decodeObjectForKey:TGBridgeBotCommandDescriptionKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.command forKey:TGBridgeBotCommandInfoCommandKey]; + [aCoder encodeObject:self.commandDescription forKey:TGBridgeBotCommandDescriptionKey]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeBotInfo.m b/submodules/WatchCommon/Host/Sources/TGBridgeBotInfo.m new file mode 100644 index 0000000000..996abb3a95 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeBotInfo.m @@ -0,0 +1,25 @@ +#import "TGBridgeBotInfo.h" + +NSString *const TGBridgeBotInfoShortDescriptionKey = @"shortDescription"; +NSString *const TGBridgeBotInfoCommandListKey = @"commandList"; + +@implementation TGBridgeBotInfo + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _shortDescription = [aDecoder decodeObjectForKey:TGBridgeBotInfoShortDescriptionKey]; + _commandList = [aDecoder decodeObjectForKey:TGBridgeBotInfoCommandListKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.shortDescription forKey:TGBridgeBotInfoShortDescriptionKey]; + [aCoder encodeObject:self.commandList forKey:TGBridgeBotInfoCommandListKey]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeChat.m b/submodules/WatchCommon/Host/Sources/TGBridgeChat.m new file mode 100644 index 0000000000..973522fa3c --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeChat.m @@ -0,0 +1,148 @@ +#import "TGBridgeChat.h" +#import "TGBridgePeerIdAdapter.h" + +NSString *const TGBridgeChatIdentifierKey = @"identifier"; +NSString *const TGBridgeChatDateKey = @"date"; +NSString *const TGBridgeChatFromUidKey = @"fromUid"; +NSString *const TGBridgeChatTextKey = @"text"; +NSString *const TGBridgeChatOutgoingKey = @"outgoing"; +NSString *const TGBridgeChatUnreadKey = @"unread"; +NSString *const TGBridgeChatMediaKey = @"media"; +NSString *const TGBridgeChatUnreadCountKey = @"unreadCount"; +NSString *const TGBridgeChatGroupTitleKey = @"groupTitle"; +NSString *const TGBridgeChatGroupPhotoSmallKey = @"groupPhotoSmall"; +NSString *const TGBridgeChatGroupPhotoBigKey = @"groupPhotoBig"; +NSString *const TGBridgeChatIsGroupKey = @"isGroup"; +NSString *const TGBridgeChatHasLeftGroupKey = @"hasLeftGroup"; +NSString *const TGBridgeChatIsKickedFromGroupKey = @"isKickedFromGroup"; +NSString *const TGBridgeChatIsChannelKey = @"isChannel"; +NSString *const TGBridgeChatIsChannelGroupKey = @"isChannelGroup"; +NSString *const TGBridgeChatUserNameKey = @"userName"; +NSString *const TGBridgeChatAboutKey = @"about"; +NSString *const TGBridgeChatVerifiedKey = @"verified"; +NSString *const TGBridgeChatGroupParticipantsCountKey = @"participantsCount"; +NSString *const TGBridgeChatGroupParticipantsKey = @"participants"; +NSString *const TGBridgeChatDeliveryStateKey = @"deliveryState"; +NSString *const TGBridgeChatDeliveryErrorKey = @"deliveryError"; + +NSString *const TGBridgeChatKey = @"chat"; +NSString *const TGBridgeChatsArrayKey = @"chats"; + +@implementation TGBridgeChat + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeChatIdentifierKey]; + _date = [aDecoder decodeDoubleForKey:TGBridgeChatDateKey]; + _fromUid = [aDecoder decodeInt32ForKey:TGBridgeChatFromUidKey]; + _text = [aDecoder decodeObjectForKey:TGBridgeChatTextKey]; + _outgoing = [aDecoder decodeBoolForKey:TGBridgeChatOutgoingKey]; + _unread = [aDecoder decodeBoolForKey:TGBridgeChatUnreadKey]; + _unreadCount = [aDecoder decodeInt32ForKey:TGBridgeChatUnreadCountKey]; + _deliveryState = [aDecoder decodeInt32ForKey:TGBridgeChatDeliveryStateKey]; + _deliveryError = [aDecoder decodeBoolForKey:TGBridgeChatDeliveryErrorKey]; + _media = [aDecoder decodeObjectForKey:TGBridgeChatMediaKey]; + + _groupTitle = [aDecoder decodeObjectForKey:TGBridgeChatGroupTitleKey]; + _groupPhotoSmall = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoSmallKey]; + _groupPhotoBig = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoBigKey]; + _isGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsGroupKey]; + _hasLeftGroup = [aDecoder decodeBoolForKey:TGBridgeChatHasLeftGroupKey]; + _isKickedFromGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsKickedFromGroupKey]; + _isChannel = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelKey]; + _isChannelGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelGroupKey]; + _userName = [aDecoder decodeObjectForKey:TGBridgeChatUserNameKey]; + _about = [aDecoder decodeObjectForKey:TGBridgeChatAboutKey]; + _verified = [aDecoder decodeBoolForKey:TGBridgeChatVerifiedKey]; + _participantsCount = [aDecoder decodeInt32ForKey:TGBridgeChatGroupParticipantsCountKey]; + _participants = [aDecoder decodeObjectForKey:TGBridgeChatGroupParticipantsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeChatIdentifierKey]; + [aCoder encodeDouble:self.date forKey:TGBridgeChatDateKey]; + [aCoder encodeInt32:self.fromUid forKey:TGBridgeChatFromUidKey]; + [aCoder encodeObject:self.text forKey:TGBridgeChatTextKey]; + [aCoder encodeBool:self.outgoing forKey:TGBridgeChatOutgoingKey]; + [aCoder encodeBool:self.unread forKey:TGBridgeChatUnreadKey]; + [aCoder encodeInt32:self.unreadCount forKey:TGBridgeChatUnreadCountKey]; + [aCoder encodeInt32:self.deliveryState forKey:TGBridgeChatDeliveryStateKey]; + [aCoder encodeBool:self.deliveryError forKey:TGBridgeChatDeliveryErrorKey]; + [aCoder encodeObject:self.media forKey:TGBridgeChatMediaKey]; + + [aCoder encodeObject:self.groupTitle forKey:TGBridgeChatGroupTitleKey]; + [aCoder encodeObject:self.groupPhotoSmall forKey:TGBridgeChatGroupPhotoSmallKey]; + [aCoder encodeObject:self.groupPhotoBig forKey:TGBridgeChatGroupPhotoBigKey]; + + [aCoder encodeBool:self.isGroup forKey:TGBridgeChatIsGroupKey]; + [aCoder encodeBool:self.hasLeftGroup forKey:TGBridgeChatHasLeftGroupKey]; + [aCoder encodeBool:self.isKickedFromGroup forKey:TGBridgeChatIsKickedFromGroupKey]; + + [aCoder encodeBool:self.isChannel forKey:TGBridgeChatIsChannelKey]; + [aCoder encodeBool:self.isChannelGroup forKey:TGBridgeChatIsChannelGroupKey]; + [aCoder encodeObject:self.userName forKey:TGBridgeChatUserNameKey]; + [aCoder encodeObject:self.about forKey:TGBridgeChatAboutKey]; + [aCoder encodeBool:self.verified forKey:TGBridgeChatVerifiedKey]; + + [aCoder encodeInt32:self.participantsCount forKey:TGBridgeChatGroupParticipantsCountKey]; + [aCoder encodeObject:self.participants forKey:TGBridgeChatGroupParticipantsKey]; +} + +- (NSArray *)involvedUserIds +{ + NSMutableSet *userIds = [[NSMutableSet alloc] init]; + if (!self.isGroup && !self.isChannel && self.identifier != 0) + [userIds addObject:[NSNumber numberWithLongLong:self.identifier]]; + if ((!self.isChannel || self.isChannelGroup) && self.fromUid != self.identifier && self.fromUid != 0 && !TGPeerIdIsChannel(self.fromUid) && self.fromUid > 0) + [userIds addObject:[NSNumber numberWithLongLong:self.fromUid]]; + + for (TGBridgeMediaAttachment *attachment in self.media) + { + if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]]) + { + TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment; + if (actionAttachment.actionData[@"uid"] != nil) + [userIds addObject:[NSNumber numberWithLongLong:[actionAttachment.actionData[@"uid"] longLongValue]]]; + } + } + + NSMutableArray *result = [[NSMutableArray alloc] init]; + for (NSNumber *object in userIds) { + [result addObject:object]; + } + return result; +} + +- (NSArray *)participantsUserIds +{ + NSMutableSet *userIds = [[NSMutableSet alloc] init]; + + for (NSNumber *uid in self.participants) { + [userIds addObject:[NSNumber numberWithLongLong:uid.longLongValue]]; + } + + NSMutableArray *result = [[NSMutableArray alloc] init]; + for (NSNumber *object in userIds) { + [result addObject:object]; + } + return result; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + return self.identifier == ((TGBridgeChat *)object).identifier; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeChatMessages.m b/submodules/WatchCommon/Host/Sources/TGBridgeChatMessages.m new file mode 100644 index 0000000000..c7b64e2c13 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeChatMessages.m @@ -0,0 +1,27 @@ +#import "TGBridgeChatMessages.h" +#import "TGBridgeMessage.h" + +NSString *const TGBridgeChatMessageListViewMessagesKey = @"messages"; +NSString *const TGBridgeChatMessageListViewEarlierMessageIdKey = @"earlier"; +NSString *const TGBridgeChatMessageListViewLaterMessageIdKey = @"later"; + +NSString *const TGBridgeChatMessageListViewKey = @"messageListView"; + +@implementation TGBridgeChatMessages + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _messages = [aDecoder decodeObjectForKey:TGBridgeChatMessageListViewMessagesKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.messages forKey:TGBridgeChatMessageListViewMessagesKey]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeCommon.m b/submodules/WatchCommon/Host/Sources/TGBridgeCommon.m new file mode 100644 index 0000000000..ae0cf5300b --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeCommon.m @@ -0,0 +1,295 @@ +#import "TGBridgeCommon.h" + +NSString *const TGBridgeIncomingFileTypeKey = @"type"; +NSString *const TGBridgeIncomingFileIdentifierKey = @"identifier"; +NSString *const TGBridgeIncomingFileRandomIdKey = @"randomId"; +NSString *const TGBridgeIncomingFilePeerIdKey = @"peerId"; +NSString *const TGBridgeIncomingFileReplyToMidKey = @"replyToMid"; +NSString *const TGBridgeIncomingFileTypeAudio = @"audio"; +NSString *const TGBridgeIncomingFileTypeImage = @"image"; + +NSString *const TGBridgeResponseSubscriptionIdentifier = @"identifier"; +NSString *const TGBridgeResponseTypeKey = @"type"; +NSString *const TGBridgeResponseNextKey = @"next"; +NSString *const TGBridgeResponseErrorKey = @"error"; + +@implementation TGBridgeResponse + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _subscriptionIdentifier = [aDecoder decodeInt64ForKey:TGBridgeResponseSubscriptionIdentifier]; + _type = [aDecoder decodeInt32ForKey:TGBridgeResponseTypeKey]; + _next = [aDecoder decodeObjectForKey:TGBridgeResponseNextKey]; + _error = [aDecoder decodeObjectForKey:TGBridgeResponseErrorKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.subscriptionIdentifier forKey:TGBridgeResponseSubscriptionIdentifier]; + [aCoder encodeInt32:self.type forKey:TGBridgeResponseTypeKey]; + [aCoder encodeObject:self.next forKey:TGBridgeResponseNextKey]; + [aCoder encodeObject:self.error forKey:TGBridgeResponseErrorKey]; +} + ++ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription +{ + TGBridgeResponse *response = [[TGBridgeResponse alloc] init]; + response->_subscriptionIdentifier = subscription.identifier; + response->_type = TGBridgeResponseTypeNext; + response->_next = next; + return response; +} + ++ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription +{ + TGBridgeResponse *response = [[TGBridgeResponse alloc] init]; + response->_subscriptionIdentifier = subscription.identifier; + response->_type = TGBridgeResponseTypeFailed; + response->_error = error; + return response; +} + ++ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription +{ + TGBridgeResponse *response = [[TGBridgeResponse alloc] init]; + response->_subscriptionIdentifier = subscription.identifier; + response->_type = TGBridgeResponseTypeCompleted; + return response; +} + +@end + + +NSString *const TGBridgeSubscriptionIdentifierKey = @"identifier"; +NSString *const TGBridgeSubscriptionNameKey = @"name"; +NSString *const TGBridgeSubscriptionParametersKey = @"parameters"; + +@implementation TGBridgeSubscription + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + int64_t randomId = 0; + arc4random_buf(&randomId, sizeof(int64_t)); + _identifier = randomId; + _name = [[self class] subscriptionName]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey]; + _name = [aDecoder decodeObjectForKey:TGBridgeSubscriptionNameKey]; + [self _unserializeParametersWithCoder:aDecoder]; + } + return self; +} + +- (bool)synchronous +{ + return false; +} + +- (bool)renewable +{ + return true; +} + +- (bool)dropPreviouslyQueued +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)__unused aCoder +{ + +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)__unused aDecoder +{ + +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey]; + [aCoder encodeObject:self.name forKey:TGBridgeSubscriptionNameKey]; + [self _serializeParametersWithCoder:aCoder]; +} + ++ (NSString *)subscriptionName +{ + return nil; +} + +@end + + +@implementation TGBridgeDisposal + +- (instancetype)initWithIdentifier:(int64_t)identifier +{ + self = [super init]; + if (self != nil) + { + _identifier = identifier; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey]; +} + +@end + +NSString *const TGBridgeFileDataKey = @"data"; +NSString *const TGBridgeFileMetadataKey = @"metadata"; + +@implementation TGBridgeFile + +- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata +{ + self = [super init]; + if (self != nil) + { + _data = data; + _metadata = metadata; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _data = [aDecoder decodeObjectForKey:TGBridgeFileDataKey]; + _metadata = [aDecoder decodeObjectForKey:TGBridgeFileMetadataKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.data forKey:TGBridgeFileDataKey]; + [aCoder encodeObject:self.metadata forKey:TGBridgeFileMetadataKey]; +} + +@end + + +NSString *const TGBridgeSessionIdKey = @"sessionId"; + +@implementation TGBridgePing + +- (instancetype)initWithSessionId:(int32_t)sessionId +{ + self = [super init]; + if (self != nil) + { + _sessionId = sessionId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey]; +} + +@end + + +@implementation TGBridgeSubscriptionListRequest + +- (instancetype)initWithSessionId:(int32_t)sessionId +{ + self = [super init]; + if (self != nil) + { + _sessionId = sessionId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey]; +} + +@end + + +NSString *const TGBridgeSubscriptionListSubscriptionsKey = @"subscriptions"; + +@implementation TGBridgeSubscriptionList + +- (instancetype)initWithArray:(NSArray *)array +{ + self = [super init]; + if (self != nil) + { + _subscriptions = array; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _subscriptions = [aDecoder decodeObjectForKey:TGBridgeSubscriptionListSubscriptionsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.subscriptions forKey:TGBridgeSubscriptionListSubscriptionsKey]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeContactMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeContactMediaAttachment.m new file mode 100644 index 0000000000..4b2f482eaa --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeContactMediaAttachment.m @@ -0,0 +1,60 @@ +#import "TGBridgeContactMediaAttachment.h" + +//#import "../Extension/TGStringUtils.h" + +const NSInteger TGBridgeContactMediaAttachmentType = 0xB90A5663; + +NSString *const TGBridgeContactMediaUidKey = @"uid"; +NSString *const TGBridgeContactMediaFirstNameKey = @"firstName"; +NSString *const TGBridgeContactMediaLastNameKey = @"lastName"; +NSString *const TGBridgeContactMediaPhoneNumberKey = @"phoneNumber"; +NSString *const TGBridgeContactMediaPrettyPhoneNumberKey = @"prettyPhoneNumber"; + +@implementation TGBridgeContactMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _uid = [aDecoder decodeInt32ForKey:TGBridgeContactMediaUidKey]; + _firstName = [aDecoder decodeObjectForKey:TGBridgeContactMediaFirstNameKey]; + _lastName = [aDecoder decodeObjectForKey:TGBridgeContactMediaLastNameKey]; + _phoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPhoneNumberKey]; + _prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPrettyPhoneNumberKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.uid forKey:TGBridgeContactMediaUidKey]; + [aCoder encodeObject:self.firstName forKey:TGBridgeContactMediaFirstNameKey]; + [aCoder encodeObject:self.lastName forKey:TGBridgeContactMediaLastNameKey]; + [aCoder encodeObject:self.phoneNumber forKey:TGBridgeContactMediaPhoneNumberKey]; + [aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeContactMediaPrettyPhoneNumberKey]; +} + +- (NSString *)displayName +{ + NSString *firstName = self.firstName; + NSString *lastName = self.lastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + { + return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; + } + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + ++ (NSInteger)mediaType +{ + return TGBridgeContactMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeContext.m b/submodules/WatchCommon/Host/Sources/TGBridgeContext.m new file mode 100644 index 0000000000..4b0600e2fc --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeContext.m @@ -0,0 +1,101 @@ +#import "TGBridgeContext.h" +#import "TGBridgeCommon.h" +//#import "TGWatchCommon.h" + +NSString *const TGBridgeContextAuthorized = @"authorized"; +NSString *const TGBridgeContextUserId = @"userId"; +NSString *const TGBridgeContextMicAccessAllowed = @"micAccessAllowed"; +NSString *const TGBridgeContextStartupData = @"startupData"; +NSString *const TGBridgeContextStartupDataVersion = @"version"; + +@implementation TGBridgeContext + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + self = [super init]; + if (self != nil) + { + _authorized = [dictionary[TGBridgeContextAuthorized] boolValue]; + _userId = (int32_t)[dictionary[TGBridgeContextUserId] intValue]; + _micAccessAllowed = [dictionary[TGBridgeContextMicAccessAllowed] boolValue]; + + if (dictionary[TGBridgeContextStartupData] != nil) { + _preheatData = [NSKeyedUnarchiver unarchiveObjectWithData:dictionary[TGBridgeContextStartupData]]; + _preheatVersion = [dictionary[TGBridgeContextStartupDataVersion] integerValue]; + } + } + return self; +} + +- (NSDictionary *)dictionary +{ + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; + dictionary[TGBridgeContextAuthorized] = @(self.authorized); + dictionary[TGBridgeContextUserId] = @(self.userId); + dictionary[TGBridgeContextMicAccessAllowed] = @(self.micAccessAllowed); + if (self.preheatData != nil) { + dictionary[TGBridgeContextStartupData] = [NSKeyedArchiver archivedDataWithRootObject:self.preheatData]; + dictionary[TGBridgeContextStartupDataVersion] = @(self.preheatVersion); + } + return dictionary; +} + +- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId +{ + TGBridgeContext *context = [[TGBridgeContext alloc] init]; + context->_authorized = authorized; + context->_userId = peerId; + context->_micAccessAllowed = self.micAccessAllowed; + if (authorized) { + context->_preheatData = self.preheatData; + context->_preheatVersion = self.preheatVersion; + } + return context; +} + +- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data +{ + TGBridgeContext *context = [[TGBridgeContext alloc] init]; + context->_authorized = self.authorized; + context->_userId = self.userId; + context->_micAccessAllowed = self.micAccessAllowed; + if (data != nil) { + context->_preheatData = data; + context->_preheatVersion = (int32_t)[NSDate date].timeIntervalSinceReferenceDate; + } + return context; +} + +- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed +{ + TGBridgeContext *context = [[TGBridgeContext alloc] init]; + context->_authorized = self.authorized; + context->_userId = self.userId; + context->_micAccessAllowed = allowed; + context->_preheatData = self.preheatData; + context->_preheatVersion = self.preheatVersion; + return context; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return true; + + if (!object || ![object isKindOfClass:[self class]]) + return false; + + TGBridgeContext *context = (TGBridgeContext *)object; + if (context.authorized != self.authorized) + return false; + if (context.userId != self.userId) + return false; + if (context.micAccessAllowed != self.micAccessAllowed) + return false; + if (context.preheatVersion != self.preheatVersion) + return false; + + return true; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeDocumentMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeDocumentMediaAttachment.m new file mode 100644 index 0000000000..8d492ae704 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeDocumentMediaAttachment.m @@ -0,0 +1,84 @@ +#import "TGBridgeDocumentMediaAttachment.h" + +const NSInteger TGBridgeDocumentMediaAttachmentType = 0xE6C64318; + +NSString *const TGBridgeDocumentMediaDocumentIdKey = @"documentId"; +NSString *const TGBridgeDocumentMediaLocalDocumentIdKey = @"localDocumentId"; +NSString *const TGBridgeDocumentMediaFileSizeKey = @"fileSize"; +NSString *const TGBridgeDocumentMediaFileNameKey = @"fileName"; +NSString *const TGBridgeDocumentMediaImageSizeKey = @"imageSize"; +NSString *const TGBridgeDocumentMediaAnimatedKey = @"animated"; +NSString *const TGBridgeDocumentMediaStickerKey = @"sticker"; +NSString *const TGBridgeDocumentMediaStickerAltKey = @"stickerAlt"; +NSString *const TGBridgeDocumentMediaStickerPackIdKey = @"stickerPackId"; +NSString *const TGBridgeDocumentMediaStickerPackAccessHashKey = @"stickerPackAccessHash"; +NSString *const TGBridgeDocumentMediaAudioKey = @"audio"; +NSString *const TGBridgeDocumentMediaAudioTitleKey = @"title"; +NSString *const TGBridgeDocumentMediaAudioPerformerKey = @"performer"; +NSString *const TGBridgeDocumentMediaAudioVoice = @"voice"; +NSString *const TGBridgeDocumentMediaAudioDuration = @"duration"; + +@implementation TGBridgeDocumentMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _documentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaDocumentIdKey]; + _localDocumentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaLocalDocumentIdKey]; + _fileSize = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaFileSizeKey]; + _fileName = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaFileNameKey]; + _imageSize = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaImageSizeKey]; + _isAnimated = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAnimatedKey]; + _isSticker = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaStickerKey]; + _stickerAlt = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaStickerAltKey]; + _stickerPackId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackIdKey]; + _stickerPackAccessHash = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackAccessHashKey]; + _isAudio = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioKey]; + _title = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioTitleKey]; + _performer = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioPerformerKey]; + _isVoice = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioVoice]; + _duration = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaAudioDuration]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.documentId forKey:TGBridgeDocumentMediaDocumentIdKey]; + [aCoder encodeInt64:self.localDocumentId forKey:TGBridgeDocumentMediaLocalDocumentIdKey]; + [aCoder encodeInt32:self.fileSize forKey:TGBridgeDocumentMediaFileSizeKey]; + [aCoder encodeObject:self.fileName forKey:TGBridgeDocumentMediaFileNameKey]; + [aCoder encodeObject:self.imageSize forKey:TGBridgeDocumentMediaImageSizeKey]; + [aCoder encodeBool:self.isAnimated forKey:TGBridgeDocumentMediaAnimatedKey]; + [aCoder encodeBool:self.isSticker forKey:TGBridgeDocumentMediaStickerKey]; + [aCoder encodeObject:self.stickerAlt forKey:TGBridgeDocumentMediaStickerAltKey]; + [aCoder encodeInt64:self.stickerPackId forKey:TGBridgeDocumentMediaStickerPackIdKey]; + [aCoder encodeInt64:self.stickerPackAccessHash forKey:TGBridgeDocumentMediaStickerPackAccessHashKey]; + [aCoder encodeBool:self.isAudio forKey:TGBridgeDocumentMediaAudioKey]; + [aCoder encodeObject:self.title forKey:TGBridgeDocumentMediaAudioTitleKey]; + [aCoder encodeObject:self.performer forKey:TGBridgeDocumentMediaAudioPerformerKey]; + [aCoder encodeBool:self.isVoice forKey:TGBridgeDocumentMediaAudioVoice]; + [aCoder encodeInt32:self.duration forKey:TGBridgeDocumentMediaAudioDuration]; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeDocumentMediaAttachment *document = (TGBridgeDocumentMediaAttachment *)object; + + return (self.localDocumentId == 0 && self.documentId == document.documentId) || (self.localDocumentId != 0 && self.localDocumentId == document.localDocumentId); +} + ++ (NSInteger)mediaType +{ + return TGBridgeDocumentMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeForwardedMessageMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeForwardedMessageMediaAttachment.m new file mode 100644 index 0000000000..169e261cff --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeForwardedMessageMediaAttachment.m @@ -0,0 +1,35 @@ +#import "TGBridgeForwardedMessageMediaAttachment.h" + +const NSInteger TGBridgeForwardedMessageMediaAttachmentType = 0xAA1050C1; + +NSString *const TGBridgeForwardedMessageMediaPeerIdKey = @"peerId"; +NSString *const TGBridgeForwardedMessageMediaMidKey = @"mid"; +NSString *const TGBridgeForwardedMessageMediaDateKey = @"date"; + +@implementation TGBridgeForwardedMessageMediaAttachment + +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _peerId = [aDecoder decodeInt64ForKey:TGBridgeForwardedMessageMediaPeerIdKey]; + _mid = [aDecoder decodeInt32ForKey:TGBridgeForwardedMessageMediaMidKey]; + _date = [aDecoder decodeInt32ForKey:TGBridgeForwardedMessageMediaDateKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeForwardedMessageMediaPeerIdKey]; + [aCoder encodeInt32:self.mid forKey:TGBridgeForwardedMessageMediaMidKey]; + [aCoder encodeInt32:self.date forKey:TGBridgeForwardedMessageMediaDateKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeForwardedMessageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeImageMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeImageMediaAttachment.m new file mode 100644 index 0000000000..8ab5ec7044 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeImageMediaAttachment.m @@ -0,0 +1,33 @@ +#import "TGBridgeImageMediaAttachment.h" +#import + +const NSInteger TGBridgeImageMediaAttachmentType = 0x269BD8A8; + +NSString *const TGBridgeImageMediaImageIdKey = @"imageId"; +NSString *const TGBridgeImageMediaDimensionsKey = @"dimensions"; + +@implementation TGBridgeImageMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _imageId = [aDecoder decodeInt64ForKey:TGBridgeImageMediaImageIdKey]; + _dimensions = [aDecoder decodeCGSizeForKey:TGBridgeImageMediaDimensionsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.imageId forKey:TGBridgeImageMediaImageIdKey]; + [aCoder encodeCGSize:self.dimensions forKey:TGBridgeImageMediaDimensionsKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeImageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeLocationMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeLocationMediaAttachment.m new file mode 100644 index 0000000000..f6762eb549 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeLocationMediaAttachment.m @@ -0,0 +1,95 @@ +#import "TGBridgeLocationMediaAttachment.h" + +const NSInteger TGBridgeLocationMediaAttachmentType = 0x0C9ED06E; + +NSString *const TGBridgeLocationMediaLatitudeKey = @"lat"; +NSString *const TGBridgeLocationMediaLongitudeKey = @"lon"; +NSString *const TGBridgeLocationMediaVenueKey = @"venue"; + +NSString *const TGBridgeVenueTitleKey = @"title"; +NSString *const TGBridgeVenueAddressKey = @"address"; +NSString *const TGBridgeVenueProviderKey = @"provider"; +NSString *const TGBridgeVenueIdKey = @"venueId"; + +@implementation TGBridgeVenueAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _title = [aDecoder decodeObjectForKey:TGBridgeVenueTitleKey]; + _address = [aDecoder decodeObjectForKey:TGBridgeVenueAddressKey]; + _provider = [aDecoder decodeObjectForKey:TGBridgeVenueProviderKey]; + _venueId = [aDecoder decodeObjectForKey:TGBridgeVenueIdKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.title forKey:TGBridgeVenueTitleKey]; + [aCoder encodeObject:self.address forKey:TGBridgeVenueAddressKey]; + [aCoder encodeObject:self.provider forKey:TGBridgeVenueProviderKey]; + [aCoder encodeObject:self.venueId forKey:TGBridgeVenueIdKey]; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeVenueAttachment *venue = (TGBridgeVenueAttachment *)object; + + return [self.title isEqualToString:venue.title] && [self.address isEqualToString:venue.address] && [self.provider isEqualToString:venue.provider] && [self.venueId isEqualToString:venue.venueId]; +} + +@end + + +@implementation TGBridgeLocationMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _latitude = [aDecoder decodeDoubleForKey:TGBridgeLocationMediaLatitudeKey]; + _longitude = [aDecoder decodeDoubleForKey:TGBridgeLocationMediaLongitudeKey]; + _venue = [aDecoder decodeObjectForKey:TGBridgeLocationMediaVenueKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeDouble:self.latitude forKey:TGBridgeLocationMediaLatitudeKey]; + [aCoder encodeDouble:self.longitude forKey:TGBridgeLocationMediaLongitudeKey]; + [aCoder encodeObject:self.venue forKey:TGBridgeLocationMediaVenueKey]; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeLocationMediaAttachment *location = (TGBridgeLocationMediaAttachment *)object; + + bool equalCoord = (fabs(self.latitude - location.latitude) < DBL_EPSILON && fabs(self.longitude - location.longitude) < DBL_EPSILON); + bool equalVenue = (self.venue == nil && location.venue == nil) || ([self.venue isEqual:location.venue]); + + return equalCoord || equalVenue; +} + ++ (NSInteger)mediaType +{ + return TGBridgeLocationMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeLocationVenue.m b/submodules/WatchCommon/Host/Sources/TGBridgeLocationVenue.m new file mode 100644 index 0000000000..01c8fc8c1a --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeLocationVenue.m @@ -0,0 +1,66 @@ +#import "TGBridgeLocationVenue.h" + +#import "TGBridgeLocationMediaAttachment.h" + +NSString *const TGBridgeLocationVenueLatitudeKey = @"lat"; +NSString *const TGBridgeLocationVenueLongitudeKey = @"lon"; +NSString *const TGBridgeLocationVenueIdentifierKey = @"identifier"; +NSString *const TGBridgeLocationVenueProviderKey = @"provider"; +NSString *const TGBridgeLocationVenueNameKey = @"name"; +NSString *const TGBridgeLocationVenueAddressKey = @"address"; + +@implementation TGBridgeLocationVenue + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _coordinate = CLLocationCoordinate2DMake([aDecoder decodeDoubleForKey:TGBridgeLocationVenueLatitudeKey], [aDecoder decodeDoubleForKey:TGBridgeLocationVenueLongitudeKey]); + _identifier = [aDecoder decodeObjectForKey:TGBridgeLocationVenueIdentifierKey]; + _provider = [aDecoder decodeObjectForKey:TGBridgeLocationVenueProviderKey]; + _name = [aDecoder decodeObjectForKey:TGBridgeLocationVenueNameKey]; + _address = [aDecoder decodeObjectForKey:TGBridgeLocationVenueAddressKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeDouble:self.coordinate.latitude forKey:TGBridgeLocationVenueLatitudeKey]; + [aCoder encodeDouble:self.coordinate.longitude forKey:TGBridgeLocationVenueLongitudeKey]; + [aCoder encodeObject:self.identifier forKey:TGBridgeLocationVenueIdentifierKey]; + [aCoder encodeObject:self.provider forKey:TGBridgeLocationVenueProviderKey]; + [aCoder encodeObject:self.name forKey:TGBridgeLocationVenueNameKey]; + [aCoder encodeObject:self.address forKey:TGBridgeLocationVenueAddressKey]; +} + +- (TGBridgeLocationMediaAttachment *)locationAttachment +{ + TGBridgeLocationMediaAttachment *attachment = [[TGBridgeLocationMediaAttachment alloc] init]; + attachment.latitude = self.coordinate.latitude; + attachment.longitude = self.coordinate.longitude; + + TGBridgeVenueAttachment *venueAttachment = [[TGBridgeVenueAttachment alloc] init]; + venueAttachment.title = self.name; + venueAttachment.address = self.address; + venueAttachment.provider = self.provider; + venueAttachment.venueId = self.identifier; + + attachment.venue = venueAttachment; + + return attachment; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + return [self.identifier isEqualToString:((TGBridgeLocationVenue *)object).identifier]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeMediaAttachment.m new file mode 100644 index 0000000000..971a23b64d --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeMediaAttachment.m @@ -0,0 +1,32 @@ +#import "TGBridgeMediaAttachment.h" + +NSString *const TGBridgeMediaAttachmentTypeKey = @"type"; + +@implementation TGBridgeMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder +{ + self = [super init]; + if (self != nil) + { + + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder +{ + +} + +- (NSInteger)mediaType +{ + return 0; +} + ++ (NSInteger)mediaType +{ + return 0; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeMessage.m b/submodules/WatchCommon/Host/Sources/TGBridgeMessage.m new file mode 100644 index 0000000000..e66e3313b3 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeMessage.m @@ -0,0 +1,242 @@ +#import "TGBridgeMessage.h" +//#import "TGWatchCommon.h" +#import "TGBridgePeerIdAdapter.h" + +NSString *const TGBridgeMessageIdentifierKey = @"identifier"; +NSString *const TGBridgeMessageDateKey = @"date"; +NSString *const TGBridgeMessageRandomIdKey = @"randomId"; +NSString *const TGBridgeMessageFromUidKey = @"fromUid"; +NSString *const TGBridgeMessageCidKey = @"cid"; +NSString *const TGBridgeMessageTextKey = @"text"; +NSString *const TGBridgeMessageUnreadKey = @"unread"; +NSString *const TGBridgeMessageOutgoingKey = @"outgoing"; +NSString *const TGBridgeMessageMediaKey = @"media"; +NSString *const TGBridgeMessageDeliveryStateKey = @"deliveryState"; +NSString *const TGBridgeMessageForceReplyKey = @"forceReply"; + +NSString *const TGBridgeMessageKey = @"message"; +NSString *const TGBridgeMessagesArrayKey = @"messages"; + +@interface TGBridgeMessage () +{ + NSArray *_textCheckingResults; +} +@end + +@implementation TGBridgeMessage + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt32ForKey:TGBridgeMessageIdentifierKey]; + _date = [aDecoder decodeDoubleForKey:TGBridgeMessageDateKey]; + _randomId = [aDecoder decodeInt64ForKey:TGBridgeMessageRandomIdKey]; + _fromUid = [aDecoder decodeInt64ForKey:TGBridgeMessageFromUidKey]; + _cid = [aDecoder decodeInt64ForKey:TGBridgeMessageCidKey]; + _text = [aDecoder decodeObjectForKey:TGBridgeMessageTextKey]; + _outgoing = [aDecoder decodeBoolForKey:TGBridgeMessageOutgoingKey]; + _unread = [aDecoder decodeBoolForKey:TGBridgeMessageUnreadKey]; + _deliveryState = [aDecoder decodeInt32ForKey:TGBridgeMessageDeliveryStateKey]; + _media = [aDecoder decodeObjectForKey:TGBridgeMessageMediaKey]; + _forceReply = [aDecoder decodeBoolForKey:TGBridgeMessageForceReplyKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.identifier forKey:TGBridgeMessageIdentifierKey]; + [aCoder encodeDouble:self.date forKey:TGBridgeMessageDateKey]; + [aCoder encodeInt64:self.randomId forKey:TGBridgeMessageRandomIdKey]; + [aCoder encodeInt64:self.fromUid forKey:TGBridgeMessageFromUidKey]; + [aCoder encodeInt64:self.cid forKey:TGBridgeMessageCidKey]; + [aCoder encodeObject:self.text forKey:TGBridgeMessageTextKey]; + [aCoder encodeBool:self.outgoing forKey:TGBridgeMessageOutgoingKey]; + [aCoder encodeBool:self.unread forKey:TGBridgeMessageUnreadKey]; + [aCoder encodeInt32:self.deliveryState forKey:TGBridgeMessageDeliveryStateKey]; + [aCoder encodeObject:self.media forKey:TGBridgeMessageMediaKey]; + [aCoder encodeBool:self.forceReply forKey:TGBridgeMessageForceReplyKey]; +} + +- (NSArray *)involvedUserIds +{ + NSMutableSet *userIds = [[NSMutableSet alloc] init]; + if (!TGPeerIdIsChannel(self.fromUid)) + [userIds addObject:[NSNumber numberWithLongLong:self.fromUid]]; + + for (TGBridgeMediaAttachment *attachment in self.media) + { + if ([attachment isKindOfClass:[TGBridgeContactMediaAttachment class]]) + { + TGBridgeContactMediaAttachment *contactAttachment = (TGBridgeContactMediaAttachment *)attachment; + if (contactAttachment.uid != 0) + [userIds addObject:[NSNumber numberWithLongLong:contactAttachment.uid]]; + } + else if ([attachment isKindOfClass:[TGBridgeForwardedMessageMediaAttachment class]]) + { + TGBridgeForwardedMessageMediaAttachment *forwardAttachment = (TGBridgeForwardedMessageMediaAttachment *)attachment; + if (forwardAttachment.peerId != 0 && !TGPeerIdIsChannel(forwardAttachment.peerId)) + [userIds addObject:[NSNumber numberWithLongLong:forwardAttachment.peerId]]; + } + else if ([attachment isKindOfClass:[TGBridgeReplyMessageMediaAttachment class]]) + { + TGBridgeReplyMessageMediaAttachment *replyAttachment = (TGBridgeReplyMessageMediaAttachment *)attachment; + if (replyAttachment.message != nil && !TGPeerIdIsChannel(replyAttachment.message.fromUid)) + [userIds addObject:[NSNumber numberWithLongLong:replyAttachment.message.fromUid]]; + } + else if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]]) + { + TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment; + if (actionAttachment.actionData[@"uid"] != nil) + [userIds addObject:[NSNumber numberWithLongLong:[actionAttachment.actionData[@"uid"] intValue]]]; + } + } + + NSMutableArray *result = [[NSMutableArray alloc] init]; + for (NSNumber *object in userIds) { + [result addObject:object]; + } + return result; +} + +- (NSArray *)textCheckingResults +{ + if (_textCheckingResults == nil) + { + NSMutableArray *results = [[NSMutableArray alloc] init]; + + NSArray *entities = nil; + for (TGBridgeMediaAttachment *attachment in self.media) + { + if ([attachment isKindOfClass:[TGBridgeMessageEntitiesAttachment class]]) + { + entities = ((TGBridgeMessageEntitiesAttachment *)attachment).entities; + break; + } + } + + for (TGBridgeMessageEntity *entity in entities) + { + TGBridgeTextCheckingResult *result = [[TGBridgeTextCheckingResult alloc] init]; + result.range = entity.range; + + if ([entity isKindOfClass:[TGBridgeMessageEntityBold class]]) + result.type = TGBridgeTextCheckingResultTypeBold; + else if ([entity isKindOfClass:[TGBridgeMessageEntityItalic class]]) + result.type = TGBridgeTextCheckingResultTypeItalic; + else if ([entity isKindOfClass:[TGBridgeMessageEntityCode class]]) + result.type = TGBridgeTextCheckingResultTypeCode; + else if ([entity isKindOfClass:[TGBridgeMessageEntityPre class]]) + result.type = TGBridgeTextCheckingResultTypePre; + + if (result.type != TGBridgeTextCheckingResultTypeUndefined) + [results addObject:result]; + } + + _textCheckingResults = results; + } + + return _textCheckingResults; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeMessage *message = (TGBridgeMessage *)object; + + if (self.randomId != 0) + return self.randomId == message.randomId; + else + return self.identifier == message.identifier; +} + ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId +{ + return [self temporaryNewMessageForText:text userId:userId replyToMessage:nil]; +} + ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId replyToMessage:(TGBridgeMessage *)replyToMessage +{ + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + + int32_t messageId = 0; + arc4random_buf(&messageId, 4); + + TGBridgeMessage *message = [[TGBridgeMessage alloc] init]; + message->_identifier = -abs(messageId); + message->_fromUid = userId; + message->_randomId = randomId; + message->_unread = true; + message->_outgoing = true; + message->_deliveryState = TGBridgeMessageDeliveryStatePending; + message->_text = text; + message->_date = [[NSDate date] timeIntervalSince1970]; + + if (replyToMessage != nil) + { + TGBridgeReplyMessageMediaAttachment *replyAttachment = [[TGBridgeReplyMessageMediaAttachment alloc] init]; + replyAttachment.mid = replyToMessage.identifier; + replyAttachment.message = replyToMessage; + + message->_media = @[ replyToMessage ]; + } + + return message; +} + ++ (instancetype)temporaryNewMessageForSticker:(TGBridgeDocumentMediaAttachment *)sticker userId:(int32_t)userId +{ + return [self _temporaryNewMessageForMediaAttachment:sticker userId:userId]; +} + ++ (instancetype)temporaryNewMessageForLocation:(TGBridgeLocationMediaAttachment *)location userId:(int32_t)userId +{ + return [self _temporaryNewMessageForMediaAttachment:location userId:userId]; +} + ++ (instancetype)temporaryNewMessageForAudioWithDuration:(int32_t)duration userId:(int32_t)userId localAudioId:(int64_t)localAudioId +{ + TGBridgeDocumentMediaAttachment *document = [[TGBridgeDocumentMediaAttachment alloc] init]; + document.isAudio = true; + document.isVoice = true; + document.localDocumentId = localAudioId; + document.duration = duration; + + return [self _temporaryNewMessageForMediaAttachment:document userId:userId]; +} + ++ (instancetype)_temporaryNewMessageForMediaAttachment:(TGBridgeMediaAttachment *)attachment userId:(int32_t)userId +{ + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + + int32_t messageId = 0; + arc4random_buf(&messageId, 4); + + TGBridgeMessage *message = [[TGBridgeMessage alloc] init]; + message->_identifier = -abs(messageId); + message->_fromUid = userId; + message->_unread = true; + message->_outgoing = true; + message->_deliveryState = TGBridgeMessageDeliveryStatePending; + message->_date = [[NSDate date] timeIntervalSince1970]; + + message->_media = @[ attachment ]; + + return message; +} + +@end + + +@implementation TGBridgeTextCheckingResult + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeMessageEntities.m b/submodules/WatchCommon/Host/Sources/TGBridgeMessageEntities.m new file mode 100644 index 0000000000..8d639a9468 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeMessageEntities.m @@ -0,0 +1,83 @@ +#import "TGBridgeMessageEntities.h" + +NSString *const TGBridgeMessageEntityLocationKey = @"loc"; +NSString *const TGBridgeMessageEntityLengthKey = @"len"; + +@implementation TGBridgeMessageEntity + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + NSUInteger loc = [aDecoder decodeIntegerForKey:TGBridgeMessageEntityLocationKey]; + NSUInteger len = [aDecoder decodeIntegerForKey:TGBridgeMessageEntityLengthKey]; + _range = NSMakeRange(loc, len); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInteger:self.range.location forKey:TGBridgeMessageEntityLocationKey]; + [aCoder encodeInteger:self.range.length forKey:TGBridgeMessageEntityLengthKey]; +} + ++ (instancetype)entitityWithRange:(NSRange)range +{ + TGBridgeMessageEntity *entity = [[self alloc] init]; + entity.range = range; + return entity; +} + +@end + + +@implementation TGBridgeMessageEntityUrl + +@end + + +@implementation TGBridgeMessageEntityEmail + +@end + + +@implementation TGBridgeMessageEntityTextUrl + +@end + + +@implementation TGBridgeMessageEntityMention + +@end + + +@implementation TGBridgeMessageEntityHashtag + +@end + + +@implementation TGBridgeMessageEntityBotCommand + +@end + + +@implementation TGBridgeMessageEntityBold + +@end + + +@implementation TGBridgeMessageEntityItalic + +@end + + +@implementation TGBridgeMessageEntityCode + +@end + + +@implementation TGBridgeMessageEntityPre + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeMessageEntitiesAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeMessageEntitiesAttachment.m new file mode 100644 index 0000000000..fb5cb3b7d7 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeMessageEntitiesAttachment.m @@ -0,0 +1,30 @@ +#import "TGBridgeMessageEntitiesAttachment.h" + +const NSInteger TGBridgeMessageEntitiesAttachmentType = 0x8c2e3cce; + +NSString *const TGBridgeMessageEntitiesKey = @"entities"; + +@implementation TGBridgeMessageEntitiesAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _entities = [aDecoder decodeObjectForKey:TGBridgeMessageEntitiesKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.entities forKey:TGBridgeMessageEntitiesKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeMessageEntitiesAttachmentType; +} + + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgePeerNotificationSettings.m b/submodules/WatchCommon/Host/Sources/TGBridgePeerNotificationSettings.m new file mode 100644 index 0000000000..662c2f4ca4 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgePeerNotificationSettings.m @@ -0,0 +1,22 @@ +#import "TGBridgePeerNotificationSettings.h" + +NSString *const TGBridgePeerNotificationSettingsMuteForKey = @"muteFor"; + +@implementation TGBridgePeerNotificationSettings + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _muteFor = [aDecoder decodeInt32ForKey:TGBridgePeerNotificationSettingsMuteForKey]; + } + return self; +} + +- (void)encodeWithCoder:(nonnull NSCoder *)aCoder +{ + [aCoder encodeInt32:self.muteFor forKey:TGBridgePeerNotificationSettingsMuteForKey]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeReplyMarkupMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeReplyMarkupMediaAttachment.m new file mode 100644 index 0000000000..1299c900cd --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeReplyMarkupMediaAttachment.m @@ -0,0 +1,29 @@ +#import "TGBridgeReplyMarkupMediaAttachment.h" + +const NSInteger TGBridgeReplyMarkupMediaAttachmentType = 0x5678acc1; + +NSString *const TGBridgeReplyMarkupMediaMessageKey = @"replyMarkup"; + +@implementation TGBridgeReplyMarkupMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _replyMarkup = [aDecoder decodeObjectForKey:TGBridgeReplyMarkupMediaMessageKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.replyMarkup forKey:TGBridgeReplyMarkupMediaMessageKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeReplyMarkupMediaAttachmentType; +} + +@end \ No newline at end of file diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeReplyMessageMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeReplyMessageMediaAttachment.m new file mode 100644 index 0000000000..fbc2456919 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeReplyMessageMediaAttachment.m @@ -0,0 +1,33 @@ +#import "TGBridgeReplyMessageMediaAttachment.h" +#import "TGBridgeMessage.h" + +const NSInteger TGBridgeReplyMessageMediaAttachmentType = 414002169; + +NSString *const TGBridgeReplyMessageMediaMidKey = @"mid"; +NSString *const TGBridgeReplyMessageMediaMessageKey = @"message"; + +@implementation TGBridgeReplyMessageMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _mid = [aDecoder decodeInt32ForKey:TGBridgeReplyMessageMediaMidKey]; + _message = [aDecoder decodeObjectForKey:TGBridgeReplyMessageMediaMessageKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.mid forKey:TGBridgeReplyMessageMediaMidKey]; + [aCoder encodeObject:self.message forKey:TGBridgeReplyMessageMediaMessageKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeReplyMessageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeSubscriptions.m b/submodules/WatchCommon/Host/Sources/TGBridgeSubscriptions.m new file mode 100644 index 0000000000..8c4b50d224 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeSubscriptions.m @@ -0,0 +1,1048 @@ +#import "TGBridgeSubscriptions.h" + +#import + +#import "TGBridgeImageMediaAttachment.h" +#import "TGBridgeVideoMediaAttachment.h" +#import "TGBridgeDocumentMediaAttachment.h" +#import "TGBridgeLocationMediaAttachment.h" +#import "TGBridgePeerNotificationSettings.h" + +NSString *const TGBridgeAudioSubscriptionName = @"media.audio"; +NSString *const TGBridgeAudioSubscriptionAttachmentKey = @"attachment"; +NSString *const TGBridgeAudioSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeAudioSubscriptionMessageIdKey = @"messageId"; + +@implementation TGBridgeAudioSubscription + +- (instancetype)initWithAttachment:(TGBridgeMediaAttachment *)attachment peerId:(int64_t)peerId messageId:(int32_t)messageId +{ + self = [super init]; + if (self != nil) + { + _attachment = attachment; + _peerId = peerId; + _messageId = messageId; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.attachment forKey:TGBridgeAudioSubscriptionAttachmentKey]; + [aCoder encodeInt64:self.peerId forKey:TGBridgeAudioSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeAudioSubscriptionMessageIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _attachment = [aDecoder decodeObjectForKey:TGBridgeAudioSubscriptionAttachmentKey]; + _peerId = [aDecoder decodeInt64ForKey:TGBridgeAudioSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeAudioSubscriptionMessageIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeAudioSubscriptionName; +} + +@end + + +NSString *const TGBridgeAudioSentSubscriptionName = @"media.audioSent"; +NSString *const TGBridgeAudioSentSubscriptionConversationIdKey = @"conversationId"; + +@implementation TGBridgeAudioSentSubscription + +- (instancetype)initWithConversationId:(int64_t)conversationId +{ + self = [super init]; + if (self != nil) + { + _conversationId = conversationId; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.conversationId forKey:TGBridgeAudioSentSubscriptionConversationIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _conversationId = [aDecoder decodeInt64ForKey:TGBridgeAudioSentSubscriptionConversationIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeAudioSentSubscriptionName; +} + +@end + + +NSString *const TGBridgeChatListSubscriptionName = @"chats.chatList"; +NSString *const TGBridgeChatListSubscriptionLimitKey = @"limit"; + +@implementation TGBridgeChatListSubscription + +- (instancetype)initWithLimit:(int32_t)limit +{ + self = [super init]; + if (self != nil) + { + _limit = limit; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.limit forKey:TGBridgeChatListSubscriptionLimitKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _limit = [aDecoder decodeInt32ForKey:TGBridgeChatListSubscriptionLimitKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeChatListSubscriptionName; +} + +@end + + +NSString *const TGBridgeChatMessageListSubscriptionName = @"chats.chatMessageList"; +NSString *const TGBridgeChatMessageListSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeChatMessageListSubscriptionAtMessageIdKey = @"atMessageId"; +NSString *const TGBridgeChatMessageListSubscriptionRangeMessageCountKey = @"rangeMessageCount"; + +@implementation TGBridgeChatMessageListSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId atMessageId:(int32_t)messageId rangeMessageCount:(NSUInteger)rangeMessageCount +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _atMessageId = messageId; + _rangeMessageCount = rangeMessageCount; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeChatMessageListSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.atMessageId forKey:TGBridgeChatMessageListSubscriptionAtMessageIdKey]; + [aCoder encodeInt32:(int32_t)self.rangeMessageCount forKey:TGBridgeChatMessageListSubscriptionRangeMessageCountKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeChatMessageListSubscriptionPeerIdKey]; + _atMessageId = [aDecoder decodeInt32ForKey:TGBridgeChatMessageListSubscriptionAtMessageIdKey]; + _rangeMessageCount = [aDecoder decodeInt32ForKey:TGBridgeChatMessageListSubscriptionRangeMessageCountKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeChatMessageListSubscriptionName; +} + +@end + + +NSString *const TGBridgeChatMessageSubscriptionName = @"chats.message"; +NSString *const TGBridgeChatMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeChatMessageSubscriptionMessageIdKey = @"mid"; + +@implementation TGBridgeChatMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + } + return self; +} + +- (bool)synchronous +{ + return true; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeChatMessageSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeChatMessageSubscriptionMessageIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeChatMessageSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeChatMessageSubscriptionMessageIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeChatMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeReadChatMessageListSubscriptionName = @"chats.readChatMessageList"; +NSString *const TGBridgeReadChatMessageListSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeReadChatMessageListSubscriptionMessageIdKey = @"mid"; + +@implementation TGBridgeReadChatMessageListSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeReadChatMessageListSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeReadChatMessageListSubscriptionMessageIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeReadChatMessageListSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeReadChatMessageListSubscriptionMessageIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeReadChatMessageListSubscriptionName; +} + +@end + + +NSString *const TGBridgeContactsSubscriptionName = @"contacts.search"; +NSString *const TGBridgeContactsSubscriptionQueryKey = @"query"; + +@implementation TGBridgeContactsSubscription + +- (instancetype)initWithQuery:(NSString *)query +{ + self = [super init]; + if (self != nil) + { + _query = query; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.query forKey:TGBridgeContactsSubscriptionQueryKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _query = [aDecoder decodeObjectForKey:TGBridgeContactsSubscriptionQueryKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeContactsSubscriptionName; +} + +@end + + +NSString *const TGBridgeConversationSubscriptionName = @"chats.conversation"; +NSString *const TGBridgeConversationSubscriptionPeerIdKey = @"peerId"; + +@implementation TGBridgeConversationSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeConversationSubscriptionPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeConversationSubscriptionPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeConversationSubscriptionName; +} + +@end + + +NSString *const TGBridgeNearbyVenuesSubscriptionName = @"location.nearbyVenues"; +NSString *const TGBridgeNearbyVenuesSubscriptionLatitudeKey = @"lat"; +NSString *const TGBridgeNearbyVenuesSubscriptionLongitudeKey = @"lon"; +NSString *const TGBridgeNearbyVenuesSubscriptionLimitKey = @"limit"; + +@implementation TGBridgeNearbyVenuesSubscription + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate limit:(int32_t)limit +{ + self = [super init]; + if (self != nil) + { + _coordinate = coordinate; + _limit = limit; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeDouble:self.coordinate.latitude forKey:TGBridgeNearbyVenuesSubscriptionLatitudeKey]; + [aCoder encodeDouble:self.coordinate.longitude forKey:TGBridgeNearbyVenuesSubscriptionLongitudeKey]; + [aCoder encodeInt32:self.limit forKey:TGBridgeNearbyVenuesSubscriptionLimitKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _coordinate = CLLocationCoordinate2DMake([aDecoder decodeDoubleForKey:TGBridgeNearbyVenuesSubscriptionLatitudeKey], + [aDecoder decodeDoubleForKey:TGBridgeNearbyVenuesSubscriptionLongitudeKey]); + _limit = [aDecoder decodeInt32ForKey:TGBridgeNearbyVenuesSubscriptionLimitKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeNearbyVenuesSubscriptionName; +} + +@end + + +NSString *const TGBridgeMediaThumbnailSubscriptionName = @"media.thumbnail"; +NSString *const TGBridgeMediaThumbnailPeerIdKey = @"peerId"; +NSString *const TGBridgeMediaThumbnailMessageIdKey = @"mid"; +NSString *const TGBridgeMediaThumbnailSizeKey = @"size"; +NSString *const TGBridgeMediaThumbnailNotificationKey = @"notification"; + +@implementation TGBridgeMediaThumbnailSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId size:(CGSize)size notification:(bool)notification +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + _size = size; + _notification = notification; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeMediaThumbnailPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeMediaThumbnailMessageIdKey]; + [aCoder encodeCGSize:self.size forKey:TGBridgeMediaThumbnailSizeKey]; + [aCoder encodeBool:self.notification forKey:TGBridgeMediaThumbnailNotificationKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeMediaThumbnailPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeMediaThumbnailMessageIdKey]; + _size = [aDecoder decodeCGSizeForKey:TGBridgeMediaThumbnailSizeKey]; + _notification = [aDecoder decodeBoolForKey:TGBridgeMediaThumbnailNotificationKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeMediaThumbnailSubscriptionName; +} + +@end + + +NSString *const TGBridgeMediaAvatarSubscriptionName = @"media.avatar"; +NSString *const TGBridgeMediaAvatarPeerIdKey = @"peerId"; +NSString *const TGBridgeMediaAvatarUrlKey = @"url"; +NSString *const TGBridgeMediaAvatarTypeKey = @"type"; + +@implementation TGBridgeMediaAvatarSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId url:(NSString *)url type:(TGBridgeMediaAvatarType)type +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _url = url; + _type = type; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeMediaAvatarPeerIdKey]; + [aCoder encodeObject:self.url forKey:TGBridgeMediaAvatarUrlKey]; + [aCoder encodeInt32:self.type forKey:TGBridgeMediaAvatarTypeKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeMediaAvatarPeerIdKey]; + _url = [aDecoder decodeObjectForKey:TGBridgeMediaAvatarUrlKey]; + _type = [aDecoder decodeInt32ForKey:TGBridgeMediaAvatarTypeKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeMediaAvatarSubscriptionName; +} + +@end + + +NSString *const TGBridgeMediaStickerSubscriptionName = @"media.sticker"; +NSString *const TGBridgeMediaStickerDocumentIdKey = @"documentId"; +NSString *const TGBridgeMediaStickerPackIdKey = @"packId"; +NSString *const TGBridgeMediaStickerPackAccessHashKey = @"accessHash"; +NSString *const TGBridgeMediaStickerPeerIdKey = @"peerId"; +NSString *const TGBridgeMediaStickerMessageIdKey = @"mid"; +NSString *const TGBridgeMediaStickerNotificationKey = @"notification"; +NSString *const TGBridgeMediaStickerSizeKey = @"size"; + +@implementation TGBridgeMediaStickerSubscription + +- (instancetype)initWithDocumentId:(int64_t)documentId stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash stickerPeerId:(int64_t)stickerPeerId stickerMessageId:(int32_t)stickerMessageId notification:(bool)notification size:(CGSize)size +{ + self = [super init]; + if (self != nil) + { + _documentId = documentId; + _stickerPackId = stickerPackId; + _stickerPackAccessHash = stickerPackAccessHash; + _stickerPeerId = stickerPeerId; + _stickerMessageId = stickerMessageId; + _notification = notification; + _size = size; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.documentId forKey:TGBridgeMediaStickerDocumentIdKey]; + [aCoder encodeInt64:self.stickerPackId forKey:TGBridgeMediaStickerPackIdKey]; + [aCoder encodeInt64:self.stickerPackAccessHash forKey:TGBridgeMediaStickerPackAccessHashKey]; + [aCoder encodeInt64:self.stickerPeerId forKey:TGBridgeMediaStickerPeerIdKey]; + [aCoder encodeInt32:self.stickerMessageId forKey:TGBridgeMediaStickerMessageIdKey]; + [aCoder encodeBool:self.notification forKey:TGBridgeMediaStickerNotificationKey]; + [aCoder encodeCGSize:self.size forKey:TGBridgeMediaStickerSizeKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _documentId = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerDocumentIdKey]; + _stickerPackId = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerPackIdKey]; + _stickerPackAccessHash = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerPackAccessHashKey]; + _stickerPeerId = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerPeerIdKey]; + _stickerMessageId = [aDecoder decodeInt32ForKey:TGBridgeMediaStickerMessageIdKey]; + _notification = [aDecoder decodeBoolForKey:TGBridgeMediaStickerNotificationKey]; + _size = [aDecoder decodeCGSizeForKey:TGBridgeMediaStickerSizeKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeMediaStickerSubscriptionName; +} + +@end + + +NSString *const TGBridgePeerSettingsSubscriptionName = @"peer.settings"; +NSString *const TGBridgePeerSettingsSubscriptionPeerIdKey = @"peerId"; + +@implementation TGBridgePeerSettingsSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgePeerSettingsSubscriptionPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgePeerSettingsSubscriptionPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgePeerSettingsSubscriptionName; +} + +@end + + +NSString *const TGBridgePeerUpdateNotificationSettingsSubscriptionName = @"peer.notificationSettings"; +NSString *const TGBridgePeerUpdateNotificationSettingsSubscriptionPeerIdKey = @"peerId"; + +@implementation TGBridgePeerUpdateNotificationSettingsSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgePeerUpdateNotificationSettingsSubscriptionPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgePeerUpdateNotificationSettingsSubscriptionPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgePeerUpdateNotificationSettingsSubscriptionName; +} + +@end + + +NSString *const TGBridgePeerUpdateBlockStatusSubscriptionName = @"peer.updateBlocked"; +NSString *const TGBridgePeerUpdateBlockStatusSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgePeerUpdateBlockStatusSubscriptionBlockedKey = @"blocked"; + +@implementation TGBridgePeerUpdateBlockStatusSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId blocked:(bool)blocked +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _blocked = blocked; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgePeerUpdateBlockStatusSubscriptionPeerIdKey]; + [aCoder encodeBool:self.blocked forKey:TGBridgePeerUpdateBlockStatusSubscriptionBlockedKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgePeerUpdateBlockStatusSubscriptionPeerIdKey]; + _blocked = [aDecoder decodeBoolForKey:TGBridgePeerUpdateBlockStatusSubscriptionBlockedKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgePeerUpdateBlockStatusSubscriptionName; +} + +@end + + +NSString *const TGBridgeRemoteSubscriptionName = @"remote.request"; +NSString *const TGBridgeRemotePeerIdKey = @"peerId"; +NSString *const TGBridgeRemoteMessageIdKey = @"mid"; +NSString *const TGBridgeRemoteTypeKey = @"mediaType"; +NSString *const TGBridgeRemoteAutoPlayKey = @"autoPlay"; + +@implementation TGBridgeRemoteSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId type:(int32_t)type autoPlay:(bool)autoPlay +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + _type = type; + _autoPlay = autoPlay; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeRemotePeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeRemoteMessageIdKey]; + [aCoder encodeInt32:self.type forKey:TGBridgeRemoteTypeKey]; + [aCoder encodeBool:self.autoPlay forKey:TGBridgeRemoteAutoPlayKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeRemotePeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeRemoteMessageIdKey]; + _type = [aDecoder decodeInt32ForKey:TGBridgeRemoteTypeKey]; + _autoPlay = [aDecoder decodeBoolForKey:TGBridgeRemoteAutoPlayKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeRemoteSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendTextMessageSubscriptionName = @"sendMessage.text"; +NSString *const TGBridgeSendTextMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendTextMessageSubscriptionTextKey = @"text"; +NSString *const TGBridgeSendTextMessageSubscriptionReplyToMidKey = @"replyToMid"; + +@implementation TGBridgeSendTextMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId text:(NSString *)text replyToMid:(int32_t)replyToMid +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _text = text; + _replyToMid = replyToMid; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendTextMessageSubscriptionPeerIdKey]; + [aCoder encodeObject:self.text forKey:TGBridgeSendTextMessageSubscriptionTextKey]; + [aCoder encodeInt32:self.replyToMid forKey:TGBridgeSendTextMessageSubscriptionReplyToMidKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendTextMessageSubscriptionPeerIdKey]; + _text = [aDecoder decodeObjectForKey:TGBridgeSendTextMessageSubscriptionTextKey]; + _replyToMid = [aDecoder decodeInt32ForKey:TGBridgeSendTextMessageSubscriptionReplyToMidKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendTextMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendStickerMessageSubscriptionName = @"sendMessage.sticker"; +NSString *const TGBridgeSendStickerMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendStickerMessageSubscriptionDocumentKey = @"document"; +NSString *const TGBridgeSendStickerMessageSubscriptionReplyToMidKey = @"replyToMid"; + +@implementation TGBridgeSendStickerMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId document:(TGBridgeDocumentMediaAttachment *)document replyToMid:(int32_t)replyToMid +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _document = document; + _replyToMid = replyToMid; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendStickerMessageSubscriptionPeerIdKey]; + [aCoder encodeObject:self.document forKey:TGBridgeSendStickerMessageSubscriptionDocumentKey]; + [aCoder encodeInt32:self.replyToMid forKey:TGBridgeSendStickerMessageSubscriptionReplyToMidKey]; +} + + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendStickerMessageSubscriptionPeerIdKey]; + _document = [aDecoder decodeObjectForKey:TGBridgeSendStickerMessageSubscriptionDocumentKey]; + _replyToMid = [aDecoder decodeInt32ForKey:TGBridgeSendStickerMessageSubscriptionReplyToMidKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendStickerMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendLocationMessageSubscriptionName = @"sendMessage.location"; +NSString *const TGBridgeSendLocationMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendLocationMessageSubscriptionLocationKey = @"location"; +NSString *const TGBridgeSendLocationMessageSubscriptionReplyToMidKey = @"replyToMid"; + +@implementation TGBridgeSendLocationMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId location:(TGBridgeLocationMediaAttachment *)location replyToMid:(int32_t)replyToMid +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _location = location; + _replyToMid = replyToMid; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendLocationMessageSubscriptionPeerIdKey]; + [aCoder encodeObject:self.location forKey:TGBridgeSendLocationMessageSubscriptionLocationKey]; + [aCoder encodeInt32:self.replyToMid forKey:TGBridgeSendLocationMessageSubscriptionReplyToMidKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendLocationMessageSubscriptionPeerIdKey]; + _location = [aDecoder decodeObjectForKey:TGBridgeSendLocationMessageSubscriptionLocationKey]; + _replyToMid = [aDecoder decodeInt32ForKey:TGBridgeSendLocationMessageSubscriptionReplyToMidKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendLocationMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendForwardedMessageSubscriptionName = @"sendMessage.forward"; +NSString *const TGBridgeSendForwardedMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendForwardedMessageSubscriptionMidKey = @"mid"; +NSString *const TGBridgeSendForwardedMessageSubscriptionTargetPeerIdKey = @"targetPeerId"; + +@implementation TGBridgeSendForwardedMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId targetPeerId:(int64_t)targetPeerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + _targetPeerId = targetPeerId; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendForwardedMessageSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeSendForwardedMessageSubscriptionMidKey]; + [aCoder encodeInt64:self.targetPeerId forKey:TGBridgeSendForwardedMessageSubscriptionTargetPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendForwardedMessageSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeSendForwardedMessageSubscriptionMidKey]; + _targetPeerId = [aDecoder decodeInt64ForKey:TGBridgeSendForwardedMessageSubscriptionTargetPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendForwardedMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeStateSubscriptionName = @"state.syncState"; + +@implementation TGBridgeStateSubscription + +- (bool)dropPreviouslyQueued +{ + return true; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeStateSubscriptionName; +} + +@end + + +NSString *const TGBridgeStickerPacksSubscriptionName = @"stickers.packs"; + +@implementation TGBridgeStickerPacksSubscription + ++ (NSString *)subscriptionName +{ + return TGBridgeStickerPacksSubscriptionName; +} + +@end + + +NSString *const TGBridgeRecentStickersSubscriptionName = @"stickers.recent"; +NSString *const TGBridgeRecentStickersSubscriptionLimitKey = @"limit"; + +@implementation TGBridgeRecentStickersSubscription + +- (instancetype)initWithLimit:(int32_t)limit +{ + self = [super init]; + if (self != nil) + { + _limit = limit; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.limit forKey:TGBridgeRecentStickersSubscriptionLimitKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _limit = [aDecoder decodeInt32ForKey:TGBridgeRecentStickersSubscriptionLimitKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeRecentStickersSubscriptionName; +} + +@end + + +NSString *const TGBridgeUserInfoSubscriptionName = @"user.userInfo"; +NSString *const TGBridgeUserInfoSubscriptionUserIdsKey = @"uids"; + +@implementation TGBridgeUserInfoSubscription + +- (instancetype)initWithUserIds:(NSArray *)userIds +{ + self = [super init]; + if (self != nil) + { + _userIds = userIds; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.userIds forKey:TGBridgeUserInfoSubscriptionUserIdsKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _userIds = [aDecoder decodeObjectForKey:TGBridgeUserInfoSubscriptionUserIdsKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeUserInfoSubscriptionName; +} + +@end + + +NSString *const TGBridgeUserBotInfoSubscriptionName = @"user.botInfo"; +NSString *const TGBridgeUserBotInfoSubscriptionUserIdsKey = @"uids"; + +@implementation TGBridgeUserBotInfoSubscription + +- (instancetype)initWithUserIds:(NSArray *)userIds +{ + self = [super init]; + if (self != nil) + { + _userIds = userIds; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.userIds forKey:TGBridgeUserBotInfoSubscriptionUserIdsKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _userIds = [aDecoder decodeObjectForKey:TGBridgeUserBotInfoSubscriptionUserIdsKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeUserBotInfoSubscriptionName; +} + +@end + + +NSString *const TGBridgeBotReplyMarkupSubscriptionName = @"user.botReplyMarkup"; +NSString *const TGBridgeBotReplyMarkupPeerIdKey = @"peerId"; + +@implementation TGBridgeBotReplyMarkupSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeBotReplyMarkupPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeBotReplyMarkupPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeBotReplyMarkupSubscriptionName; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeUnsupportedMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeUnsupportedMediaAttachment.m new file mode 100644 index 0000000000..b51e422fd1 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeUnsupportedMediaAttachment.m @@ -0,0 +1,35 @@ +#import "TGBridgeUnsupportedMediaAttachment.h" + +const NSInteger TGBridgeUnsupportedMediaAttachmentType = 0x3837BEF7; + +NSString *const TGBridgeUnsupportedMediaCompactTitleKey = @"compactTitle"; +NSString *const TGBridgeUnsupportedMediaTitleKey = @"title"; +NSString *const TGBridgeUnsupportedMediaSubtitleKey = @"subtitle"; + +@implementation TGBridgeUnsupportedMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _compactTitle = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaCompactTitleKey]; + _title = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaTitleKey]; + _subtitle = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaSubtitleKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.compactTitle forKey:TGBridgeUnsupportedMediaCompactTitleKey]; + [aCoder encodeObject:self.title forKey:TGBridgeUnsupportedMediaTitleKey]; + [aCoder encodeObject:self.subtitle forKey:TGBridgeUnsupportedMediaSubtitleKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeUnsupportedMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeUser.m b/submodules/WatchCommon/Host/Sources/TGBridgeUser.m new file mode 100644 index 0000000000..4c0fed8d97 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeUser.m @@ -0,0 +1,286 @@ +#import "TGBridgeUser.h" +//#import "TGWatchCommon.h" +#import "TGBridgeBotInfo.h" + +//#import "../Extension/TGStringUtils.h" + +NSString *const TGBridgeUserIdentifierKey = @"identifier"; +NSString *const TGBridgeUserFirstNameKey = @"firstName"; +NSString *const TGBridgeUserLastNameKey = @"lastName"; +NSString *const TGBridgeUserUserNameKey = @"userName"; +NSString *const TGBridgeUserPhoneNumberKey = @"phoneNumber"; +NSString *const TGBridgeUserPrettyPhoneNumberKey = @"prettyPhoneNumber"; +NSString *const TGBridgeUserOnlineKey = @"online"; +NSString *const TGBridgeUserLastSeenKey = @"lastSeen"; +NSString *const TGBridgeUserPhotoSmallKey = @"photoSmall"; +NSString *const TGBridgeUserPhotoBigKey = @"photoBig"; +NSString *const TGBridgeUserKindKey = @"kind"; +NSString *const TGBridgeUserBotKindKey = @"botKind"; +NSString *const TGBridgeUserBotVersionKey = @"botVersion"; +NSString *const TGBridgeUserVerifiedKey = @"verified"; +NSString *const TGBridgeUserAboutKey = @"about"; +NSString *const TGBridgeUserVersionKey = @"version"; + +NSString *const TGBridgeUsersDictionaryKey = @"users"; + +@implementation TGBridgeUser + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeUserIdentifierKey]; + _firstName = [aDecoder decodeObjectForKey:TGBridgeUserFirstNameKey]; + _lastName = [aDecoder decodeObjectForKey:TGBridgeUserLastNameKey]; + _userName = [aDecoder decodeObjectForKey:TGBridgeUserUserNameKey]; + _phoneNumber = [aDecoder decodeObjectForKey:TGBridgeUserPhoneNumberKey]; + _prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeUserPrettyPhoneNumberKey]; + _online = [aDecoder decodeBoolForKey:TGBridgeUserOnlineKey]; + _lastSeen = [aDecoder decodeDoubleForKey:TGBridgeUserLastSeenKey]; + _photoSmall = [aDecoder decodeObjectForKey:TGBridgeUserPhotoSmallKey]; + _photoBig = [aDecoder decodeObjectForKey:TGBridgeUserPhotoBigKey]; + _kind = [aDecoder decodeInt32ForKey:TGBridgeUserKindKey]; + _botKind = [aDecoder decodeInt32ForKey:TGBridgeUserBotKindKey]; + _botVersion = [aDecoder decodeInt32ForKey:TGBridgeUserBotVersionKey]; + _verified = [aDecoder decodeBoolForKey:TGBridgeUserVerifiedKey]; + _about = [aDecoder decodeObjectForKey:TGBridgeUserAboutKey]; + _userVersion = [aDecoder decodeInt32ForKey:TGBridgeUserVersionKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeUserIdentifierKey]; + [aCoder encodeObject:self.firstName forKey:TGBridgeUserFirstNameKey]; + [aCoder encodeObject:self.lastName forKey:TGBridgeUserLastNameKey]; + [aCoder encodeObject:self.userName forKey:TGBridgeUserUserNameKey]; + [aCoder encodeObject:self.phoneNumber forKey:TGBridgeUserPhoneNumberKey]; + [aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeUserPrettyPhoneNumberKey]; + [aCoder encodeBool:self.online forKey:TGBridgeUserOnlineKey]; + [aCoder encodeDouble:self.lastSeen forKey:TGBridgeUserLastSeenKey]; + [aCoder encodeObject:self.photoSmall forKey:TGBridgeUserPhotoSmallKey]; + [aCoder encodeObject:self.photoBig forKey:TGBridgeUserPhotoBigKey]; + [aCoder encodeInt32:self.kind forKey:TGBridgeUserKindKey]; + [aCoder encodeInt32:self.botKind forKey:TGBridgeUserBotKindKey]; + [aCoder encodeInt32:self.botVersion forKey:TGBridgeUserBotVersionKey]; + [aCoder encodeBool:self.verified forKey:TGBridgeUserVerifiedKey]; + [aCoder encodeObject:self.about forKey:TGBridgeUserAboutKey]; + [aCoder encodeInt32:self.userVersion forKey:TGBridgeUserVersionKey]; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone +{ + TGBridgeUser *user = [[TGBridgeUser alloc] init]; + user->_identifier = self.identifier; + user->_firstName = self.firstName; + user->_lastName = self.lastName; + user->_userName = self.userName; + user->_phoneNumber = self.phoneNumber; + user->_prettyPhoneNumber = self.prettyPhoneNumber; + user->_online = self.online; + user->_lastSeen = self.lastSeen; + user->_photoSmall = self.photoSmall; + user->_photoBig = self.photoBig; + user->_kind = self.kind; + user->_botKind = self.botKind; + user->_botVersion = self.botVersion; + user->_verified = self.verified; + user->_about = self.about; + user->_userVersion = self.userVersion; + + return user; +} + +- (NSString *)displayName +{ + NSString *firstName = self.firstName; + NSString *lastName = self.lastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + { + return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; + } + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + +- (bool)isBot +{ + return (self.kind == TGBridgeUserKindBot || self.kind ==TGBridgeUserKindSmartBot); +} + +- (TGBridgeUserChange *)changeFromUser:(TGBridgeUser *)user +{ + NSMutableDictionary *fields = [[NSMutableDictionary alloc] init]; + + [self _compareString:self.firstName oldString:user.firstName dict:fields key:TGBridgeUserFirstNameKey]; + [self _compareString:self.lastName oldString:user.lastName dict:fields key:TGBridgeUserLastNameKey]; + [self _compareString:self.userName oldString:user.userName dict:fields key:TGBridgeUserUserNameKey]; + [self _compareString:self.phoneNumber oldString:user.phoneNumber dict:fields key:TGBridgeUserPhoneNumberKey]; + [self _compareString:self.prettyPhoneNumber oldString:user.prettyPhoneNumber dict:fields key:TGBridgeUserPrettyPhoneNumberKey]; + + if (self.online != user.online) + fields[TGBridgeUserOnlineKey] = @(self.online); + + if (fabs(self.lastSeen - user.lastSeen) > DBL_EPSILON) + fields[TGBridgeUserLastSeenKey] = @(self.lastSeen); + + [self _compareString:self.photoSmall oldString:user.photoSmall dict:fields key:TGBridgeUserPhotoSmallKey]; + [self _compareString:self.photoBig oldString:user.photoBig dict:fields key:TGBridgeUserPhotoBigKey]; + + if (self.kind != user.kind) + fields[TGBridgeUserKindKey] = @(self.kind); + + if (self.botKind != user.botKind) + fields[TGBridgeUserBotKindKey] = @(self.botKind); + + if (self.botVersion != user.botVersion) + fields[TGBridgeUserBotVersionKey] = @(self.botVersion); + + if (self.verified != user.verified) + fields[TGBridgeUserVerifiedKey] = @(self.verified); + + if (fields.count == 0) + return nil; + + return [[TGBridgeUserChange alloc] initWithUserIdentifier:user.identifier fields:fields]; +} + +- (void)_compareString:(NSString *)newString oldString:(NSString *)oldString dict:(NSMutableDictionary *)dict key:(NSString *)key +{ + if (newString == nil && oldString == nil) + return; + + if (![newString isEqualToString:oldString]) + { + if (newString == nil) + dict[key] = [NSNull null]; + else + dict[key] = newString; + } +} + +- (TGBridgeUser *)userByApplyingChange:(TGBridgeUserChange *)change +{ + if (change.userIdentifier != self.identifier) + return nil; + + TGBridgeUser *user = [self copy]; + + NSString *firstNameChange = change.fields[TGBridgeUserFirstNameKey]; + if (firstNameChange != nil) + user->_firstName = [self _stringForFieldChange:firstNameChange]; + + NSString *lastNameChange = change.fields[TGBridgeUserLastNameKey]; + if (lastNameChange != nil) + user->_lastName = [self _stringForFieldChange:lastNameChange]; + + NSString *userNameChange = change.fields[TGBridgeUserUserNameKey]; + if (userNameChange != nil) + user->_userName = [self _stringForFieldChange:userNameChange]; + + NSString *phoneNumberChange = change.fields[TGBridgeUserPhoneNumberKey]; + if (phoneNumberChange != nil) + user->_phoneNumber = [self _stringForFieldChange:phoneNumberChange]; + + NSString *prettyPhoneNumberChange = change.fields[TGBridgeUserPrettyPhoneNumberKey]; + if (prettyPhoneNumberChange != nil) + user->_prettyPhoneNumber = [self _stringForFieldChange:prettyPhoneNumberChange]; + + NSNumber *onlineChange = change.fields[TGBridgeUserOnlineKey]; + if (onlineChange != nil) + user->_online = [onlineChange boolValue]; + + NSNumber *lastSeenChange = change.fields[TGBridgeUserLastSeenKey]; + if (lastSeenChange != nil) + user->_lastSeen = [lastSeenChange doubleValue]; + + NSString *photoSmallChange = change.fields[TGBridgeUserPhotoSmallKey]; + if (photoSmallChange != nil) + user->_photoSmall = [self _stringForFieldChange:photoSmallChange]; + + NSString *photoBigChange = change.fields[TGBridgeUserPhotoBigKey]; + if (photoBigChange != nil) + user->_photoBig = [self _stringForFieldChange:photoBigChange]; + + NSNumber *kindChange = change.fields[TGBridgeUserKindKey]; + if (kindChange != nil) + user->_kind = (int32_t)[kindChange intValue]; + + NSNumber *botKindChange = change.fields[TGBridgeUserBotKindKey]; + if (botKindChange != nil) + user->_botKind = (int32_t)[botKindChange intValue]; + + NSNumber *botVersionChange = change.fields[TGBridgeUserBotVersionKey]; + if (botVersionChange != nil) + user->_botVersion = (int32_t)[botVersionChange intValue]; + + NSNumber *verifiedChange = change.fields[TGBridgeUserVerifiedKey]; + if (verifiedChange != nil) + user->_verified = [verifiedChange boolValue]; + + return user; +} + +- (NSString *)_stringForFieldChange:(NSString *)fieldChange +{ + if ([fieldChange isKindOfClass:[NSNull class]]) + return nil; + + return fieldChange; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + return self.identifier == ((TGBridgeUser *)object).identifier; +} + +@end + + +NSString *const TGBridgeUserChangeIdentifierKey = @"userIdentifier"; +NSString *const TGBridgeUserChangeFieldsKey = @"fields"; + +@implementation TGBridgeUserChange + +- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields +{ + self = [super init]; + if (self != nil) + { + _userIdentifier = userIdentifier; + _fields = fields; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _userIdentifier = [aDecoder decodeInt32ForKey:TGBridgeUserChangeIdentifierKey]; + _fields = [aDecoder decodeObjectForKey:TGBridgeUserChangeFieldsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.userIdentifier forKey:TGBridgeUserChangeIdentifierKey]; + [aCoder encodeObject:self.fields forKey:TGBridgeUserChangeFieldsKey]; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeVideoMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeVideoMediaAttachment.m new file mode 100644 index 0000000000..66fe867996 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeVideoMediaAttachment.m @@ -0,0 +1,39 @@ +#import "TGBridgeVideoMediaAttachment.h" +#import + +const NSInteger TGBridgeVideoMediaAttachmentType = 0x338EAA20; + +NSString *const TGBridgeVideoMediaVideoIdKey = @"videoId"; +NSString *const TGBridgeVideoMediaDimensionsKey = @"dimensions"; +NSString *const TGBridgeVideoMediaDurationKey = @"duration"; +NSString *const TGBridgeVideoMediaRoundKey = @"round"; + +@implementation TGBridgeVideoMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _videoId = [aDecoder decodeInt64ForKey:TGBridgeVideoMediaVideoIdKey]; + _dimensions = [aDecoder decodeCGSizeForKey:TGBridgeVideoMediaDimensionsKey]; + _duration = [aDecoder decodeInt32ForKey:TGBridgeVideoMediaDurationKey]; + _round = [aDecoder decodeBoolForKey:TGBridgeVideoMediaRoundKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.videoId forKey:TGBridgeVideoMediaVideoIdKey]; + [aCoder encodeCGSize:self.dimensions forKey:TGBridgeVideoMediaDimensionsKey]; + [aCoder encodeInt32:self.duration forKey:TGBridgeVideoMediaDurationKey]; + [aCoder encodeBool:self.round forKey:TGBridgeVideoMediaRoundKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeVideoMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Host/Sources/TGBridgeWebPageMediaAttachment.m b/submodules/WatchCommon/Host/Sources/TGBridgeWebPageMediaAttachment.m new file mode 100644 index 0000000000..3983075d69 --- /dev/null +++ b/submodules/WatchCommon/Host/Sources/TGBridgeWebPageMediaAttachment.m @@ -0,0 +1,65 @@ +#import "TGBridgeWebPageMediaAttachment.h" +#import "TGBridgeImageMediaAttachment.h" +#import + +const NSInteger TGBridgeWebPageMediaAttachmentType = 0x584197af; + +NSString *const TGBridgeWebPageMediaWebPageIdKey = @"webPageId"; +NSString *const TGBridgeWebPageMediaUrlKey = @"url"; +NSString *const TGBridgeWebPageMediaDisplayUrlKey = @"displayUrl"; +NSString *const TGBridgeWebPageMediaPageTypeKey = @"pageType"; +NSString *const TGBridgeWebPageMediaSiteNameKey = @"siteName"; +NSString *const TGBridgeWebPageMediaTitleKey = @"title"; +NSString *const TGBridgeWebPageMediaPageDescriptionKey = @"pageDescription"; +NSString *const TGBridgeWebPageMediaPhotoKey = @"photo"; +NSString *const TGBridgeWebPageMediaEmbedUrlKey = @"embedUrl"; +NSString *const TGBridgeWebPageMediaEmbedTypeKey = @"embedType"; +NSString *const TGBridgeWebPageMediaEmbedSizeKey = @"embedSize"; +NSString *const TGBridgeWebPageMediaDurationKey = @"duration"; +NSString *const TGBridgeWebPageMediaAuthorKey = @"author"; + +@implementation TGBridgeWebPageMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _webPageId = [aDecoder decodeInt64ForKey:TGBridgeWebPageMediaWebPageIdKey]; + _url = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaUrlKey]; + _displayUrl = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaDisplayUrlKey]; + _pageType = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPageTypeKey]; + _siteName = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaSiteNameKey]; + _title = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaTitleKey]; + _pageDescription = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPageDescriptionKey]; + _photo = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPhotoKey]; + _embedUrl = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaEmbedUrlKey]; + _embedSize = [aDecoder decodeCGSizeForKey:TGBridgeWebPageMediaEmbedSizeKey]; + _duration = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaDurationKey]; + _author = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaAuthorKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.webPageId forKey:TGBridgeWebPageMediaWebPageIdKey]; + [aCoder encodeObject:self.url forKey:TGBridgeWebPageMediaUrlKey]; + [aCoder encodeObject:self.displayUrl forKey:TGBridgeWebPageMediaDisplayUrlKey]; + [aCoder encodeObject:self.pageType forKey:TGBridgeWebPageMediaPageTypeKey]; + [aCoder encodeObject:self.siteName forKey:TGBridgeWebPageMediaSiteNameKey]; + [aCoder encodeObject:self.title forKey:TGBridgeWebPageMediaTitleKey]; + [aCoder encodeObject:self.pageDescription forKey:TGBridgeWebPageMediaPageDescriptionKey]; + [aCoder encodeObject:self.photo forKey:TGBridgeWebPageMediaPhotoKey]; + [aCoder encodeObject:self.embedUrl forKey:TGBridgeWebPageMediaEmbedUrlKey]; + [aCoder encodeCGSize:self.embedSize forKey:TGBridgeWebPageMediaEmbedSizeKey]; + [aCoder encodeObject:self.duration forKey:TGBridgeWebPageMediaDurationKey]; + [aCoder encodeObject:self.author forKey:TGBridgeWebPageMediaAuthorKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeWebPageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeActionMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeActionMediaAttachment.h new file mode 100644 index 0000000000..5b2f13e4f4 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeActionMediaAttachment.h @@ -0,0 +1,36 @@ +#import + +typedef NS_ENUM(NSUInteger, TGBridgeMessageAction) { + TGBridgeMessageActionNone = 0, + TGBridgeMessageActionChatEditTitle = 1, + TGBridgeMessageActionChatAddMember = 2, + TGBridgeMessageActionChatDeleteMember = 3, + TGBridgeMessageActionCreateChat = 4, + TGBridgeMessageActionChatEditPhoto = 5, + TGBridgeMessageActionContactRequest = 6, + TGBridgeMessageActionAcceptContactRequest = 7, + TGBridgeMessageActionContactRegistered = 8, + TGBridgeMessageActionUserChangedPhoto = 9, + TGBridgeMessageActionEncryptedChatRequest = 10, + TGBridgeMessageActionEncryptedChatAccept = 11, + TGBridgeMessageActionEncryptedChatDecline = 12, + TGBridgeMessageActionEncryptedChatMessageLifetime = 13, + TGBridgeMessageActionEncryptedChatScreenshot = 14, + TGBridgeMessageActionEncryptedChatMessageScreenshot = 15, + TGBridgeMessageActionCreateBroadcastList = 16, + TGBridgeMessageActionJoinedByLink = 17, + TGBridgeMessageActionChannelCreated = 18, + TGBridgeMessageActionChannelCommentsStatusChanged = 19, + TGBridgeMessageActionChannelInviter = 20, + TGBridgeMessageActionGroupMigratedTo = 21, + TGBridgeMessageActionGroupDeactivated = 22, + TGBridgeMessageActionGroupActivated = 23, + TGBridgeMessageActionChannelMigratedFrom = 24 +}; + +@interface TGBridgeActionMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) TGBridgeMessageAction actionType; +@property (nonatomic, strong) NSDictionary *actionData; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeActionMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeActionMediaAttachment.m new file mode 100644 index 0000000000..763cf8a1eb --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeActionMediaAttachment.m @@ -0,0 +1,33 @@ +#import "TGBridgeActionMediaAttachment.h" +#import "TGBridgeImageMediaAttachment.h" + +const NSInteger TGBridgeActionMediaAttachmentType = 0x1167E28B; + +NSString *const TGBridgeActionMediaTypeKey = @"actionType"; +NSString *const TGBridgeActionMediaDataKey = @"actionData"; + +@implementation TGBridgeActionMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _actionType = (TGBridgeMessageAction)[aDecoder decodeInt32ForKey:TGBridgeActionMediaTypeKey]; + _actionData = [aDecoder decodeObjectForKey:TGBridgeActionMediaDataKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.actionType forKey:TGBridgeActionMediaTypeKey]; + [aCoder encodeObject:self.actionData forKey:TGBridgeActionMediaDataKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeActionMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeAudioMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeAudioMediaAttachment.h new file mode 100644 index 0000000000..d6bdcd9716 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeAudioMediaAttachment.h @@ -0,0 +1,16 @@ +#import + +@interface TGBridgeAudioMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t audioId; +@property (nonatomic, assign) int64_t accessHash; +@property (nonatomic, assign) int32_t datacenterId; + +@property (nonatomic, assign) int64_t localAudioId; + +@property (nonatomic, assign) int32_t duration; +@property (nonatomic, assign) int32_t fileSize; + +- (int64_t)identifier; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeAudioMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeAudioMediaAttachment.m new file mode 100644 index 0000000000..3f4096a897 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeAudioMediaAttachment.m @@ -0,0 +1,65 @@ +#import "TGBridgeAudioMediaAttachment.h" + +const NSInteger TGBridgeAudioMediaAttachmentType = 0x3A0E7A32; + +NSString *const TGBridgeAudioMediaAudioIdKey = @"audioId"; +NSString *const TGBridgeAudioMediaAccessHashKey = @"accessHash"; +NSString *const TGBridgeAudioMediaLocalIdKey = @"localId"; +NSString *const TGBridgeAudioMediaDatacenterIdKey = @"datacenterId"; +NSString *const TGBridgeAudioMediaDurationKey = @"duration"; +NSString *const TGBridgeAudioMediaFileSizeKey = @"fileSize"; + +@implementation TGBridgeAudioMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _audioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAudioIdKey]; + _accessHash = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAccessHashKey]; + _localAudioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaLocalIdKey]; + _datacenterId = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDatacenterIdKey]; + _duration = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDurationKey]; + _fileSize = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaFileSizeKey]; + } + return self; +} + +- (void)encodeWithCoder:(nonnull NSCoder *)aCoder +{ + [aCoder encodeInt64:self.audioId forKey:TGBridgeAudioMediaAudioIdKey]; + [aCoder encodeInt64:self.accessHash forKey:TGBridgeAudioMediaAccessHashKey]; + [aCoder encodeInt64:self.localAudioId forKey:TGBridgeAudioMediaLocalIdKey]; + [aCoder encodeInt32:self.datacenterId forKey:TGBridgeAudioMediaDatacenterIdKey]; + [aCoder encodeInt32:self.duration forKey:TGBridgeAudioMediaDurationKey]; + [aCoder encodeInt32:self.fileSize forKey:TGBridgeAudioMediaFileSizeKey]; +} + +- (int64_t)identifier +{ + if (self.localAudioId != 0) + return self.localAudioId; + + return self.audioId; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeAudioMediaAttachment *audio = (TGBridgeAudioMediaAttachment *)object; + + return (self.audioId == audio.audioId || self.localAudioId == audio.localAudioId); +} + ++ (NSInteger)mediaType +{ + return TGBridgeAudioMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeBotCommandInfo.h b/submodules/WatchCommon/Watch/Sources/TGBridgeBotCommandInfo.h new file mode 100644 index 0000000000..fe6f72e1a0 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeBotCommandInfo.h @@ -0,0 +1,12 @@ +#import + +@interface TGBridgeBotCommandInfo : NSObject +{ + NSString *_command; + NSString *_commandDescription; +} + +@property (nonatomic, readonly) NSString *command; +@property (nonatomic, readonly) NSString *commandDescription; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeBotCommandInfo.m b/submodules/WatchCommon/Watch/Sources/TGBridgeBotCommandInfo.m new file mode 100644 index 0000000000..0f1e005861 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeBotCommandInfo.m @@ -0,0 +1,25 @@ +#import "TGBridgeBotCommandInfo.h" + +NSString *const TGBridgeBotCommandInfoCommandKey = @"command"; +NSString *const TGBridgeBotCommandDescriptionKey = @"commandDescription"; + +@implementation TGBridgeBotCommandInfo + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _command = [aDecoder decodeObjectForKey:TGBridgeBotCommandInfoCommandKey]; + _commandDescription = [aDecoder decodeObjectForKey:TGBridgeBotCommandDescriptionKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.command forKey:TGBridgeBotCommandInfoCommandKey]; + [aCoder encodeObject:self.commandDescription forKey:TGBridgeBotCommandDescriptionKey]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.h b/submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.h new file mode 100644 index 0000000000..0dafae5cef --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.h @@ -0,0 +1,12 @@ +#import + +@interface TGBridgeBotInfo : NSObject +{ + NSString *_shortDescription; + NSArray *_commandList; +} + +@property (nonatomic, readonly) NSString *shortDescription; +@property (nonatomic, readonly) NSArray *commandList; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.m b/submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.m new file mode 100644 index 0000000000..996abb3a95 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.m @@ -0,0 +1,25 @@ +#import "TGBridgeBotInfo.h" + +NSString *const TGBridgeBotInfoShortDescriptionKey = @"shortDescription"; +NSString *const TGBridgeBotInfoCommandListKey = @"commandList"; + +@implementation TGBridgeBotInfo + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _shortDescription = [aDecoder decodeObjectForKey:TGBridgeBotInfoShortDescriptionKey]; + _commandList = [aDecoder decodeObjectForKey:TGBridgeBotInfoCommandListKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.shortDescription forKey:TGBridgeBotInfoShortDescriptionKey]; + [aCoder encodeObject:self.commandList forKey:TGBridgeBotInfoCommandListKey]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeChat.h b/submodules/WatchCommon/Watch/Sources/TGBridgeChat.h new file mode 100644 index 0000000000..d326e18fcf --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeChat.h @@ -0,0 +1,46 @@ +#import +#import + +@interface TGBridgeChat : NSObject + +@property (nonatomic) int64_t identifier; +@property (nonatomic) NSTimeInterval date; +@property (nonatomic) int32_t fromUid; +@property (nonatomic, strong) NSString *text; + +@property (nonatomic, strong) NSArray *media; + +@property (nonatomic) bool outgoing; +@property (nonatomic) bool unread; +@property (nonatomic) bool deliveryError; +@property (nonatomic) TGBridgeMessageDeliveryState deliveryState; + +@property (nonatomic) int32_t unreadCount; + +@property (nonatomic) bool isBroadcast; + +@property (nonatomic, strong) NSString *groupTitle; +@property (nonatomic, strong) NSString *groupPhotoSmall; +@property (nonatomic, strong) NSString *groupPhotoBig; + +@property (nonatomic) bool isGroup; +@property (nonatomic) bool hasLeftGroup; +@property (nonatomic) bool isKickedFromGroup; + +@property (nonatomic) bool isChannel; +@property (nonatomic) bool isChannelGroup; + +@property (nonatomic, strong) NSString *userName; +@property (nonatomic, strong) NSString *about; +@property (nonatomic) bool verified; + +@property (nonatomic) int32_t participantsCount; +@property (nonatomic, strong) NSArray *participants; + +- (NSIndexSet *)involvedUserIds; +- (NSIndexSet *)participantsUserIds; + +@end + +extern NSString *const TGBridgeChatKey; +extern NSString *const TGBridgeChatsArrayKey; diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeChat.m b/submodules/WatchCommon/Watch/Sources/TGBridgeChat.m new file mode 100644 index 0000000000..4ea9552f08 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeChat.m @@ -0,0 +1,139 @@ +#import "TGBridgeChat.h" +#import "TGBridgePeerIdAdapter.h" + +NSString *const TGBridgeChatIdentifierKey = @"identifier"; +NSString *const TGBridgeChatDateKey = @"date"; +NSString *const TGBridgeChatFromUidKey = @"fromUid"; +NSString *const TGBridgeChatTextKey = @"text"; +NSString *const TGBridgeChatOutgoingKey = @"outgoing"; +NSString *const TGBridgeChatUnreadKey = @"unread"; +NSString *const TGBridgeChatMediaKey = @"media"; +NSString *const TGBridgeChatUnreadCountKey = @"unreadCount"; +NSString *const TGBridgeChatGroupTitleKey = @"groupTitle"; +NSString *const TGBridgeChatGroupPhotoSmallKey = @"groupPhotoSmall"; +NSString *const TGBridgeChatGroupPhotoBigKey = @"groupPhotoBig"; +NSString *const TGBridgeChatIsGroupKey = @"isGroup"; +NSString *const TGBridgeChatHasLeftGroupKey = @"hasLeftGroup"; +NSString *const TGBridgeChatIsKickedFromGroupKey = @"isKickedFromGroup"; +NSString *const TGBridgeChatIsChannelKey = @"isChannel"; +NSString *const TGBridgeChatIsChannelGroupKey = @"isChannelGroup"; +NSString *const TGBridgeChatUserNameKey = @"userName"; +NSString *const TGBridgeChatAboutKey = @"about"; +NSString *const TGBridgeChatVerifiedKey = @"verified"; +NSString *const TGBridgeChatGroupParticipantsCountKey = @"participantsCount"; +NSString *const TGBridgeChatGroupParticipantsKey = @"participants"; +NSString *const TGBridgeChatDeliveryStateKey = @"deliveryState"; +NSString *const TGBridgeChatDeliveryErrorKey = @"deliveryError"; + +NSString *const TGBridgeChatKey = @"chat"; +NSString *const TGBridgeChatsArrayKey = @"chats"; + +@implementation TGBridgeChat + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeChatIdentifierKey]; + _date = [aDecoder decodeDoubleForKey:TGBridgeChatDateKey]; + _fromUid = [aDecoder decodeInt32ForKey:TGBridgeChatFromUidKey]; + _text = [aDecoder decodeObjectForKey:TGBridgeChatTextKey]; + _outgoing = [aDecoder decodeBoolForKey:TGBridgeChatOutgoingKey]; + _unread = [aDecoder decodeBoolForKey:TGBridgeChatUnreadKey]; + _unreadCount = [aDecoder decodeInt32ForKey:TGBridgeChatUnreadCountKey]; + _deliveryState = [aDecoder decodeInt32ForKey:TGBridgeChatDeliveryStateKey]; + _deliveryError = [aDecoder decodeBoolForKey:TGBridgeChatDeliveryErrorKey]; + _media = [aDecoder decodeObjectForKey:TGBridgeChatMediaKey]; + + _groupTitle = [aDecoder decodeObjectForKey:TGBridgeChatGroupTitleKey]; + _groupPhotoSmall = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoSmallKey]; + _groupPhotoBig = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoBigKey]; + _isGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsGroupKey]; + _hasLeftGroup = [aDecoder decodeBoolForKey:TGBridgeChatHasLeftGroupKey]; + _isKickedFromGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsKickedFromGroupKey]; + _isChannel = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelKey]; + _isChannelGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelGroupKey]; + _userName = [aDecoder decodeObjectForKey:TGBridgeChatUserNameKey]; + _about = [aDecoder decodeObjectForKey:TGBridgeChatAboutKey]; + _verified = [aDecoder decodeBoolForKey:TGBridgeChatVerifiedKey]; + _participantsCount = [aDecoder decodeInt32ForKey:TGBridgeChatGroupParticipantsCountKey]; + _participants = [aDecoder decodeObjectForKey:TGBridgeChatGroupParticipantsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeChatIdentifierKey]; + [aCoder encodeDouble:self.date forKey:TGBridgeChatDateKey]; + [aCoder encodeInt32:self.fromUid forKey:TGBridgeChatFromUidKey]; + [aCoder encodeObject:self.text forKey:TGBridgeChatTextKey]; + [aCoder encodeBool:self.outgoing forKey:TGBridgeChatOutgoingKey]; + [aCoder encodeBool:self.unread forKey:TGBridgeChatUnreadKey]; + [aCoder encodeInt32:self.unreadCount forKey:TGBridgeChatUnreadCountKey]; + [aCoder encodeInt32:self.deliveryState forKey:TGBridgeChatDeliveryStateKey]; + [aCoder encodeBool:self.deliveryError forKey:TGBridgeChatDeliveryErrorKey]; + [aCoder encodeObject:self.media forKey:TGBridgeChatMediaKey]; + + [aCoder encodeObject:self.groupTitle forKey:TGBridgeChatGroupTitleKey]; + [aCoder encodeObject:self.groupPhotoSmall forKey:TGBridgeChatGroupPhotoSmallKey]; + [aCoder encodeObject:self.groupPhotoBig forKey:TGBridgeChatGroupPhotoBigKey]; + + [aCoder encodeBool:self.isGroup forKey:TGBridgeChatIsGroupKey]; + [aCoder encodeBool:self.hasLeftGroup forKey:TGBridgeChatHasLeftGroupKey]; + [aCoder encodeBool:self.isKickedFromGroup forKey:TGBridgeChatIsKickedFromGroupKey]; + + [aCoder encodeBool:self.isChannel forKey:TGBridgeChatIsChannelKey]; + [aCoder encodeBool:self.isChannelGroup forKey:TGBridgeChatIsChannelGroupKey]; + [aCoder encodeObject:self.userName forKey:TGBridgeChatUserNameKey]; + [aCoder encodeObject:self.about forKey:TGBridgeChatAboutKey]; + [aCoder encodeBool:self.verified forKey:TGBridgeChatVerifiedKey]; + + [aCoder encodeInt32:self.participantsCount forKey:TGBridgeChatGroupParticipantsCountKey]; + [aCoder encodeObject:self.participants forKey:TGBridgeChatGroupParticipantsKey]; +} + +- (NSIndexSet *)involvedUserIds +{ + NSMutableIndexSet *userIds = [[NSMutableIndexSet alloc] init]; + if (!self.isGroup && !self.isChannel && self.identifier != 0) + [userIds addIndex:(int32_t)self.identifier]; + if ((!self.isChannel || self.isChannelGroup) && self.fromUid != self.identifier && self.fromUid != 0 && !TGPeerIdIsChannel(self.fromUid) && self.fromUid > 0) + [userIds addIndex:(int32_t)self.fromUid]; + + for (TGBridgeMediaAttachment *attachment in self.media) + { + if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]]) + { + TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment; + if (actionAttachment.actionData[@"uid"] != nil) + [userIds addIndex:[actionAttachment.actionData[@"uid"] integerValue]]; + } + } + + return userIds; +} + +- (NSIndexSet *)participantsUserIds +{ + NSMutableIndexSet *userIds = [[NSMutableIndexSet alloc] init]; + + for (NSNumber *uid in self.participants) + [userIds addIndex:uid.unsignedIntegerValue]; + + return userIds; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + return self.identifier == ((TGBridgeChat *)object).identifier; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.h b/submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.h new file mode 100644 index 0000000000..b185037e4d --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.h @@ -0,0 +1,14 @@ +#import + +@class SSignal; + +@interface TGBridgeChatMessages : NSObject +{ + NSArray *_messages; +} + +@property (nonatomic, readonly) NSArray *messages; + +@end + +extern NSString *const TGBridgeChatMessageListViewKey; diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.m b/submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.m new file mode 100644 index 0000000000..c7b64e2c13 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.m @@ -0,0 +1,27 @@ +#import "TGBridgeChatMessages.h" +#import "TGBridgeMessage.h" + +NSString *const TGBridgeChatMessageListViewMessagesKey = @"messages"; +NSString *const TGBridgeChatMessageListViewEarlierMessageIdKey = @"earlier"; +NSString *const TGBridgeChatMessageListViewLaterMessageIdKey = @"later"; + +NSString *const TGBridgeChatMessageListViewKey = @"messageListView"; + +@implementation TGBridgeChatMessages + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _messages = [aDecoder decodeObjectForKey:TGBridgeChatMessageListViewMessagesKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.messages forKey:TGBridgeChatMessageListViewMessagesKey]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeCommon.h b/submodules/WatchCommon/Watch/Sources/TGBridgeCommon.h new file mode 100644 index 0000000000..fe0510c041 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeCommon.h @@ -0,0 +1,95 @@ +#import + +extern NSString *const TGBridgeIncomingFileTypeKey; +extern NSString *const TGBridgeIncomingFileIdentifierKey; +extern NSString *const TGBridgeIncomingFileRandomIdKey; +extern NSString *const TGBridgeIncomingFilePeerIdKey; +extern NSString *const TGBridgeIncomingFileReplyToMidKey; + +extern NSString *const TGBridgeIncomingFileTypeAudio; +extern NSString *const TGBridgeIncomingFileTypeImage; + +@interface TGBridgeSubscription : NSObject + +@property (nonatomic, readonly) int64_t identifier; +@property (nonatomic, readonly, strong) NSString *name; + +@property (nonatomic, readonly) bool isOneTime; +@property (nonatomic, readonly) bool renewable; +@property (nonatomic, readonly) bool dropPreviouslyQueued; +@property (nonatomic, readonly) bool synchronous; + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder; +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder; + ++ (NSString *)subscriptionName; + +@end + + +@interface TGBridgeDisposal : NSObject + +@property (nonatomic, readonly) int64_t identifier; + +- (instancetype)initWithIdentifier:(int64_t)identifier; + +@end + + +@interface TGBridgeFile : NSObject + +@property (nonatomic, readonly, strong) NSData *data; +@property (nonatomic, readonly, strong) NSDictionary *metadata; + +- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata; + +@end + + +@interface TGBridgePing : NSObject + +@property (nonatomic, readonly) int32_t sessionId; + +- (instancetype)initWithSessionId:(int32_t)sessionId; + +@end + + +@interface TGBridgeSubscriptionListRequest : NSObject + +@property (nonatomic, readonly) int32_t sessionId; + +- (instancetype)initWithSessionId:(int32_t)sessionId; + +@end + + +@interface TGBridgeSubscriptionList : NSObject + +@property (nonatomic, readonly, strong) NSArray *subscriptions; + +- (instancetype)initWithArray:(NSArray *)array; + +@end + + +typedef NS_ENUM(int32_t, TGBridgeResponseType) { + TGBridgeResponseTypeUndefined, + TGBridgeResponseTypeNext, + TGBridgeResponseTypeFailed, + TGBridgeResponseTypeCompleted +}; + +@interface TGBridgeResponse : NSObject + +@property (nonatomic, readonly) int64_t subscriptionIdentifier; + +@property (nonatomic, readonly) TGBridgeResponseType type; +@property (nonatomic, readonly, strong) id next; +@property (nonatomic, readonly, strong) NSString *error; + ++ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription; ++ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription; ++ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeCommon.m b/submodules/WatchCommon/Watch/Sources/TGBridgeCommon.m new file mode 100644 index 0000000000..ae0cf5300b --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeCommon.m @@ -0,0 +1,295 @@ +#import "TGBridgeCommon.h" + +NSString *const TGBridgeIncomingFileTypeKey = @"type"; +NSString *const TGBridgeIncomingFileIdentifierKey = @"identifier"; +NSString *const TGBridgeIncomingFileRandomIdKey = @"randomId"; +NSString *const TGBridgeIncomingFilePeerIdKey = @"peerId"; +NSString *const TGBridgeIncomingFileReplyToMidKey = @"replyToMid"; +NSString *const TGBridgeIncomingFileTypeAudio = @"audio"; +NSString *const TGBridgeIncomingFileTypeImage = @"image"; + +NSString *const TGBridgeResponseSubscriptionIdentifier = @"identifier"; +NSString *const TGBridgeResponseTypeKey = @"type"; +NSString *const TGBridgeResponseNextKey = @"next"; +NSString *const TGBridgeResponseErrorKey = @"error"; + +@implementation TGBridgeResponse + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _subscriptionIdentifier = [aDecoder decodeInt64ForKey:TGBridgeResponseSubscriptionIdentifier]; + _type = [aDecoder decodeInt32ForKey:TGBridgeResponseTypeKey]; + _next = [aDecoder decodeObjectForKey:TGBridgeResponseNextKey]; + _error = [aDecoder decodeObjectForKey:TGBridgeResponseErrorKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.subscriptionIdentifier forKey:TGBridgeResponseSubscriptionIdentifier]; + [aCoder encodeInt32:self.type forKey:TGBridgeResponseTypeKey]; + [aCoder encodeObject:self.next forKey:TGBridgeResponseNextKey]; + [aCoder encodeObject:self.error forKey:TGBridgeResponseErrorKey]; +} + ++ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription +{ + TGBridgeResponse *response = [[TGBridgeResponse alloc] init]; + response->_subscriptionIdentifier = subscription.identifier; + response->_type = TGBridgeResponseTypeNext; + response->_next = next; + return response; +} + ++ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription +{ + TGBridgeResponse *response = [[TGBridgeResponse alloc] init]; + response->_subscriptionIdentifier = subscription.identifier; + response->_type = TGBridgeResponseTypeFailed; + response->_error = error; + return response; +} + ++ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription +{ + TGBridgeResponse *response = [[TGBridgeResponse alloc] init]; + response->_subscriptionIdentifier = subscription.identifier; + response->_type = TGBridgeResponseTypeCompleted; + return response; +} + +@end + + +NSString *const TGBridgeSubscriptionIdentifierKey = @"identifier"; +NSString *const TGBridgeSubscriptionNameKey = @"name"; +NSString *const TGBridgeSubscriptionParametersKey = @"parameters"; + +@implementation TGBridgeSubscription + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + int64_t randomId = 0; + arc4random_buf(&randomId, sizeof(int64_t)); + _identifier = randomId; + _name = [[self class] subscriptionName]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey]; + _name = [aDecoder decodeObjectForKey:TGBridgeSubscriptionNameKey]; + [self _unserializeParametersWithCoder:aDecoder]; + } + return self; +} + +- (bool)synchronous +{ + return false; +} + +- (bool)renewable +{ + return true; +} + +- (bool)dropPreviouslyQueued +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)__unused aCoder +{ + +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)__unused aDecoder +{ + +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey]; + [aCoder encodeObject:self.name forKey:TGBridgeSubscriptionNameKey]; + [self _serializeParametersWithCoder:aCoder]; +} + ++ (NSString *)subscriptionName +{ + return nil; +} + +@end + + +@implementation TGBridgeDisposal + +- (instancetype)initWithIdentifier:(int64_t)identifier +{ + self = [super init]; + if (self != nil) + { + _identifier = identifier; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey]; +} + +@end + +NSString *const TGBridgeFileDataKey = @"data"; +NSString *const TGBridgeFileMetadataKey = @"metadata"; + +@implementation TGBridgeFile + +- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata +{ + self = [super init]; + if (self != nil) + { + _data = data; + _metadata = metadata; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _data = [aDecoder decodeObjectForKey:TGBridgeFileDataKey]; + _metadata = [aDecoder decodeObjectForKey:TGBridgeFileMetadataKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.data forKey:TGBridgeFileDataKey]; + [aCoder encodeObject:self.metadata forKey:TGBridgeFileMetadataKey]; +} + +@end + + +NSString *const TGBridgeSessionIdKey = @"sessionId"; + +@implementation TGBridgePing + +- (instancetype)initWithSessionId:(int32_t)sessionId +{ + self = [super init]; + if (self != nil) + { + _sessionId = sessionId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey]; +} + +@end + + +@implementation TGBridgeSubscriptionListRequest + +- (instancetype)initWithSessionId:(int32_t)sessionId +{ + self = [super init]; + if (self != nil) + { + _sessionId = sessionId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey]; +} + +@end + + +NSString *const TGBridgeSubscriptionListSubscriptionsKey = @"subscriptions"; + +@implementation TGBridgeSubscriptionList + +- (instancetype)initWithArray:(NSArray *)array +{ + self = [super init]; + if (self != nil) + { + _subscriptions = array; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _subscriptions = [aDecoder decodeObjectForKey:TGBridgeSubscriptionListSubscriptionsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.subscriptions forKey:TGBridgeSubscriptionListSubscriptionsKey]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeContactMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeContactMediaAttachment.h new file mode 100644 index 0000000000..a8531272d8 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeContactMediaAttachment.h @@ -0,0 +1,13 @@ +#import + +@interface TGBridgeContactMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int32_t uid; +@property (nonatomic, strong) NSString *firstName; +@property (nonatomic, strong) NSString *lastName; +@property (nonatomic, strong) NSString *phoneNumber; +@property (nonatomic, strong) NSString *prettyPhoneNumber; + +- (NSString *)displayName; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeContactMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeContactMediaAttachment.m new file mode 100644 index 0000000000..4b2f482eaa --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeContactMediaAttachment.m @@ -0,0 +1,60 @@ +#import "TGBridgeContactMediaAttachment.h" + +//#import "../Extension/TGStringUtils.h" + +const NSInteger TGBridgeContactMediaAttachmentType = 0xB90A5663; + +NSString *const TGBridgeContactMediaUidKey = @"uid"; +NSString *const TGBridgeContactMediaFirstNameKey = @"firstName"; +NSString *const TGBridgeContactMediaLastNameKey = @"lastName"; +NSString *const TGBridgeContactMediaPhoneNumberKey = @"phoneNumber"; +NSString *const TGBridgeContactMediaPrettyPhoneNumberKey = @"prettyPhoneNumber"; + +@implementation TGBridgeContactMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _uid = [aDecoder decodeInt32ForKey:TGBridgeContactMediaUidKey]; + _firstName = [aDecoder decodeObjectForKey:TGBridgeContactMediaFirstNameKey]; + _lastName = [aDecoder decodeObjectForKey:TGBridgeContactMediaLastNameKey]; + _phoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPhoneNumberKey]; + _prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPrettyPhoneNumberKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.uid forKey:TGBridgeContactMediaUidKey]; + [aCoder encodeObject:self.firstName forKey:TGBridgeContactMediaFirstNameKey]; + [aCoder encodeObject:self.lastName forKey:TGBridgeContactMediaLastNameKey]; + [aCoder encodeObject:self.phoneNumber forKey:TGBridgeContactMediaPhoneNumberKey]; + [aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeContactMediaPrettyPhoneNumberKey]; +} + +- (NSString *)displayName +{ + NSString *firstName = self.firstName; + NSString *lastName = self.lastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + { + return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; + } + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + ++ (NSInteger)mediaType +{ + return TGBridgeContactMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeContext.h b/submodules/WatchCommon/Watch/Sources/TGBridgeContext.h new file mode 100644 index 0000000000..45225a04f6 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeContext.h @@ -0,0 +1,18 @@ +#import + +@interface TGBridgeContext : NSObject + +@property (nonatomic, readonly) bool authorized; +@property (nonatomic, readonly) int32_t userId; +@property (nonatomic, readonly) bool micAccessAllowed; +@property (nonatomic, readonly) NSDictionary *preheatData; +@property (nonatomic, readonly) NSInteger preheatVersion; + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; +- (NSDictionary *)dictionary; + +- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId; +- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data; +- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeContext.m b/submodules/WatchCommon/Watch/Sources/TGBridgeContext.m new file mode 100644 index 0000000000..4b0600e2fc --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeContext.m @@ -0,0 +1,101 @@ +#import "TGBridgeContext.h" +#import "TGBridgeCommon.h" +//#import "TGWatchCommon.h" + +NSString *const TGBridgeContextAuthorized = @"authorized"; +NSString *const TGBridgeContextUserId = @"userId"; +NSString *const TGBridgeContextMicAccessAllowed = @"micAccessAllowed"; +NSString *const TGBridgeContextStartupData = @"startupData"; +NSString *const TGBridgeContextStartupDataVersion = @"version"; + +@implementation TGBridgeContext + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + self = [super init]; + if (self != nil) + { + _authorized = [dictionary[TGBridgeContextAuthorized] boolValue]; + _userId = (int32_t)[dictionary[TGBridgeContextUserId] intValue]; + _micAccessAllowed = [dictionary[TGBridgeContextMicAccessAllowed] boolValue]; + + if (dictionary[TGBridgeContextStartupData] != nil) { + _preheatData = [NSKeyedUnarchiver unarchiveObjectWithData:dictionary[TGBridgeContextStartupData]]; + _preheatVersion = [dictionary[TGBridgeContextStartupDataVersion] integerValue]; + } + } + return self; +} + +- (NSDictionary *)dictionary +{ + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; + dictionary[TGBridgeContextAuthorized] = @(self.authorized); + dictionary[TGBridgeContextUserId] = @(self.userId); + dictionary[TGBridgeContextMicAccessAllowed] = @(self.micAccessAllowed); + if (self.preheatData != nil) { + dictionary[TGBridgeContextStartupData] = [NSKeyedArchiver archivedDataWithRootObject:self.preheatData]; + dictionary[TGBridgeContextStartupDataVersion] = @(self.preheatVersion); + } + return dictionary; +} + +- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId +{ + TGBridgeContext *context = [[TGBridgeContext alloc] init]; + context->_authorized = authorized; + context->_userId = peerId; + context->_micAccessAllowed = self.micAccessAllowed; + if (authorized) { + context->_preheatData = self.preheatData; + context->_preheatVersion = self.preheatVersion; + } + return context; +} + +- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data +{ + TGBridgeContext *context = [[TGBridgeContext alloc] init]; + context->_authorized = self.authorized; + context->_userId = self.userId; + context->_micAccessAllowed = self.micAccessAllowed; + if (data != nil) { + context->_preheatData = data; + context->_preheatVersion = (int32_t)[NSDate date].timeIntervalSinceReferenceDate; + } + return context; +} + +- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed +{ + TGBridgeContext *context = [[TGBridgeContext alloc] init]; + context->_authorized = self.authorized; + context->_userId = self.userId; + context->_micAccessAllowed = allowed; + context->_preheatData = self.preheatData; + context->_preheatVersion = self.preheatVersion; + return context; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return true; + + if (!object || ![object isKindOfClass:[self class]]) + return false; + + TGBridgeContext *context = (TGBridgeContext *)object; + if (context.authorized != self.authorized) + return false; + if (context.userId != self.userId) + return false; + if (context.micAccessAllowed != self.micAccessAllowed) + return false; + if (context.preheatVersion != self.preheatVersion) + return false; + + return true; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeDocumentMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeDocumentMediaAttachment.h new file mode 100644 index 0000000000..da96a2ba22 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeDocumentMediaAttachment.h @@ -0,0 +1,23 @@ +#import + +@interface TGBridgeDocumentMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t documentId; +@property (nonatomic, assign) int64_t localDocumentId; +@property (nonatomic, assign) int32_t fileSize; + +@property (nonatomic, strong) NSString *fileName; +@property (nonatomic, strong) NSValue *imageSize; +@property (nonatomic, assign) bool isAnimated; +@property (nonatomic, assign) bool isSticker; +@property (nonatomic, strong) NSString *stickerAlt; +@property (nonatomic, assign) int64_t stickerPackId; +@property (nonatomic, assign) int64_t stickerPackAccessHash; + +@property (nonatomic, assign) bool isVoice; +@property (nonatomic, assign) bool isAudio; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *performer; +@property (nonatomic, assign) int32_t duration; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeDocumentMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeDocumentMediaAttachment.m new file mode 100644 index 0000000000..8d492ae704 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeDocumentMediaAttachment.m @@ -0,0 +1,84 @@ +#import "TGBridgeDocumentMediaAttachment.h" + +const NSInteger TGBridgeDocumentMediaAttachmentType = 0xE6C64318; + +NSString *const TGBridgeDocumentMediaDocumentIdKey = @"documentId"; +NSString *const TGBridgeDocumentMediaLocalDocumentIdKey = @"localDocumentId"; +NSString *const TGBridgeDocumentMediaFileSizeKey = @"fileSize"; +NSString *const TGBridgeDocumentMediaFileNameKey = @"fileName"; +NSString *const TGBridgeDocumentMediaImageSizeKey = @"imageSize"; +NSString *const TGBridgeDocumentMediaAnimatedKey = @"animated"; +NSString *const TGBridgeDocumentMediaStickerKey = @"sticker"; +NSString *const TGBridgeDocumentMediaStickerAltKey = @"stickerAlt"; +NSString *const TGBridgeDocumentMediaStickerPackIdKey = @"stickerPackId"; +NSString *const TGBridgeDocumentMediaStickerPackAccessHashKey = @"stickerPackAccessHash"; +NSString *const TGBridgeDocumentMediaAudioKey = @"audio"; +NSString *const TGBridgeDocumentMediaAudioTitleKey = @"title"; +NSString *const TGBridgeDocumentMediaAudioPerformerKey = @"performer"; +NSString *const TGBridgeDocumentMediaAudioVoice = @"voice"; +NSString *const TGBridgeDocumentMediaAudioDuration = @"duration"; + +@implementation TGBridgeDocumentMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _documentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaDocumentIdKey]; + _localDocumentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaLocalDocumentIdKey]; + _fileSize = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaFileSizeKey]; + _fileName = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaFileNameKey]; + _imageSize = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaImageSizeKey]; + _isAnimated = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAnimatedKey]; + _isSticker = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaStickerKey]; + _stickerAlt = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaStickerAltKey]; + _stickerPackId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackIdKey]; + _stickerPackAccessHash = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackAccessHashKey]; + _isAudio = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioKey]; + _title = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioTitleKey]; + _performer = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioPerformerKey]; + _isVoice = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioVoice]; + _duration = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaAudioDuration]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.documentId forKey:TGBridgeDocumentMediaDocumentIdKey]; + [aCoder encodeInt64:self.localDocumentId forKey:TGBridgeDocumentMediaLocalDocumentIdKey]; + [aCoder encodeInt32:self.fileSize forKey:TGBridgeDocumentMediaFileSizeKey]; + [aCoder encodeObject:self.fileName forKey:TGBridgeDocumentMediaFileNameKey]; + [aCoder encodeObject:self.imageSize forKey:TGBridgeDocumentMediaImageSizeKey]; + [aCoder encodeBool:self.isAnimated forKey:TGBridgeDocumentMediaAnimatedKey]; + [aCoder encodeBool:self.isSticker forKey:TGBridgeDocumentMediaStickerKey]; + [aCoder encodeObject:self.stickerAlt forKey:TGBridgeDocumentMediaStickerAltKey]; + [aCoder encodeInt64:self.stickerPackId forKey:TGBridgeDocumentMediaStickerPackIdKey]; + [aCoder encodeInt64:self.stickerPackAccessHash forKey:TGBridgeDocumentMediaStickerPackAccessHashKey]; + [aCoder encodeBool:self.isAudio forKey:TGBridgeDocumentMediaAudioKey]; + [aCoder encodeObject:self.title forKey:TGBridgeDocumentMediaAudioTitleKey]; + [aCoder encodeObject:self.performer forKey:TGBridgeDocumentMediaAudioPerformerKey]; + [aCoder encodeBool:self.isVoice forKey:TGBridgeDocumentMediaAudioVoice]; + [aCoder encodeInt32:self.duration forKey:TGBridgeDocumentMediaAudioDuration]; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeDocumentMediaAttachment *document = (TGBridgeDocumentMediaAttachment *)object; + + return (self.localDocumentId == 0 && self.documentId == document.documentId) || (self.localDocumentId != 0 && self.localDocumentId == document.localDocumentId); +} + ++ (NSInteger)mediaType +{ + return TGBridgeDocumentMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeForwardedMessageMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeForwardedMessageMediaAttachment.h new file mode 100644 index 0000000000..7077334864 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeForwardedMessageMediaAttachment.h @@ -0,0 +1,9 @@ +#import + +@interface TGBridgeForwardedMessageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t peerId; +@property (nonatomic, assign) int32_t mid; +@property (nonatomic, assign) int32_t date; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeForwardedMessageMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeForwardedMessageMediaAttachment.m new file mode 100644 index 0000000000..169e261cff --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeForwardedMessageMediaAttachment.m @@ -0,0 +1,35 @@ +#import "TGBridgeForwardedMessageMediaAttachment.h" + +const NSInteger TGBridgeForwardedMessageMediaAttachmentType = 0xAA1050C1; + +NSString *const TGBridgeForwardedMessageMediaPeerIdKey = @"peerId"; +NSString *const TGBridgeForwardedMessageMediaMidKey = @"mid"; +NSString *const TGBridgeForwardedMessageMediaDateKey = @"date"; + +@implementation TGBridgeForwardedMessageMediaAttachment + +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _peerId = [aDecoder decodeInt64ForKey:TGBridgeForwardedMessageMediaPeerIdKey]; + _mid = [aDecoder decodeInt32ForKey:TGBridgeForwardedMessageMediaMidKey]; + _date = [aDecoder decodeInt32ForKey:TGBridgeForwardedMessageMediaDateKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeForwardedMessageMediaPeerIdKey]; + [aCoder encodeInt32:self.mid forKey:TGBridgeForwardedMessageMediaMidKey]; + [aCoder encodeInt32:self.date forKey:TGBridgeForwardedMessageMediaDateKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeForwardedMessageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeImageMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeImageMediaAttachment.h new file mode 100644 index 0000000000..2a428dc15a --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeImageMediaAttachment.h @@ -0,0 +1,10 @@ +#import + +#import + +@interface TGBridgeImageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t imageId; +@property (nonatomic, assign) CGSize dimensions; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeImageMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeImageMediaAttachment.m new file mode 100644 index 0000000000..8ab5ec7044 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeImageMediaAttachment.m @@ -0,0 +1,33 @@ +#import "TGBridgeImageMediaAttachment.h" +#import + +const NSInteger TGBridgeImageMediaAttachmentType = 0x269BD8A8; + +NSString *const TGBridgeImageMediaImageIdKey = @"imageId"; +NSString *const TGBridgeImageMediaDimensionsKey = @"dimensions"; + +@implementation TGBridgeImageMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _imageId = [aDecoder decodeInt64ForKey:TGBridgeImageMediaImageIdKey]; + _dimensions = [aDecoder decodeCGSizeForKey:TGBridgeImageMediaDimensionsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.imageId forKey:TGBridgeImageMediaImageIdKey]; + [aCoder encodeCGSize:self.dimensions forKey:TGBridgeImageMediaDimensionsKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeImageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeLocationMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationMediaAttachment.h new file mode 100644 index 0000000000..bba943c11c --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationMediaAttachment.h @@ -0,0 +1,19 @@ +#import + +@interface TGBridgeVenueAttachment : NSObject + +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *address; +@property (nonatomic, strong) NSString *provider; +@property (nonatomic, strong) NSString *venueId; + +@end + +@interface TGBridgeLocationMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) double latitude; +@property (nonatomic, assign) double longitude; + +@property (nonatomic, strong) TGBridgeVenueAttachment *venue; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeLocationMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationMediaAttachment.m new file mode 100644 index 0000000000..f6762eb549 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationMediaAttachment.m @@ -0,0 +1,95 @@ +#import "TGBridgeLocationMediaAttachment.h" + +const NSInteger TGBridgeLocationMediaAttachmentType = 0x0C9ED06E; + +NSString *const TGBridgeLocationMediaLatitudeKey = @"lat"; +NSString *const TGBridgeLocationMediaLongitudeKey = @"lon"; +NSString *const TGBridgeLocationMediaVenueKey = @"venue"; + +NSString *const TGBridgeVenueTitleKey = @"title"; +NSString *const TGBridgeVenueAddressKey = @"address"; +NSString *const TGBridgeVenueProviderKey = @"provider"; +NSString *const TGBridgeVenueIdKey = @"venueId"; + +@implementation TGBridgeVenueAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _title = [aDecoder decodeObjectForKey:TGBridgeVenueTitleKey]; + _address = [aDecoder decodeObjectForKey:TGBridgeVenueAddressKey]; + _provider = [aDecoder decodeObjectForKey:TGBridgeVenueProviderKey]; + _venueId = [aDecoder decodeObjectForKey:TGBridgeVenueIdKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.title forKey:TGBridgeVenueTitleKey]; + [aCoder encodeObject:self.address forKey:TGBridgeVenueAddressKey]; + [aCoder encodeObject:self.provider forKey:TGBridgeVenueProviderKey]; + [aCoder encodeObject:self.venueId forKey:TGBridgeVenueIdKey]; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeVenueAttachment *venue = (TGBridgeVenueAttachment *)object; + + return [self.title isEqualToString:venue.title] && [self.address isEqualToString:venue.address] && [self.provider isEqualToString:venue.provider] && [self.venueId isEqualToString:venue.venueId]; +} + +@end + + +@implementation TGBridgeLocationMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _latitude = [aDecoder decodeDoubleForKey:TGBridgeLocationMediaLatitudeKey]; + _longitude = [aDecoder decodeDoubleForKey:TGBridgeLocationMediaLongitudeKey]; + _venue = [aDecoder decodeObjectForKey:TGBridgeLocationMediaVenueKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeDouble:self.latitude forKey:TGBridgeLocationMediaLatitudeKey]; + [aCoder encodeDouble:self.longitude forKey:TGBridgeLocationMediaLongitudeKey]; + [aCoder encodeObject:self.venue forKey:TGBridgeLocationMediaVenueKey]; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeLocationMediaAttachment *location = (TGBridgeLocationMediaAttachment *)object; + + bool equalCoord = (fabs(self.latitude - location.latitude) < DBL_EPSILON && fabs(self.longitude - location.longitude) < DBL_EPSILON); + bool equalVenue = (self.venue == nil && location.venue == nil) || ([self.venue isEqual:location.venue]); + + return equalCoord || equalVenue; +} + ++ (NSInteger)mediaType +{ + return TGBridgeLocationMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeLocationVenue.h b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationVenue.h new file mode 100644 index 0000000000..c626f76f9c --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationVenue.h @@ -0,0 +1,15 @@ +#import + +@class TGBridgeLocationMediaAttachment; + +@interface TGBridgeLocationVenue : NSObject + +@property (nonatomic) CLLocationCoordinate2D coordinate; +@property (nonatomic, strong) NSString *identifier; +@property (nonatomic, strong) NSString *provider; +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSString *address; + +- (TGBridgeLocationMediaAttachment *)locationAttachment; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeLocationVenue.m b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationVenue.m new file mode 100644 index 0000000000..01c8fc8c1a --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeLocationVenue.m @@ -0,0 +1,66 @@ +#import "TGBridgeLocationVenue.h" + +#import "TGBridgeLocationMediaAttachment.h" + +NSString *const TGBridgeLocationVenueLatitudeKey = @"lat"; +NSString *const TGBridgeLocationVenueLongitudeKey = @"lon"; +NSString *const TGBridgeLocationVenueIdentifierKey = @"identifier"; +NSString *const TGBridgeLocationVenueProviderKey = @"provider"; +NSString *const TGBridgeLocationVenueNameKey = @"name"; +NSString *const TGBridgeLocationVenueAddressKey = @"address"; + +@implementation TGBridgeLocationVenue + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _coordinate = CLLocationCoordinate2DMake([aDecoder decodeDoubleForKey:TGBridgeLocationVenueLatitudeKey], [aDecoder decodeDoubleForKey:TGBridgeLocationVenueLongitudeKey]); + _identifier = [aDecoder decodeObjectForKey:TGBridgeLocationVenueIdentifierKey]; + _provider = [aDecoder decodeObjectForKey:TGBridgeLocationVenueProviderKey]; + _name = [aDecoder decodeObjectForKey:TGBridgeLocationVenueNameKey]; + _address = [aDecoder decodeObjectForKey:TGBridgeLocationVenueAddressKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeDouble:self.coordinate.latitude forKey:TGBridgeLocationVenueLatitudeKey]; + [aCoder encodeDouble:self.coordinate.longitude forKey:TGBridgeLocationVenueLongitudeKey]; + [aCoder encodeObject:self.identifier forKey:TGBridgeLocationVenueIdentifierKey]; + [aCoder encodeObject:self.provider forKey:TGBridgeLocationVenueProviderKey]; + [aCoder encodeObject:self.name forKey:TGBridgeLocationVenueNameKey]; + [aCoder encodeObject:self.address forKey:TGBridgeLocationVenueAddressKey]; +} + +- (TGBridgeLocationMediaAttachment *)locationAttachment +{ + TGBridgeLocationMediaAttachment *attachment = [[TGBridgeLocationMediaAttachment alloc] init]; + attachment.latitude = self.coordinate.latitude; + attachment.longitude = self.coordinate.longitude; + + TGBridgeVenueAttachment *venueAttachment = [[TGBridgeVenueAttachment alloc] init]; + venueAttachment.title = self.name; + venueAttachment.address = self.address; + venueAttachment.provider = self.provider; + venueAttachment.venueId = self.identifier; + + attachment.venue = venueAttachment; + + return attachment; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + return [self.identifier isEqualToString:((TGBridgeLocationVenue *)object).identifier]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeMediaAttachment.h new file mode 100644 index 0000000000..3a564e1575 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMediaAttachment.h @@ -0,0 +1,11 @@ +#import + +@interface TGBridgeMediaAttachment : NSObject + +@property (nonatomic, readonly) NSInteger mediaType; + ++ (NSInteger)mediaType; + +@end + +extern NSString *const TGBridgeMediaAttachmentTypeKey; diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeMediaAttachment.m new file mode 100644 index 0000000000..971a23b64d --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMediaAttachment.m @@ -0,0 +1,32 @@ +#import "TGBridgeMediaAttachment.h" + +NSString *const TGBridgeMediaAttachmentTypeKey = @"type"; + +@implementation TGBridgeMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder +{ + self = [super init]; + if (self != nil) + { + + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder +{ + +} + +- (NSInteger)mediaType +{ + return 0; +} + ++ (NSInteger)mediaType +{ + return 0; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMessage.h b/submodules/WatchCommon/Watch/Sources/TGBridgeMessage.h new file mode 100644 index 0000000000..59ec1178d2 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMessage.h @@ -0,0 +1,65 @@ +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +typedef enum { + TGBridgeTextCheckingResultTypeUndefined, + TGBridgeTextCheckingResultTypeBold, + TGBridgeTextCheckingResultTypeItalic, + TGBridgeTextCheckingResultTypeCode, + TGBridgeTextCheckingResultTypePre +} TGBridgeTextCheckingResultType; + +@interface TGBridgeTextCheckingResult : NSObject + +@property (nonatomic, assign) TGBridgeTextCheckingResultType type; +@property (nonatomic, assign) NSRange range; + +@end + + +typedef NS_ENUM(NSUInteger, TGBridgeMessageDeliveryState) { + TGBridgeMessageDeliveryStateDelivered = 0, + TGBridgeMessageDeliveryStatePending = 1, + TGBridgeMessageDeliveryStateFailed = 2 +}; + +@interface TGBridgeMessage : NSObject + +@property (nonatomic) int32_t identifier; +@property (nonatomic) NSTimeInterval date; +@property (nonatomic) int64_t randomId; +@property (nonatomic) bool unread; +@property (nonatomic) bool deliveryError; +@property (nonatomic) TGBridgeMessageDeliveryState deliveryState; +@property (nonatomic) bool outgoing; +@property (nonatomic) int64_t fromUid; +@property (nonatomic) int64_t toUid; +@property (nonatomic) int64_t cid; +@property (nonatomic, strong) NSString *text; +@property (nonatomic, strong) NSArray *media; +@property (nonatomic) bool forceReply; + +- (NSIndexSet *)involvedUserIds; +- (NSArray *)textCheckingResults; + ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId; ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId replyToMessage:(TGBridgeMessage *)replyToMessage; ++ (instancetype)temporaryNewMessageForSticker:(TGBridgeDocumentMediaAttachment *)sticker userId:(int32_t)userId; ++ (instancetype)temporaryNewMessageForLocation:(TGBridgeLocationMediaAttachment *)location userId:(int32_t)userId; ++ (instancetype)temporaryNewMessageForAudioWithDuration:(int32_t)duration userId:(int32_t)userId localAudioId:(int64_t)localAudioId; + +@end + +extern NSString *const TGBridgeMessageKey; +extern NSString *const TGBridgeMessagesArrayKey; diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMessage.m b/submodules/WatchCommon/Watch/Sources/TGBridgeMessage.m new file mode 100644 index 0000000000..4f5e5bb6e7 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMessage.m @@ -0,0 +1,238 @@ +#import "TGBridgeMessage.h" +//#import "TGWatchCommon.h" +#import "TGBridgePeerIdAdapter.h" + +NSString *const TGBridgeMessageIdentifierKey = @"identifier"; +NSString *const TGBridgeMessageDateKey = @"date"; +NSString *const TGBridgeMessageRandomIdKey = @"randomId"; +NSString *const TGBridgeMessageFromUidKey = @"fromUid"; +NSString *const TGBridgeMessageCidKey = @"cid"; +NSString *const TGBridgeMessageTextKey = @"text"; +NSString *const TGBridgeMessageUnreadKey = @"unread"; +NSString *const TGBridgeMessageOutgoingKey = @"outgoing"; +NSString *const TGBridgeMessageMediaKey = @"media"; +NSString *const TGBridgeMessageDeliveryStateKey = @"deliveryState"; +NSString *const TGBridgeMessageForceReplyKey = @"forceReply"; + +NSString *const TGBridgeMessageKey = @"message"; +NSString *const TGBridgeMessagesArrayKey = @"messages"; + +@interface TGBridgeMessage () +{ + NSArray *_textCheckingResults; +} +@end + +@implementation TGBridgeMessage + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt32ForKey:TGBridgeMessageIdentifierKey]; + _date = [aDecoder decodeDoubleForKey:TGBridgeMessageDateKey]; + _randomId = [aDecoder decodeInt64ForKey:TGBridgeMessageRandomIdKey]; + _fromUid = [aDecoder decodeInt64ForKey:TGBridgeMessageFromUidKey]; + _cid = [aDecoder decodeInt64ForKey:TGBridgeMessageCidKey]; + _text = [aDecoder decodeObjectForKey:TGBridgeMessageTextKey]; + _outgoing = [aDecoder decodeBoolForKey:TGBridgeMessageOutgoingKey]; + _unread = [aDecoder decodeBoolForKey:TGBridgeMessageUnreadKey]; + _deliveryState = [aDecoder decodeInt32ForKey:TGBridgeMessageDeliveryStateKey]; + _media = [aDecoder decodeObjectForKey:TGBridgeMessageMediaKey]; + _forceReply = [aDecoder decodeBoolForKey:TGBridgeMessageForceReplyKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.identifier forKey:TGBridgeMessageIdentifierKey]; + [aCoder encodeDouble:self.date forKey:TGBridgeMessageDateKey]; + [aCoder encodeInt64:self.randomId forKey:TGBridgeMessageRandomIdKey]; + [aCoder encodeInt64:self.fromUid forKey:TGBridgeMessageFromUidKey]; + [aCoder encodeInt64:self.cid forKey:TGBridgeMessageCidKey]; + [aCoder encodeObject:self.text forKey:TGBridgeMessageTextKey]; + [aCoder encodeBool:self.outgoing forKey:TGBridgeMessageOutgoingKey]; + [aCoder encodeBool:self.unread forKey:TGBridgeMessageUnreadKey]; + [aCoder encodeInt32:self.deliveryState forKey:TGBridgeMessageDeliveryStateKey]; + [aCoder encodeObject:self.media forKey:TGBridgeMessageMediaKey]; + [aCoder encodeBool:self.forceReply forKey:TGBridgeMessageForceReplyKey]; +} + +- (NSIndexSet *)involvedUserIds +{ + NSMutableIndexSet *userIds = [[NSMutableIndexSet alloc] init]; + if (!TGPeerIdIsChannel(self.fromUid)) + [userIds addIndex:(int32_t)self.fromUid]; + + for (TGBridgeMediaAttachment *attachment in self.media) + { + if ([attachment isKindOfClass:[TGBridgeContactMediaAttachment class]]) + { + TGBridgeContactMediaAttachment *contactAttachment = (TGBridgeContactMediaAttachment *)attachment; + if (contactAttachment.uid != 0) + [userIds addIndex:contactAttachment.uid]; + } + else if ([attachment isKindOfClass:[TGBridgeForwardedMessageMediaAttachment class]]) + { + TGBridgeForwardedMessageMediaAttachment *forwardAttachment = (TGBridgeForwardedMessageMediaAttachment *)attachment; + if (forwardAttachment.peerId != 0 && !TGPeerIdIsChannel(forwardAttachment.peerId)) + [userIds addIndex:(int32_t)forwardAttachment.peerId]; + } + else if ([attachment isKindOfClass:[TGBridgeReplyMessageMediaAttachment class]]) + { + TGBridgeReplyMessageMediaAttachment *replyAttachment = (TGBridgeReplyMessageMediaAttachment *)attachment; + if (replyAttachment.message != nil && !TGPeerIdIsChannel(replyAttachment.message.fromUid)) + [userIds addIndex:(int32_t)replyAttachment.message.fromUid]; + } + else if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]]) + { + TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment; + if (actionAttachment.actionData[@"uid"] != nil) + [userIds addIndex:(int32_t)[actionAttachment.actionData[@"uid"] intValue]]; + } + } + + return userIds; +} + +- (NSArray *)textCheckingResults +{ + if (_textCheckingResults == nil) + { + NSMutableArray *results = [[NSMutableArray alloc] init]; + + NSArray *entities = nil; + for (TGBridgeMediaAttachment *attachment in self.media) + { + if ([attachment isKindOfClass:[TGBridgeMessageEntitiesAttachment class]]) + { + entities = ((TGBridgeMessageEntitiesAttachment *)attachment).entities; + break; + } + } + + for (TGBridgeMessageEntity *entity in entities) + { + TGBridgeTextCheckingResult *result = [[TGBridgeTextCheckingResult alloc] init]; + result.range = entity.range; + + if ([entity isKindOfClass:[TGBridgeMessageEntityBold class]]) + result.type = TGBridgeTextCheckingResultTypeBold; + else if ([entity isKindOfClass:[TGBridgeMessageEntityItalic class]]) + result.type = TGBridgeTextCheckingResultTypeItalic; + else if ([entity isKindOfClass:[TGBridgeMessageEntityCode class]]) + result.type = TGBridgeTextCheckingResultTypeCode; + else if ([entity isKindOfClass:[TGBridgeMessageEntityPre class]]) + result.type = TGBridgeTextCheckingResultTypePre; + + if (result.type != TGBridgeTextCheckingResultTypeUndefined) + [results addObject:result]; + } + + _textCheckingResults = results; + } + + return _textCheckingResults; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + TGBridgeMessage *message = (TGBridgeMessage *)object; + + if (self.randomId != 0) + return self.randomId == message.randomId; + else + return self.identifier == message.identifier; +} + ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId +{ + return [self temporaryNewMessageForText:text userId:userId replyToMessage:nil]; +} + ++ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId replyToMessage:(TGBridgeMessage *)replyToMessage +{ + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + + int32_t messageId = 0; + arc4random_buf(&messageId, 4); + + TGBridgeMessage *message = [[TGBridgeMessage alloc] init]; + message->_identifier = -abs(messageId); + message->_fromUid = userId; + message->_randomId = randomId; + message->_unread = true; + message->_outgoing = true; + message->_deliveryState = TGBridgeMessageDeliveryStatePending; + message->_text = text; + message->_date = [[NSDate date] timeIntervalSince1970]; + + if (replyToMessage != nil) + { + TGBridgeReplyMessageMediaAttachment *replyAttachment = [[TGBridgeReplyMessageMediaAttachment alloc] init]; + replyAttachment.mid = replyToMessage.identifier; + replyAttachment.message = replyToMessage; + + message->_media = @[ replyToMessage ]; + } + + return message; +} + ++ (instancetype)temporaryNewMessageForSticker:(TGBridgeDocumentMediaAttachment *)sticker userId:(int32_t)userId +{ + return [self _temporaryNewMessageForMediaAttachment:sticker userId:userId]; +} + ++ (instancetype)temporaryNewMessageForLocation:(TGBridgeLocationMediaAttachment *)location userId:(int32_t)userId +{ + return [self _temporaryNewMessageForMediaAttachment:location userId:userId]; +} + ++ (instancetype)temporaryNewMessageForAudioWithDuration:(int32_t)duration userId:(int32_t)userId localAudioId:(int64_t)localAudioId +{ + TGBridgeDocumentMediaAttachment *document = [[TGBridgeDocumentMediaAttachment alloc] init]; + document.isAudio = true; + document.isVoice = true; + document.localDocumentId = localAudioId; + document.duration = duration; + + return [self _temporaryNewMessageForMediaAttachment:document userId:userId]; +} + ++ (instancetype)_temporaryNewMessageForMediaAttachment:(TGBridgeMediaAttachment *)attachment userId:(int32_t)userId +{ + int64_t randomId = 0; + arc4random_buf(&randomId, 8); + + int32_t messageId = 0; + arc4random_buf(&messageId, 4); + + TGBridgeMessage *message = [[TGBridgeMessage alloc] init]; + message->_identifier = -abs(messageId); + message->_fromUid = userId; + message->_unread = true; + message->_outgoing = true; + message->_deliveryState = TGBridgeMessageDeliveryStatePending; + message->_date = [[NSDate date] timeIntervalSince1970]; + + message->_media = @[ attachment ]; + + return message; +} + +@end + + +@implementation TGBridgeTextCheckingResult + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntities.h b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntities.h new file mode 100644 index 0000000000..669ff3b21d --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntities.h @@ -0,0 +1,59 @@ +#import + +@interface TGBridgeMessageEntity : NSObject + +@property (nonatomic, assign) NSRange range; + ++ (instancetype)entitityWithRange:(NSRange)range; + +@end + + +@interface TGBridgeMessageEntityUrl : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityEmail : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityTextUrl : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityMention : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityHashtag : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityBotCommand : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityBold : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityItalic : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityCode : TGBridgeMessageEntity + +@end + + +@interface TGBridgeMessageEntityPre : TGBridgeMessageEntity + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntities.m b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntities.m new file mode 100644 index 0000000000..8d639a9468 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntities.m @@ -0,0 +1,83 @@ +#import "TGBridgeMessageEntities.h" + +NSString *const TGBridgeMessageEntityLocationKey = @"loc"; +NSString *const TGBridgeMessageEntityLengthKey = @"len"; + +@implementation TGBridgeMessageEntity + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + NSUInteger loc = [aDecoder decodeIntegerForKey:TGBridgeMessageEntityLocationKey]; + NSUInteger len = [aDecoder decodeIntegerForKey:TGBridgeMessageEntityLengthKey]; + _range = NSMakeRange(loc, len); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInteger:self.range.location forKey:TGBridgeMessageEntityLocationKey]; + [aCoder encodeInteger:self.range.length forKey:TGBridgeMessageEntityLengthKey]; +} + ++ (instancetype)entitityWithRange:(NSRange)range +{ + TGBridgeMessageEntity *entity = [[self alloc] init]; + entity.range = range; + return entity; +} + +@end + + +@implementation TGBridgeMessageEntityUrl + +@end + + +@implementation TGBridgeMessageEntityEmail + +@end + + +@implementation TGBridgeMessageEntityTextUrl + +@end + + +@implementation TGBridgeMessageEntityMention + +@end + + +@implementation TGBridgeMessageEntityHashtag + +@end + + +@implementation TGBridgeMessageEntityBotCommand + +@end + + +@implementation TGBridgeMessageEntityBold + +@end + + +@implementation TGBridgeMessageEntityItalic + +@end + + +@implementation TGBridgeMessageEntityCode + +@end + + +@implementation TGBridgeMessageEntityPre + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntitiesAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntitiesAttachment.h new file mode 100644 index 0000000000..9aaec90b25 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntitiesAttachment.h @@ -0,0 +1,8 @@ +#import +#import + +@interface TGBridgeMessageEntitiesAttachment : TGBridgeMediaAttachment + +@property (nonatomic, strong) NSArray *entities; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntitiesAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntitiesAttachment.m new file mode 100644 index 0000000000..fb5cb3b7d7 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeMessageEntitiesAttachment.m @@ -0,0 +1,30 @@ +#import "TGBridgeMessageEntitiesAttachment.h" + +const NSInteger TGBridgeMessageEntitiesAttachmentType = 0x8c2e3cce; + +NSString *const TGBridgeMessageEntitiesKey = @"entities"; + +@implementation TGBridgeMessageEntitiesAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _entities = [aDecoder decodeObjectForKey:TGBridgeMessageEntitiesKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.entities forKey:TGBridgeMessageEntitiesKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeMessageEntitiesAttachmentType; +} + + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgePeerNotificationSettings.h b/submodules/WatchCommon/Watch/Sources/TGBridgePeerNotificationSettings.h new file mode 100644 index 0000000000..fce723cecb --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgePeerNotificationSettings.h @@ -0,0 +1,7 @@ +#import + +@interface TGBridgePeerNotificationSettings : NSObject + +@property (nonatomic, assign) int32_t muteFor; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgePeerNotificationSettings.m b/submodules/WatchCommon/Watch/Sources/TGBridgePeerNotificationSettings.m new file mode 100644 index 0000000000..662c2f4ca4 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgePeerNotificationSettings.m @@ -0,0 +1,22 @@ +#import "TGBridgePeerNotificationSettings.h" + +NSString *const TGBridgePeerNotificationSettingsMuteForKey = @"muteFor"; + +@implementation TGBridgePeerNotificationSettings + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _muteFor = [aDecoder decodeInt32ForKey:TGBridgePeerNotificationSettingsMuteForKey]; + } + return self; +} + +- (void)encodeWithCoder:(nonnull NSCoder *)aCoder +{ + [aCoder encodeInt32:self.muteFor forKey:TGBridgePeerNotificationSettingsMuteForKey]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMarkupMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMarkupMediaAttachment.h new file mode 100644 index 0000000000..430398271a --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMarkupMediaAttachment.h @@ -0,0 +1,9 @@ +#import + +@class TGBridgeBotReplyMarkup; + +@interface TGBridgeReplyMarkupMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, strong) TGBridgeBotReplyMarkup *replyMarkup; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMarkupMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMarkupMediaAttachment.m new file mode 100644 index 0000000000..1299c900cd --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMarkupMediaAttachment.m @@ -0,0 +1,29 @@ +#import "TGBridgeReplyMarkupMediaAttachment.h" + +const NSInteger TGBridgeReplyMarkupMediaAttachmentType = 0x5678acc1; + +NSString *const TGBridgeReplyMarkupMediaMessageKey = @"replyMarkup"; + +@implementation TGBridgeReplyMarkupMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _replyMarkup = [aDecoder decodeObjectForKey:TGBridgeReplyMarkupMediaMessageKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.replyMarkup forKey:TGBridgeReplyMarkupMediaMessageKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeReplyMarkupMediaAttachmentType; +} + +@end \ No newline at end of file diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMessageMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMessageMediaAttachment.h new file mode 100644 index 0000000000..a5dbeae13c --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMessageMediaAttachment.h @@ -0,0 +1,10 @@ +#import + +@class TGBridgeMessage; + +@interface TGBridgeReplyMessageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int32_t mid; +@property (nonatomic, strong) TGBridgeMessage *message; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMessageMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMessageMediaAttachment.m new file mode 100644 index 0000000000..fbc2456919 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeReplyMessageMediaAttachment.m @@ -0,0 +1,33 @@ +#import "TGBridgeReplyMessageMediaAttachment.h" +#import "TGBridgeMessage.h" + +const NSInteger TGBridgeReplyMessageMediaAttachmentType = 414002169; + +NSString *const TGBridgeReplyMessageMediaMidKey = @"mid"; +NSString *const TGBridgeReplyMessageMediaMessageKey = @"message"; + +@implementation TGBridgeReplyMessageMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _mid = [aDecoder decodeInt32ForKey:TGBridgeReplyMessageMediaMidKey]; + _message = [aDecoder decodeObjectForKey:TGBridgeReplyMessageMediaMessageKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.mid forKey:TGBridgeReplyMessageMediaMidKey]; + [aCoder encodeObject:self.message forKey:TGBridgeReplyMessageMediaMessageKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeReplyMessageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeSubscriptions.h b/submodules/WatchCommon/Watch/Sources/TGBridgeSubscriptions.h new file mode 100644 index 0000000000..f2f7ec7ac5 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeSubscriptions.h @@ -0,0 +1,268 @@ +#import + +#import +#import + +@class TGBridgeMediaAttachment; +@class TGBridgeImageMediaAttachment; +@class TGBridgeVideoMediaAttachment; +@class TGBridgeDocumentMediaAttachment; +@class TGBridgeLocationMediaAttachment; +@class TGBridgePeerNotificationSettings; + +@interface TGBridgeAudioSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) TGBridgeMediaAttachment *attachment; +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; + +- (instancetype)initWithAttachment:(TGBridgeMediaAttachment *)attachment peerId:(int64_t)peerId messageId:(int32_t)messageId; + +@end + + +@interface TGBridgeAudioSentSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t conversationId; + +- (instancetype)initWithConversationId:(int64_t)conversationId; + +@end + + +@interface TGBridgeChatListSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int32_t limit; + +- (instancetype)initWithLimit:(int32_t)limit; + +@end + + +@interface TGBridgeChatMessageListSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t atMessageId; +@property (nonatomic, readonly) NSUInteger rangeMessageCount; + +- (instancetype)initWithPeerId:(int64_t)peerId atMessageId:(int32_t)messageId rangeMessageCount:(NSUInteger)rangeMessageCount; + +@end + + +@interface TGBridgeChatMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId; + +@end + + +@interface TGBridgeReadChatMessageListSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId; + +@end + + +@interface TGBridgeContactsSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) NSString *query; + +- (instancetype)initWithQuery:(NSString *)query; + +@end + + +@interface TGBridgeConversationSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end + + +@interface TGBridgeNearbyVenuesSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; +@property (nonatomic, readonly) int32_t limit; + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate limit:(int32_t)limit; + +@end + + +@interface TGBridgeMediaThumbnailSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, readonly) CGSize size; +@property (nonatomic, readonly) bool notification; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId size:(CGSize)size notification:(bool)notification; + +@end + + +typedef NS_ENUM(NSUInteger, TGBridgeMediaAvatarType) { + TGBridgeMediaAvatarTypeSmall, + TGBridgeMediaAvatarTypeProfile, + TGBridgeMediaAvatarTypeLarge +}; + +@interface TGBridgeMediaAvatarSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) NSString *url; +@property (nonatomic, readonly) TGBridgeMediaAvatarType type; + +- (instancetype)initWithPeerId:(int64_t)peerId url:(NSString *)url type:(TGBridgeMediaAvatarType)type; + +@end + +@interface TGBridgeMediaStickerSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t documentId; +@property (nonatomic, readonly) int64_t stickerPackId; +@property (nonatomic, readonly) int64_t stickerPackAccessHash; +@property (nonatomic, readonly) int64_t stickerPeerId; +@property (nonatomic, readonly) int32_t stickerMessageId; +@property (nonatomic, readonly) bool notification; +@property (nonatomic, readonly) CGSize size; + +- (instancetype)initWithDocumentId:(int64_t)documentId stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash stickerPeerId:(int64_t)stickerPeerId stickerMessageId:(int32_t)stickerMessageId notification:(bool)notification size:(CGSize)size; + +@end + + +@interface TGBridgePeerSettingsSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end + +@interface TGBridgePeerUpdateNotificationSettingsSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end + +@interface TGBridgePeerUpdateBlockStatusSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) bool blocked; + +- (instancetype)initWithPeerId:(int64_t)peerId blocked:(bool)blocked; + +@end + + +@interface TGBridgeRemoteSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, readonly) int32_t type; +@property (nonatomic, readonly) bool autoPlay; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId type:(int32_t)type autoPlay:(bool)autoPlay; + +@end + + +@interface TGBridgeSendTextMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) NSString *text; +@property (nonatomic, readonly) int32_t replyToMid; + +- (instancetype)initWithPeerId:(int64_t)peerId text:(NSString *)text replyToMid:(int32_t)replyToMid; + +@end + + +@interface TGBridgeSendStickerMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) TGBridgeDocumentMediaAttachment *document; +@property (nonatomic, readonly) int32_t replyToMid; + +- (instancetype)initWithPeerId:(int64_t)peerId document:(TGBridgeDocumentMediaAttachment *)document replyToMid:(int32_t)replyToMid; + +@end + + +@interface TGBridgeSendLocationMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) TGBridgeLocationMediaAttachment *location; +@property (nonatomic, readonly) int32_t replyToMid; + +- (instancetype)initWithPeerId:(int64_t)peerId location:(TGBridgeLocationMediaAttachment *)location replyToMid:(int32_t)replyToMid; + +@end + + +@interface TGBridgeSendForwardedMessageSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, readonly) int64_t targetPeerId; + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId targetPeerId:(int64_t)targetPeerId; + +@end + + +@interface TGBridgeStateSubscription : TGBridgeSubscription + +@end + + +@interface TGBridgeStickerPacksSubscription : TGBridgeSubscription + +@end + + +@interface TGBridgeRecentStickersSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int32_t limit; + +- (instancetype)initWithLimit:(int32_t)limit; + +@end + + +@interface TGBridgeUserInfoSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) NSArray *userIds; + +- (instancetype)initWithUserIds:(NSArray *)userIds; + +@end + + +@interface TGBridgeUserBotInfoSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) NSArray *userIds; + +- (instancetype)initWithUserIds:(NSArray *)userIds; + +@end + +@interface TGBridgeBotReplyMarkupSubscription : TGBridgeSubscription + +@property (nonatomic, readonly) int64_t peerId; + +- (instancetype)initWithPeerId:(int64_t)peerId; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeSubscriptions.m b/submodules/WatchCommon/Watch/Sources/TGBridgeSubscriptions.m new file mode 100644 index 0000000000..8c4b50d224 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeSubscriptions.m @@ -0,0 +1,1048 @@ +#import "TGBridgeSubscriptions.h" + +#import + +#import "TGBridgeImageMediaAttachment.h" +#import "TGBridgeVideoMediaAttachment.h" +#import "TGBridgeDocumentMediaAttachment.h" +#import "TGBridgeLocationMediaAttachment.h" +#import "TGBridgePeerNotificationSettings.h" + +NSString *const TGBridgeAudioSubscriptionName = @"media.audio"; +NSString *const TGBridgeAudioSubscriptionAttachmentKey = @"attachment"; +NSString *const TGBridgeAudioSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeAudioSubscriptionMessageIdKey = @"messageId"; + +@implementation TGBridgeAudioSubscription + +- (instancetype)initWithAttachment:(TGBridgeMediaAttachment *)attachment peerId:(int64_t)peerId messageId:(int32_t)messageId +{ + self = [super init]; + if (self != nil) + { + _attachment = attachment; + _peerId = peerId; + _messageId = messageId; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.attachment forKey:TGBridgeAudioSubscriptionAttachmentKey]; + [aCoder encodeInt64:self.peerId forKey:TGBridgeAudioSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeAudioSubscriptionMessageIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _attachment = [aDecoder decodeObjectForKey:TGBridgeAudioSubscriptionAttachmentKey]; + _peerId = [aDecoder decodeInt64ForKey:TGBridgeAudioSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeAudioSubscriptionMessageIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeAudioSubscriptionName; +} + +@end + + +NSString *const TGBridgeAudioSentSubscriptionName = @"media.audioSent"; +NSString *const TGBridgeAudioSentSubscriptionConversationIdKey = @"conversationId"; + +@implementation TGBridgeAudioSentSubscription + +- (instancetype)initWithConversationId:(int64_t)conversationId +{ + self = [super init]; + if (self != nil) + { + _conversationId = conversationId; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.conversationId forKey:TGBridgeAudioSentSubscriptionConversationIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _conversationId = [aDecoder decodeInt64ForKey:TGBridgeAudioSentSubscriptionConversationIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeAudioSentSubscriptionName; +} + +@end + + +NSString *const TGBridgeChatListSubscriptionName = @"chats.chatList"; +NSString *const TGBridgeChatListSubscriptionLimitKey = @"limit"; + +@implementation TGBridgeChatListSubscription + +- (instancetype)initWithLimit:(int32_t)limit +{ + self = [super init]; + if (self != nil) + { + _limit = limit; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.limit forKey:TGBridgeChatListSubscriptionLimitKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _limit = [aDecoder decodeInt32ForKey:TGBridgeChatListSubscriptionLimitKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeChatListSubscriptionName; +} + +@end + + +NSString *const TGBridgeChatMessageListSubscriptionName = @"chats.chatMessageList"; +NSString *const TGBridgeChatMessageListSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeChatMessageListSubscriptionAtMessageIdKey = @"atMessageId"; +NSString *const TGBridgeChatMessageListSubscriptionRangeMessageCountKey = @"rangeMessageCount"; + +@implementation TGBridgeChatMessageListSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId atMessageId:(int32_t)messageId rangeMessageCount:(NSUInteger)rangeMessageCount +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _atMessageId = messageId; + _rangeMessageCount = rangeMessageCount; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeChatMessageListSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.atMessageId forKey:TGBridgeChatMessageListSubscriptionAtMessageIdKey]; + [aCoder encodeInt32:(int32_t)self.rangeMessageCount forKey:TGBridgeChatMessageListSubscriptionRangeMessageCountKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeChatMessageListSubscriptionPeerIdKey]; + _atMessageId = [aDecoder decodeInt32ForKey:TGBridgeChatMessageListSubscriptionAtMessageIdKey]; + _rangeMessageCount = [aDecoder decodeInt32ForKey:TGBridgeChatMessageListSubscriptionRangeMessageCountKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeChatMessageListSubscriptionName; +} + +@end + + +NSString *const TGBridgeChatMessageSubscriptionName = @"chats.message"; +NSString *const TGBridgeChatMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeChatMessageSubscriptionMessageIdKey = @"mid"; + +@implementation TGBridgeChatMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + } + return self; +} + +- (bool)synchronous +{ + return true; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeChatMessageSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeChatMessageSubscriptionMessageIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeChatMessageSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeChatMessageSubscriptionMessageIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeChatMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeReadChatMessageListSubscriptionName = @"chats.readChatMessageList"; +NSString *const TGBridgeReadChatMessageListSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeReadChatMessageListSubscriptionMessageIdKey = @"mid"; + +@implementation TGBridgeReadChatMessageListSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeReadChatMessageListSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeReadChatMessageListSubscriptionMessageIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeReadChatMessageListSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeReadChatMessageListSubscriptionMessageIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeReadChatMessageListSubscriptionName; +} + +@end + + +NSString *const TGBridgeContactsSubscriptionName = @"contacts.search"; +NSString *const TGBridgeContactsSubscriptionQueryKey = @"query"; + +@implementation TGBridgeContactsSubscription + +- (instancetype)initWithQuery:(NSString *)query +{ + self = [super init]; + if (self != nil) + { + _query = query; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.query forKey:TGBridgeContactsSubscriptionQueryKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _query = [aDecoder decodeObjectForKey:TGBridgeContactsSubscriptionQueryKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeContactsSubscriptionName; +} + +@end + + +NSString *const TGBridgeConversationSubscriptionName = @"chats.conversation"; +NSString *const TGBridgeConversationSubscriptionPeerIdKey = @"peerId"; + +@implementation TGBridgeConversationSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeConversationSubscriptionPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeConversationSubscriptionPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeConversationSubscriptionName; +} + +@end + + +NSString *const TGBridgeNearbyVenuesSubscriptionName = @"location.nearbyVenues"; +NSString *const TGBridgeNearbyVenuesSubscriptionLatitudeKey = @"lat"; +NSString *const TGBridgeNearbyVenuesSubscriptionLongitudeKey = @"lon"; +NSString *const TGBridgeNearbyVenuesSubscriptionLimitKey = @"limit"; + +@implementation TGBridgeNearbyVenuesSubscription + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate limit:(int32_t)limit +{ + self = [super init]; + if (self != nil) + { + _coordinate = coordinate; + _limit = limit; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeDouble:self.coordinate.latitude forKey:TGBridgeNearbyVenuesSubscriptionLatitudeKey]; + [aCoder encodeDouble:self.coordinate.longitude forKey:TGBridgeNearbyVenuesSubscriptionLongitudeKey]; + [aCoder encodeInt32:self.limit forKey:TGBridgeNearbyVenuesSubscriptionLimitKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _coordinate = CLLocationCoordinate2DMake([aDecoder decodeDoubleForKey:TGBridgeNearbyVenuesSubscriptionLatitudeKey], + [aDecoder decodeDoubleForKey:TGBridgeNearbyVenuesSubscriptionLongitudeKey]); + _limit = [aDecoder decodeInt32ForKey:TGBridgeNearbyVenuesSubscriptionLimitKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeNearbyVenuesSubscriptionName; +} + +@end + + +NSString *const TGBridgeMediaThumbnailSubscriptionName = @"media.thumbnail"; +NSString *const TGBridgeMediaThumbnailPeerIdKey = @"peerId"; +NSString *const TGBridgeMediaThumbnailMessageIdKey = @"mid"; +NSString *const TGBridgeMediaThumbnailSizeKey = @"size"; +NSString *const TGBridgeMediaThumbnailNotificationKey = @"notification"; + +@implementation TGBridgeMediaThumbnailSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId size:(CGSize)size notification:(bool)notification +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + _size = size; + _notification = notification; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeMediaThumbnailPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeMediaThumbnailMessageIdKey]; + [aCoder encodeCGSize:self.size forKey:TGBridgeMediaThumbnailSizeKey]; + [aCoder encodeBool:self.notification forKey:TGBridgeMediaThumbnailNotificationKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeMediaThumbnailPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeMediaThumbnailMessageIdKey]; + _size = [aDecoder decodeCGSizeForKey:TGBridgeMediaThumbnailSizeKey]; + _notification = [aDecoder decodeBoolForKey:TGBridgeMediaThumbnailNotificationKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeMediaThumbnailSubscriptionName; +} + +@end + + +NSString *const TGBridgeMediaAvatarSubscriptionName = @"media.avatar"; +NSString *const TGBridgeMediaAvatarPeerIdKey = @"peerId"; +NSString *const TGBridgeMediaAvatarUrlKey = @"url"; +NSString *const TGBridgeMediaAvatarTypeKey = @"type"; + +@implementation TGBridgeMediaAvatarSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId url:(NSString *)url type:(TGBridgeMediaAvatarType)type +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _url = url; + _type = type; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeMediaAvatarPeerIdKey]; + [aCoder encodeObject:self.url forKey:TGBridgeMediaAvatarUrlKey]; + [aCoder encodeInt32:self.type forKey:TGBridgeMediaAvatarTypeKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeMediaAvatarPeerIdKey]; + _url = [aDecoder decodeObjectForKey:TGBridgeMediaAvatarUrlKey]; + _type = [aDecoder decodeInt32ForKey:TGBridgeMediaAvatarTypeKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeMediaAvatarSubscriptionName; +} + +@end + + +NSString *const TGBridgeMediaStickerSubscriptionName = @"media.sticker"; +NSString *const TGBridgeMediaStickerDocumentIdKey = @"documentId"; +NSString *const TGBridgeMediaStickerPackIdKey = @"packId"; +NSString *const TGBridgeMediaStickerPackAccessHashKey = @"accessHash"; +NSString *const TGBridgeMediaStickerPeerIdKey = @"peerId"; +NSString *const TGBridgeMediaStickerMessageIdKey = @"mid"; +NSString *const TGBridgeMediaStickerNotificationKey = @"notification"; +NSString *const TGBridgeMediaStickerSizeKey = @"size"; + +@implementation TGBridgeMediaStickerSubscription + +- (instancetype)initWithDocumentId:(int64_t)documentId stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash stickerPeerId:(int64_t)stickerPeerId stickerMessageId:(int32_t)stickerMessageId notification:(bool)notification size:(CGSize)size +{ + self = [super init]; + if (self != nil) + { + _documentId = documentId; + _stickerPackId = stickerPackId; + _stickerPackAccessHash = stickerPackAccessHash; + _stickerPeerId = stickerPeerId; + _stickerMessageId = stickerMessageId; + _notification = notification; + _size = size; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.documentId forKey:TGBridgeMediaStickerDocumentIdKey]; + [aCoder encodeInt64:self.stickerPackId forKey:TGBridgeMediaStickerPackIdKey]; + [aCoder encodeInt64:self.stickerPackAccessHash forKey:TGBridgeMediaStickerPackAccessHashKey]; + [aCoder encodeInt64:self.stickerPeerId forKey:TGBridgeMediaStickerPeerIdKey]; + [aCoder encodeInt32:self.stickerMessageId forKey:TGBridgeMediaStickerMessageIdKey]; + [aCoder encodeBool:self.notification forKey:TGBridgeMediaStickerNotificationKey]; + [aCoder encodeCGSize:self.size forKey:TGBridgeMediaStickerSizeKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _documentId = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerDocumentIdKey]; + _stickerPackId = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerPackIdKey]; + _stickerPackAccessHash = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerPackAccessHashKey]; + _stickerPeerId = [aDecoder decodeInt64ForKey:TGBridgeMediaStickerPeerIdKey]; + _stickerMessageId = [aDecoder decodeInt32ForKey:TGBridgeMediaStickerMessageIdKey]; + _notification = [aDecoder decodeBoolForKey:TGBridgeMediaStickerNotificationKey]; + _size = [aDecoder decodeCGSizeForKey:TGBridgeMediaStickerSizeKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeMediaStickerSubscriptionName; +} + +@end + + +NSString *const TGBridgePeerSettingsSubscriptionName = @"peer.settings"; +NSString *const TGBridgePeerSettingsSubscriptionPeerIdKey = @"peerId"; + +@implementation TGBridgePeerSettingsSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgePeerSettingsSubscriptionPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgePeerSettingsSubscriptionPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgePeerSettingsSubscriptionName; +} + +@end + + +NSString *const TGBridgePeerUpdateNotificationSettingsSubscriptionName = @"peer.notificationSettings"; +NSString *const TGBridgePeerUpdateNotificationSettingsSubscriptionPeerIdKey = @"peerId"; + +@implementation TGBridgePeerUpdateNotificationSettingsSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgePeerUpdateNotificationSettingsSubscriptionPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgePeerUpdateNotificationSettingsSubscriptionPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgePeerUpdateNotificationSettingsSubscriptionName; +} + +@end + + +NSString *const TGBridgePeerUpdateBlockStatusSubscriptionName = @"peer.updateBlocked"; +NSString *const TGBridgePeerUpdateBlockStatusSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgePeerUpdateBlockStatusSubscriptionBlockedKey = @"blocked"; + +@implementation TGBridgePeerUpdateBlockStatusSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId blocked:(bool)blocked +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _blocked = blocked; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgePeerUpdateBlockStatusSubscriptionPeerIdKey]; + [aCoder encodeBool:self.blocked forKey:TGBridgePeerUpdateBlockStatusSubscriptionBlockedKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgePeerUpdateBlockStatusSubscriptionPeerIdKey]; + _blocked = [aDecoder decodeBoolForKey:TGBridgePeerUpdateBlockStatusSubscriptionBlockedKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgePeerUpdateBlockStatusSubscriptionName; +} + +@end + + +NSString *const TGBridgeRemoteSubscriptionName = @"remote.request"; +NSString *const TGBridgeRemotePeerIdKey = @"peerId"; +NSString *const TGBridgeRemoteMessageIdKey = @"mid"; +NSString *const TGBridgeRemoteTypeKey = @"mediaType"; +NSString *const TGBridgeRemoteAutoPlayKey = @"autoPlay"; + +@implementation TGBridgeRemoteSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId type:(int32_t)type autoPlay:(bool)autoPlay +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + _type = type; + _autoPlay = autoPlay; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeRemotePeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeRemoteMessageIdKey]; + [aCoder encodeInt32:self.type forKey:TGBridgeRemoteTypeKey]; + [aCoder encodeBool:self.autoPlay forKey:TGBridgeRemoteAutoPlayKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeRemotePeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeRemoteMessageIdKey]; + _type = [aDecoder decodeInt32ForKey:TGBridgeRemoteTypeKey]; + _autoPlay = [aDecoder decodeBoolForKey:TGBridgeRemoteAutoPlayKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeRemoteSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendTextMessageSubscriptionName = @"sendMessage.text"; +NSString *const TGBridgeSendTextMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendTextMessageSubscriptionTextKey = @"text"; +NSString *const TGBridgeSendTextMessageSubscriptionReplyToMidKey = @"replyToMid"; + +@implementation TGBridgeSendTextMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId text:(NSString *)text replyToMid:(int32_t)replyToMid +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _text = text; + _replyToMid = replyToMid; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendTextMessageSubscriptionPeerIdKey]; + [aCoder encodeObject:self.text forKey:TGBridgeSendTextMessageSubscriptionTextKey]; + [aCoder encodeInt32:self.replyToMid forKey:TGBridgeSendTextMessageSubscriptionReplyToMidKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendTextMessageSubscriptionPeerIdKey]; + _text = [aDecoder decodeObjectForKey:TGBridgeSendTextMessageSubscriptionTextKey]; + _replyToMid = [aDecoder decodeInt32ForKey:TGBridgeSendTextMessageSubscriptionReplyToMidKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendTextMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendStickerMessageSubscriptionName = @"sendMessage.sticker"; +NSString *const TGBridgeSendStickerMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendStickerMessageSubscriptionDocumentKey = @"document"; +NSString *const TGBridgeSendStickerMessageSubscriptionReplyToMidKey = @"replyToMid"; + +@implementation TGBridgeSendStickerMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId document:(TGBridgeDocumentMediaAttachment *)document replyToMid:(int32_t)replyToMid +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _document = document; + _replyToMid = replyToMid; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendStickerMessageSubscriptionPeerIdKey]; + [aCoder encodeObject:self.document forKey:TGBridgeSendStickerMessageSubscriptionDocumentKey]; + [aCoder encodeInt32:self.replyToMid forKey:TGBridgeSendStickerMessageSubscriptionReplyToMidKey]; +} + + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendStickerMessageSubscriptionPeerIdKey]; + _document = [aDecoder decodeObjectForKey:TGBridgeSendStickerMessageSubscriptionDocumentKey]; + _replyToMid = [aDecoder decodeInt32ForKey:TGBridgeSendStickerMessageSubscriptionReplyToMidKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendStickerMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendLocationMessageSubscriptionName = @"sendMessage.location"; +NSString *const TGBridgeSendLocationMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendLocationMessageSubscriptionLocationKey = @"location"; +NSString *const TGBridgeSendLocationMessageSubscriptionReplyToMidKey = @"replyToMid"; + +@implementation TGBridgeSendLocationMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId location:(TGBridgeLocationMediaAttachment *)location replyToMid:(int32_t)replyToMid +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _location = location; + _replyToMid = replyToMid; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendLocationMessageSubscriptionPeerIdKey]; + [aCoder encodeObject:self.location forKey:TGBridgeSendLocationMessageSubscriptionLocationKey]; + [aCoder encodeInt32:self.replyToMid forKey:TGBridgeSendLocationMessageSubscriptionReplyToMidKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendLocationMessageSubscriptionPeerIdKey]; + _location = [aDecoder decodeObjectForKey:TGBridgeSendLocationMessageSubscriptionLocationKey]; + _replyToMid = [aDecoder decodeInt32ForKey:TGBridgeSendLocationMessageSubscriptionReplyToMidKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendLocationMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeSendForwardedMessageSubscriptionName = @"sendMessage.forward"; +NSString *const TGBridgeSendForwardedMessageSubscriptionPeerIdKey = @"peerId"; +NSString *const TGBridgeSendForwardedMessageSubscriptionMidKey = @"mid"; +NSString *const TGBridgeSendForwardedMessageSubscriptionTargetPeerIdKey = @"targetPeerId"; + +@implementation TGBridgeSendForwardedMessageSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId targetPeerId:(int64_t)targetPeerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + _messageId = messageId; + _targetPeerId = targetPeerId; + } + return self; +} + +- (bool)renewable +{ + return false; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeSendForwardedMessageSubscriptionPeerIdKey]; + [aCoder encodeInt32:self.messageId forKey:TGBridgeSendForwardedMessageSubscriptionMidKey]; + [aCoder encodeInt64:self.targetPeerId forKey:TGBridgeSendForwardedMessageSubscriptionTargetPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeSendForwardedMessageSubscriptionPeerIdKey]; + _messageId = [aDecoder decodeInt32ForKey:TGBridgeSendForwardedMessageSubscriptionMidKey]; + _targetPeerId = [aDecoder decodeInt64ForKey:TGBridgeSendForwardedMessageSubscriptionTargetPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeSendForwardedMessageSubscriptionName; +} + +@end + + +NSString *const TGBridgeStateSubscriptionName = @"state.syncState"; + +@implementation TGBridgeStateSubscription + +- (bool)dropPreviouslyQueued +{ + return true; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeStateSubscriptionName; +} + +@end + + +NSString *const TGBridgeStickerPacksSubscriptionName = @"stickers.packs"; + +@implementation TGBridgeStickerPacksSubscription + ++ (NSString *)subscriptionName +{ + return TGBridgeStickerPacksSubscriptionName; +} + +@end + + +NSString *const TGBridgeRecentStickersSubscriptionName = @"stickers.recent"; +NSString *const TGBridgeRecentStickersSubscriptionLimitKey = @"limit"; + +@implementation TGBridgeRecentStickersSubscription + +- (instancetype)initWithLimit:(int32_t)limit +{ + self = [super init]; + if (self != nil) + { + _limit = limit; + } + return self; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.limit forKey:TGBridgeRecentStickersSubscriptionLimitKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _limit = [aDecoder decodeInt32ForKey:TGBridgeRecentStickersSubscriptionLimitKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeRecentStickersSubscriptionName; +} + +@end + + +NSString *const TGBridgeUserInfoSubscriptionName = @"user.userInfo"; +NSString *const TGBridgeUserInfoSubscriptionUserIdsKey = @"uids"; + +@implementation TGBridgeUserInfoSubscription + +- (instancetype)initWithUserIds:(NSArray *)userIds +{ + self = [super init]; + if (self != nil) + { + _userIds = userIds; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.userIds forKey:TGBridgeUserInfoSubscriptionUserIdsKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _userIds = [aDecoder decodeObjectForKey:TGBridgeUserInfoSubscriptionUserIdsKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeUserInfoSubscriptionName; +} + +@end + + +NSString *const TGBridgeUserBotInfoSubscriptionName = @"user.botInfo"; +NSString *const TGBridgeUserBotInfoSubscriptionUserIdsKey = @"uids"; + +@implementation TGBridgeUserBotInfoSubscription + +- (instancetype)initWithUserIds:(NSArray *)userIds +{ + self = [super init]; + if (self != nil) + { + _userIds = userIds; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.userIds forKey:TGBridgeUserBotInfoSubscriptionUserIdsKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _userIds = [aDecoder decodeObjectForKey:TGBridgeUserBotInfoSubscriptionUserIdsKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeUserBotInfoSubscriptionName; +} + +@end + + +NSString *const TGBridgeBotReplyMarkupSubscriptionName = @"user.botReplyMarkup"; +NSString *const TGBridgeBotReplyMarkupPeerIdKey = @"peerId"; + +@implementation TGBridgeBotReplyMarkupSubscription + +- (instancetype)initWithPeerId:(int64_t)peerId +{ + self = [super init]; + if (self != nil) + { + _peerId = peerId; + } + return self; +} + +- (bool)dropPreviouslyQueued +{ + return true; +} + +- (void)_serializeParametersWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.peerId forKey:TGBridgeBotReplyMarkupPeerIdKey]; +} + +- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder +{ + _peerId = [aDecoder decodeInt64ForKey:TGBridgeBotReplyMarkupPeerIdKey]; +} + ++ (NSString *)subscriptionName +{ + return TGBridgeBotReplyMarkupSubscriptionName; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeUnsupportedMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeUnsupportedMediaAttachment.h new file mode 100644 index 0000000000..a115df56bf --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeUnsupportedMediaAttachment.h @@ -0,0 +1,9 @@ +#import + +@interface TGBridgeUnsupportedMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, strong) NSString *compactTitle; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *subtitle; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeUnsupportedMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeUnsupportedMediaAttachment.m new file mode 100644 index 0000000000..b51e422fd1 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeUnsupportedMediaAttachment.m @@ -0,0 +1,35 @@ +#import "TGBridgeUnsupportedMediaAttachment.h" + +const NSInteger TGBridgeUnsupportedMediaAttachmentType = 0x3837BEF7; + +NSString *const TGBridgeUnsupportedMediaCompactTitleKey = @"compactTitle"; +NSString *const TGBridgeUnsupportedMediaTitleKey = @"title"; +NSString *const TGBridgeUnsupportedMediaSubtitleKey = @"subtitle"; + +@implementation TGBridgeUnsupportedMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _compactTitle = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaCompactTitleKey]; + _title = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaTitleKey]; + _subtitle = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaSubtitleKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.compactTitle forKey:TGBridgeUnsupportedMediaCompactTitleKey]; + [aCoder encodeObject:self.title forKey:TGBridgeUnsupportedMediaTitleKey]; + [aCoder encodeObject:self.subtitle forKey:TGBridgeUnsupportedMediaSubtitleKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeUnsupportedMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeUser.h b/submodules/WatchCommon/Watch/Sources/TGBridgeUser.h new file mode 100644 index 0000000000..9aca3fa520 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeUser.h @@ -0,0 +1,59 @@ +#import + +@class TGBridgeBotInfo; +@class TGBridgeUserChange; + +typedef NS_ENUM(NSUInteger, TGBridgeUserKind) { + TGBridgeUserKindGeneric, + TGBridgeUserKindBot, + TGBridgeUserKindSmartBot +}; + +typedef NS_ENUM(NSUInteger, TGBridgeBotKind) { + TGBridgeBotKindGeneric, + TGBridgeBotKindPrivate +}; + +@interface TGBridgeUser : NSObject + +@property (nonatomic) int64_t identifier; +@property (nonatomic, strong) NSString *firstName; +@property (nonatomic, strong) NSString *lastName; +@property (nonatomic, strong) NSString *userName; +@property (nonatomic, strong) NSString *phoneNumber; +@property (nonatomic, strong) NSString *prettyPhoneNumber; +@property (nonatomic, strong) NSString *about; + +@property (nonatomic) bool online; +@property (nonatomic) NSTimeInterval lastSeen; + +@property (nonatomic, strong) NSString *photoSmall; +@property (nonatomic, strong) NSString *photoBig; + +@property (nonatomic) TGBridgeUserKind kind; +@property (nonatomic) TGBridgeBotKind botKind; +@property (nonatomic) int32_t botVersion; + +@property (nonatomic) bool verified; + +@property (nonatomic) int32_t userVersion; + +- (NSString *)displayName; +- (TGBridgeUserChange *)changeFromUser:(TGBridgeUser *)user; +- (TGBridgeUser *)userByApplyingChange:(TGBridgeUserChange *)change; + +- (bool)isBot; + +@end + + +@interface TGBridgeUserChange : NSObject + +@property (nonatomic, readonly) int32_t userIdentifier; +@property (nonatomic, readonly) NSDictionary *fields; + +- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields; + +@end + +extern NSString *const TGBridgeUsersDictionaryKey; diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeUser.m b/submodules/WatchCommon/Watch/Sources/TGBridgeUser.m new file mode 100644 index 0000000000..4c0fed8d97 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeUser.m @@ -0,0 +1,286 @@ +#import "TGBridgeUser.h" +//#import "TGWatchCommon.h" +#import "TGBridgeBotInfo.h" + +//#import "../Extension/TGStringUtils.h" + +NSString *const TGBridgeUserIdentifierKey = @"identifier"; +NSString *const TGBridgeUserFirstNameKey = @"firstName"; +NSString *const TGBridgeUserLastNameKey = @"lastName"; +NSString *const TGBridgeUserUserNameKey = @"userName"; +NSString *const TGBridgeUserPhoneNumberKey = @"phoneNumber"; +NSString *const TGBridgeUserPrettyPhoneNumberKey = @"prettyPhoneNumber"; +NSString *const TGBridgeUserOnlineKey = @"online"; +NSString *const TGBridgeUserLastSeenKey = @"lastSeen"; +NSString *const TGBridgeUserPhotoSmallKey = @"photoSmall"; +NSString *const TGBridgeUserPhotoBigKey = @"photoBig"; +NSString *const TGBridgeUserKindKey = @"kind"; +NSString *const TGBridgeUserBotKindKey = @"botKind"; +NSString *const TGBridgeUserBotVersionKey = @"botVersion"; +NSString *const TGBridgeUserVerifiedKey = @"verified"; +NSString *const TGBridgeUserAboutKey = @"about"; +NSString *const TGBridgeUserVersionKey = @"version"; + +NSString *const TGBridgeUsersDictionaryKey = @"users"; + +@implementation TGBridgeUser + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _identifier = [aDecoder decodeInt64ForKey:TGBridgeUserIdentifierKey]; + _firstName = [aDecoder decodeObjectForKey:TGBridgeUserFirstNameKey]; + _lastName = [aDecoder decodeObjectForKey:TGBridgeUserLastNameKey]; + _userName = [aDecoder decodeObjectForKey:TGBridgeUserUserNameKey]; + _phoneNumber = [aDecoder decodeObjectForKey:TGBridgeUserPhoneNumberKey]; + _prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeUserPrettyPhoneNumberKey]; + _online = [aDecoder decodeBoolForKey:TGBridgeUserOnlineKey]; + _lastSeen = [aDecoder decodeDoubleForKey:TGBridgeUserLastSeenKey]; + _photoSmall = [aDecoder decodeObjectForKey:TGBridgeUserPhotoSmallKey]; + _photoBig = [aDecoder decodeObjectForKey:TGBridgeUserPhotoBigKey]; + _kind = [aDecoder decodeInt32ForKey:TGBridgeUserKindKey]; + _botKind = [aDecoder decodeInt32ForKey:TGBridgeUserBotKindKey]; + _botVersion = [aDecoder decodeInt32ForKey:TGBridgeUserBotVersionKey]; + _verified = [aDecoder decodeBoolForKey:TGBridgeUserVerifiedKey]; + _about = [aDecoder decodeObjectForKey:TGBridgeUserAboutKey]; + _userVersion = [aDecoder decodeInt32ForKey:TGBridgeUserVersionKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.identifier forKey:TGBridgeUserIdentifierKey]; + [aCoder encodeObject:self.firstName forKey:TGBridgeUserFirstNameKey]; + [aCoder encodeObject:self.lastName forKey:TGBridgeUserLastNameKey]; + [aCoder encodeObject:self.userName forKey:TGBridgeUserUserNameKey]; + [aCoder encodeObject:self.phoneNumber forKey:TGBridgeUserPhoneNumberKey]; + [aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeUserPrettyPhoneNumberKey]; + [aCoder encodeBool:self.online forKey:TGBridgeUserOnlineKey]; + [aCoder encodeDouble:self.lastSeen forKey:TGBridgeUserLastSeenKey]; + [aCoder encodeObject:self.photoSmall forKey:TGBridgeUserPhotoSmallKey]; + [aCoder encodeObject:self.photoBig forKey:TGBridgeUserPhotoBigKey]; + [aCoder encodeInt32:self.kind forKey:TGBridgeUserKindKey]; + [aCoder encodeInt32:self.botKind forKey:TGBridgeUserBotKindKey]; + [aCoder encodeInt32:self.botVersion forKey:TGBridgeUserBotVersionKey]; + [aCoder encodeBool:self.verified forKey:TGBridgeUserVerifiedKey]; + [aCoder encodeObject:self.about forKey:TGBridgeUserAboutKey]; + [aCoder encodeInt32:self.userVersion forKey:TGBridgeUserVersionKey]; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone +{ + TGBridgeUser *user = [[TGBridgeUser alloc] init]; + user->_identifier = self.identifier; + user->_firstName = self.firstName; + user->_lastName = self.lastName; + user->_userName = self.userName; + user->_phoneNumber = self.phoneNumber; + user->_prettyPhoneNumber = self.prettyPhoneNumber; + user->_online = self.online; + user->_lastSeen = self.lastSeen; + user->_photoSmall = self.photoSmall; + user->_photoBig = self.photoBig; + user->_kind = self.kind; + user->_botKind = self.botKind; + user->_botVersion = self.botVersion; + user->_verified = self.verified; + user->_about = self.about; + user->_userVersion = self.userVersion; + + return user; +} + +- (NSString *)displayName +{ + NSString *firstName = self.firstName; + NSString *lastName = self.lastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + { + return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; + } + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + +- (bool)isBot +{ + return (self.kind == TGBridgeUserKindBot || self.kind ==TGBridgeUserKindSmartBot); +} + +- (TGBridgeUserChange *)changeFromUser:(TGBridgeUser *)user +{ + NSMutableDictionary *fields = [[NSMutableDictionary alloc] init]; + + [self _compareString:self.firstName oldString:user.firstName dict:fields key:TGBridgeUserFirstNameKey]; + [self _compareString:self.lastName oldString:user.lastName dict:fields key:TGBridgeUserLastNameKey]; + [self _compareString:self.userName oldString:user.userName dict:fields key:TGBridgeUserUserNameKey]; + [self _compareString:self.phoneNumber oldString:user.phoneNumber dict:fields key:TGBridgeUserPhoneNumberKey]; + [self _compareString:self.prettyPhoneNumber oldString:user.prettyPhoneNumber dict:fields key:TGBridgeUserPrettyPhoneNumberKey]; + + if (self.online != user.online) + fields[TGBridgeUserOnlineKey] = @(self.online); + + if (fabs(self.lastSeen - user.lastSeen) > DBL_EPSILON) + fields[TGBridgeUserLastSeenKey] = @(self.lastSeen); + + [self _compareString:self.photoSmall oldString:user.photoSmall dict:fields key:TGBridgeUserPhotoSmallKey]; + [self _compareString:self.photoBig oldString:user.photoBig dict:fields key:TGBridgeUserPhotoBigKey]; + + if (self.kind != user.kind) + fields[TGBridgeUserKindKey] = @(self.kind); + + if (self.botKind != user.botKind) + fields[TGBridgeUserBotKindKey] = @(self.botKind); + + if (self.botVersion != user.botVersion) + fields[TGBridgeUserBotVersionKey] = @(self.botVersion); + + if (self.verified != user.verified) + fields[TGBridgeUserVerifiedKey] = @(self.verified); + + if (fields.count == 0) + return nil; + + return [[TGBridgeUserChange alloc] initWithUserIdentifier:user.identifier fields:fields]; +} + +- (void)_compareString:(NSString *)newString oldString:(NSString *)oldString dict:(NSMutableDictionary *)dict key:(NSString *)key +{ + if (newString == nil && oldString == nil) + return; + + if (![newString isEqualToString:oldString]) + { + if (newString == nil) + dict[key] = [NSNull null]; + else + dict[key] = newString; + } +} + +- (TGBridgeUser *)userByApplyingChange:(TGBridgeUserChange *)change +{ + if (change.userIdentifier != self.identifier) + return nil; + + TGBridgeUser *user = [self copy]; + + NSString *firstNameChange = change.fields[TGBridgeUserFirstNameKey]; + if (firstNameChange != nil) + user->_firstName = [self _stringForFieldChange:firstNameChange]; + + NSString *lastNameChange = change.fields[TGBridgeUserLastNameKey]; + if (lastNameChange != nil) + user->_lastName = [self _stringForFieldChange:lastNameChange]; + + NSString *userNameChange = change.fields[TGBridgeUserUserNameKey]; + if (userNameChange != nil) + user->_userName = [self _stringForFieldChange:userNameChange]; + + NSString *phoneNumberChange = change.fields[TGBridgeUserPhoneNumberKey]; + if (phoneNumberChange != nil) + user->_phoneNumber = [self _stringForFieldChange:phoneNumberChange]; + + NSString *prettyPhoneNumberChange = change.fields[TGBridgeUserPrettyPhoneNumberKey]; + if (prettyPhoneNumberChange != nil) + user->_prettyPhoneNumber = [self _stringForFieldChange:prettyPhoneNumberChange]; + + NSNumber *onlineChange = change.fields[TGBridgeUserOnlineKey]; + if (onlineChange != nil) + user->_online = [onlineChange boolValue]; + + NSNumber *lastSeenChange = change.fields[TGBridgeUserLastSeenKey]; + if (lastSeenChange != nil) + user->_lastSeen = [lastSeenChange doubleValue]; + + NSString *photoSmallChange = change.fields[TGBridgeUserPhotoSmallKey]; + if (photoSmallChange != nil) + user->_photoSmall = [self _stringForFieldChange:photoSmallChange]; + + NSString *photoBigChange = change.fields[TGBridgeUserPhotoBigKey]; + if (photoBigChange != nil) + user->_photoBig = [self _stringForFieldChange:photoBigChange]; + + NSNumber *kindChange = change.fields[TGBridgeUserKindKey]; + if (kindChange != nil) + user->_kind = (int32_t)[kindChange intValue]; + + NSNumber *botKindChange = change.fields[TGBridgeUserBotKindKey]; + if (botKindChange != nil) + user->_botKind = (int32_t)[botKindChange intValue]; + + NSNumber *botVersionChange = change.fields[TGBridgeUserBotVersionKey]; + if (botVersionChange != nil) + user->_botVersion = (int32_t)[botVersionChange intValue]; + + NSNumber *verifiedChange = change.fields[TGBridgeUserVerifiedKey]; + if (verifiedChange != nil) + user->_verified = [verifiedChange boolValue]; + + return user; +} + +- (NSString *)_stringForFieldChange:(NSString *)fieldChange +{ + if ([fieldChange isKindOfClass:[NSNull class]]) + return nil; + + return fieldChange; +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return YES; + + if (!object || ![object isKindOfClass:[self class]]) + return NO; + + return self.identifier == ((TGBridgeUser *)object).identifier; +} + +@end + + +NSString *const TGBridgeUserChangeIdentifierKey = @"userIdentifier"; +NSString *const TGBridgeUserChangeFieldsKey = @"fields"; + +@implementation TGBridgeUserChange + +- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields +{ + self = [super init]; + if (self != nil) + { + _userIdentifier = userIdentifier; + _fields = fields; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _userIdentifier = [aDecoder decodeInt32ForKey:TGBridgeUserChangeIdentifierKey]; + _fields = [aDecoder decodeObjectForKey:TGBridgeUserChangeFieldsKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt32:self.userIdentifier forKey:TGBridgeUserChangeIdentifierKey]; + [aCoder encodeObject:self.fields forKey:TGBridgeUserChangeFieldsKey]; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeVideoMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeVideoMediaAttachment.h new file mode 100644 index 0000000000..12eb34bd23 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeVideoMediaAttachment.h @@ -0,0 +1,12 @@ +#import + +#import + +@interface TGBridgeVideoMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t videoId; +@property (nonatomic, assign) int32_t duration; +@property (nonatomic, assign) CGSize dimensions; +@property (nonatomic, assign) bool round; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeVideoMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeVideoMediaAttachment.m new file mode 100644 index 0000000000..66fe867996 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeVideoMediaAttachment.m @@ -0,0 +1,39 @@ +#import "TGBridgeVideoMediaAttachment.h" +#import + +const NSInteger TGBridgeVideoMediaAttachmentType = 0x338EAA20; + +NSString *const TGBridgeVideoMediaVideoIdKey = @"videoId"; +NSString *const TGBridgeVideoMediaDimensionsKey = @"dimensions"; +NSString *const TGBridgeVideoMediaDurationKey = @"duration"; +NSString *const TGBridgeVideoMediaRoundKey = @"round"; + +@implementation TGBridgeVideoMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _videoId = [aDecoder decodeInt64ForKey:TGBridgeVideoMediaVideoIdKey]; + _dimensions = [aDecoder decodeCGSizeForKey:TGBridgeVideoMediaDimensionsKey]; + _duration = [aDecoder decodeInt32ForKey:TGBridgeVideoMediaDurationKey]; + _round = [aDecoder decodeBoolForKey:TGBridgeVideoMediaRoundKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.videoId forKey:TGBridgeVideoMediaVideoIdKey]; + [aCoder encodeCGSize:self.dimensions forKey:TGBridgeVideoMediaDimensionsKey]; + [aCoder encodeInt32:self.duration forKey:TGBridgeVideoMediaDurationKey]; + [aCoder encodeBool:self.round forKey:TGBridgeVideoMediaRoundKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeVideoMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeWebPageMediaAttachment.h b/submodules/WatchCommon/Watch/Sources/TGBridgeWebPageMediaAttachment.h new file mode 100644 index 0000000000..dda2a83e4a --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeWebPageMediaAttachment.h @@ -0,0 +1,23 @@ +#import + +#import + +@class TGBridgeImageMediaAttachment; + +@interface TGBridgeWebPageMediaAttachment : TGBridgeMediaAttachment + +@property (nonatomic, assign) int64_t webPageId; +@property (nonatomic, strong) NSString *url; +@property (nonatomic, strong) NSString *displayUrl; +@property (nonatomic, strong) NSString *pageType; +@property (nonatomic, strong) NSString *siteName; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *pageDescription; +@property (nonatomic, strong) TGBridgeImageMediaAttachment *photo; +@property (nonatomic, strong) NSString *embedUrl; +@property (nonatomic, strong) NSString *embedType; +@property (nonatomic, assign) CGSize embedSize; +@property (nonatomic, strong) NSNumber *duration; +@property (nonatomic, strong) NSString *author; + +@end diff --git a/submodules/WatchCommon/Watch/Sources/TGBridgeWebPageMediaAttachment.m b/submodules/WatchCommon/Watch/Sources/TGBridgeWebPageMediaAttachment.m new file mode 100644 index 0000000000..3983075d69 --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/TGBridgeWebPageMediaAttachment.m @@ -0,0 +1,65 @@ +#import "TGBridgeWebPageMediaAttachment.h" +#import "TGBridgeImageMediaAttachment.h" +#import + +const NSInteger TGBridgeWebPageMediaAttachmentType = 0x584197af; + +NSString *const TGBridgeWebPageMediaWebPageIdKey = @"webPageId"; +NSString *const TGBridgeWebPageMediaUrlKey = @"url"; +NSString *const TGBridgeWebPageMediaDisplayUrlKey = @"displayUrl"; +NSString *const TGBridgeWebPageMediaPageTypeKey = @"pageType"; +NSString *const TGBridgeWebPageMediaSiteNameKey = @"siteName"; +NSString *const TGBridgeWebPageMediaTitleKey = @"title"; +NSString *const TGBridgeWebPageMediaPageDescriptionKey = @"pageDescription"; +NSString *const TGBridgeWebPageMediaPhotoKey = @"photo"; +NSString *const TGBridgeWebPageMediaEmbedUrlKey = @"embedUrl"; +NSString *const TGBridgeWebPageMediaEmbedTypeKey = @"embedType"; +NSString *const TGBridgeWebPageMediaEmbedSizeKey = @"embedSize"; +NSString *const TGBridgeWebPageMediaDurationKey = @"duration"; +NSString *const TGBridgeWebPageMediaAuthorKey = @"author"; + +@implementation TGBridgeWebPageMediaAttachment + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + _webPageId = [aDecoder decodeInt64ForKey:TGBridgeWebPageMediaWebPageIdKey]; + _url = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaUrlKey]; + _displayUrl = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaDisplayUrlKey]; + _pageType = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPageTypeKey]; + _siteName = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaSiteNameKey]; + _title = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaTitleKey]; + _pageDescription = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPageDescriptionKey]; + _photo = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPhotoKey]; + _embedUrl = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaEmbedUrlKey]; + _embedSize = [aDecoder decodeCGSizeForKey:TGBridgeWebPageMediaEmbedSizeKey]; + _duration = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaDurationKey]; + _author = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaAuthorKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:self.webPageId forKey:TGBridgeWebPageMediaWebPageIdKey]; + [aCoder encodeObject:self.url forKey:TGBridgeWebPageMediaUrlKey]; + [aCoder encodeObject:self.displayUrl forKey:TGBridgeWebPageMediaDisplayUrlKey]; + [aCoder encodeObject:self.pageType forKey:TGBridgeWebPageMediaPageTypeKey]; + [aCoder encodeObject:self.siteName forKey:TGBridgeWebPageMediaSiteNameKey]; + [aCoder encodeObject:self.title forKey:TGBridgeWebPageMediaTitleKey]; + [aCoder encodeObject:self.pageDescription forKey:TGBridgeWebPageMediaPageDescriptionKey]; + [aCoder encodeObject:self.photo forKey:TGBridgeWebPageMediaPhotoKey]; + [aCoder encodeObject:self.embedUrl forKey:TGBridgeWebPageMediaEmbedUrlKey]; + [aCoder encodeCGSize:self.embedSize forKey:TGBridgeWebPageMediaEmbedSizeKey]; + [aCoder encodeObject:self.duration forKey:TGBridgeWebPageMediaDurationKey]; + [aCoder encodeObject:self.author forKey:TGBridgeWebPageMediaAuthorKey]; +} + ++ (NSInteger)mediaType +{ + return TGBridgeWebPageMediaAttachmentType; +} + +@end diff --git a/submodules/WatchCommon/Watch/Sources/WatchCommonWatch.h b/submodules/WatchCommon/Watch/Sources/WatchCommonWatch.h new file mode 100644 index 0000000000..38e8745e0f --- /dev/null +++ b/submodules/WatchCommon/Watch/Sources/WatchCommonWatch.h @@ -0,0 +1,36 @@ +#import + +//! Project version number for WatchCommon. +FOUNDATION_EXPORT double WatchCommonVersionNumber; + +//! Project version string for WatchCommon. +FOUNDATION_EXPORT const unsigned char WatchCommonVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +