mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Revert Watch App removal
This commit is contained in:
parent
d183f786e0
commit
d574500fdb
@ -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 }
|
||||
|
23
submodules/AccountContext/Sources/WatchManager.swift
Normal file
23
submodules/AccountContext/Sources/WatchManager.swift
Normal file
@ -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<Bool, NoError> { get }
|
||||
var navigateToMessageRequested: Signal<EngineMessage.Id, NoError> { get }
|
||||
var runningTasks: Signal<WatchRunningTasks?, NoError> { get }
|
||||
}
|
@ -895,6 +895,9 @@ private func languageSearchableItems(context: AccountContext, localizations: [Lo
|
||||
}
|
||||
|
||||
func settingsSearchableItems(context: AccountContext, notificationExceptionsList: Signal<NotificationExceptionsList?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, hasTwoStepAuth: Signal<Bool?, NoError>, twoStepAuthData: Signal<TwoStepVerificationAccessConfiguration?, NoError>, activeSessionsContext: Signal<ActiveSessionsContext?, NoError>, webSessionsContext: Signal<WebSessionsContext?, NoError>) -> 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))
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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?
|
||||
|
@ -31,6 +31,7 @@ import OverlayStatusController
|
||||
import UndoUI
|
||||
import LegacyUI
|
||||
import PassportUI
|
||||
import WatchBridge
|
||||
import SettingsUI
|
||||
import AppBundle
|
||||
import UrlHandling
|
||||
|
@ -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 {
|
||||
|
25
submodules/TelegramUI/Sources/WatchManager.swift
Normal file
25
submodules/TelegramUI/Sources/WatchManager.swift
Normal file
@ -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<Bool, NoError> {
|
||||
return self.arguments?.appInstalled ?? .single(false)
|
||||
}
|
||||
|
||||
public var navigateToMessageRequested: Signal<EngineMessage.Id, NoError> {
|
||||
return self.arguments?.navigateToMessageRequested ?? .never()
|
||||
}
|
||||
|
||||
public var runningTasks: Signal<WatchRunningTasks?, NoError> {
|
||||
return self.arguments?.runningTasks ?? .single(nil)
|
||||
}
|
||||
}
|
33
submodules/WatchBridge/BUILD
Normal file
33
submodules/WatchBridge/BUILD
Normal file
@ -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",
|
||||
],
|
||||
)
|
27
submodules/WatchBridge/Impl/BUILD
Normal file
27
submodules/WatchBridge/Impl/BUILD
Normal file
@ -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",
|
||||
],
|
||||
)
|
@ -0,0 +1,29 @@
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@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
|
@ -0,0 +1,3 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <WatchBridgeImpl/TGBridgeServer.h>
|
770
submodules/WatchBridge/Impl/Sources/TGBridgeServer.m
Normal file
770
submodules/WatchBridge/Impl/Sources/TGBridgeServer.m
Normal file
@ -0,0 +1,770 @@
|
||||
#import <WatchBridgeImpl/TGBridgeServer.h>
|
||||
|
||||
#import <LegacyComponents/LegacyComponents.h>
|
||||
#import <WatchConnectivity/WatchConnectivity.h>
|
||||
#import <os/lock.h>
|
||||
#import <WatchCommon/WatchCommon.h>
|
||||
|
||||
@interface TGBridgeSignalManager : NSObject
|
||||
|
||||
- (bool)startSignalForKey:(NSString *)key producer:(SSignal *(^)())producer;
|
||||
- (void)haltSignalForKey:(NSString *)key;
|
||||
- (void)haltAllSignals;
|
||||
|
||||
@end
|
||||
|
||||
@interface TGBridgeServer () <WCSessionDelegate>
|
||||
{
|
||||
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<SDisposable>)task replyHandler:(void (^)(NSData *))replyHandler completion:(void (^)(void))completion
|
||||
{
|
||||
if (_allowBackgroundTimeExtension) {
|
||||
_allowBackgroundTimeExtension();
|
||||
}
|
||||
|
||||
__block id<SDisposable> 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<SDisposable>(__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<NSCoding>)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<SDisposable>)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<SDisposable> 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<SDisposable> 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 <SDisposable> *disposable in _disposables.allValues)
|
||||
[disposable dispose];
|
||||
[_disposables removeAllObjects];
|
||||
os_unfair_lock_unlock(&_lock);
|
||||
}
|
||||
|
||||
@end
|
219
submodules/WatchBridge/Sources/WatchCommunicationManager.swift
Normal file
219
submodules/WatchBridge/Sources/WatchCommunicationManager.swift
Normal file
@ -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<Bool, NoError>
|
||||
public let navigateToMessageRequested: Signal<MessageId, NoError>
|
||||
public let runningTasks: Signal<WatchRunningTasks?, NoError>
|
||||
|
||||
public init(appInstalled: Signal<Bool, NoError>, navigateToMessageRequested: Signal<MessageId, NoError>, runningTasks: Signal<WatchRunningTasks?, NoError>) {
|
||||
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<AccountContext?>(nil)
|
||||
private let presets = Promise<WatchPresetSettings?>(nil)
|
||||
private let navigateToMessagePipe = ValuePipe<MessageId>()
|
||||
|
||||
public init(queue: Queue, context: Signal<WatchCommunicationManagerContext?, NoError>, 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<Bool, NoError> {
|
||||
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<WatchRunningTasks?, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = self.server.runningRequestsSignal().start(next: { value in
|
||||
if let value = value as? Dictionary<String, Any> {
|
||||
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<AnyHashable, Any>, asMessageData: Bool = false) -> Signal<Void, NoError> {
|
||||
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<AnyHashable, Any>) -> Signal<Void, NoError> {
|
||||
return Signal { subscriber in
|
||||
self.server.sendFile(with: data, metadata: metadata, errorHandler: {})
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
} |> runOn(self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
public func watchCommunicationManager(context: Signal<WatchCommunicationManagerContext?, NoError>, allowBackgroundTimeExtension: @escaping (Double) -> Void) -> Signal<WatchCommunicationManager?, NoError> {
|
||||
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
|
||||
}
|
||||
}
|
884
submodules/WatchBridge/Sources/WatchRequestHandlers.swift
Normal file
884
submodules/WatchBridge/Sources/WatchRequestHandlers.swift
Normal file
@ -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<Void, NoError> 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<Bool, NoError> 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<Bool, NoError> 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<PeerView, NoError> 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<PeerView, NoError> 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<TelegramMediaFile?, NoError> {
|
||||
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<UIImage?, NoError> in
|
||||
if let context = context {
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> mapToSignal { peer -> Signal<EnginePeer?, NoError> 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<UIImage?, NoError> 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<UIImage?, NoError> 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<UIImage?, NoError> 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<ItemCollectionsView, NoError> 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<Int64> = []
|
||||
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<String, NoError> 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<String, NoError> 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<String, NoError> 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<String, Any>, manager: WatchCommunicationManager) -> Signal<Void, NoError> {
|
||||
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<EnginePeer?, NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
|> take(1)
|
||||
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> 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<ChatContextResultCollection?, NoError> 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<PeerView, NoError> 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<Bool, NoError> in
|
||||
if let context = context {
|
||||
var signal: Signal<Void, NoError>?
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
19
submodules/WatchBridgeAudio/BUILD
Normal file
19
submodules/WatchBridgeAudio/BUILD
Normal file
@ -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",
|
||||
],
|
||||
)
|
27
submodules/WatchBridgeAudio/Impl/BUILD
Normal file
27
submodules/WatchBridgeAudio/Impl/BUILD
Normal file
@ -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",
|
||||
],
|
||||
)
|
@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeAudioDecoder : NSObject
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url outputUrl:(NSURL *)outputURL;
|
||||
- (void)startWithCompletion:(void (^)(void))completion;
|
||||
|
||||
@end
|
@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeAudioEncoder : NSObject
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url;
|
||||
- (void)startWithCompletion:(void (^)(NSString *, int32_t))completion;
|
||||
|
||||
@end
|
@ -0,0 +1,6 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <WatchBridgeAudioImpl/TGBridgeAudioEncoder.h>
|
||||
#import <WatchBridgeAudioImpl/TGBridgeAudioDecoder.h>
|
||||
|
||||
|
197
submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioDecoder.mm
Normal file
197
submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioDecoder.mm
Normal file
@ -0,0 +1,197 @@
|
||||
#import <WatchBridgeAudioImpl/TGBridgeAudioDecoder.h>
|
||||
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
|
||||
#import <OpusBinding/OpusBinding.h>
|
||||
|
||||
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
|
564
submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioEncoder.m
Normal file
564
submodules/WatchBridgeAudio/Impl/Sources/TGBridgeAudioEncoder.m
Normal file
@ -0,0 +1,564 @@
|
||||
#import <WatchBridgeAudioImpl/TGBridgeAudioEncoder.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import <OpusBinding/OpusBinding.h>
|
||||
|
||||
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
|
25
submodules/WatchBridgeAudio/Sources/LegacyBridgeAudio.swift
Normal file
25
submodules/WatchBridgeAudio/Sources/LegacyBridgeAudio.swift
Normal file
@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import WatchBridgeAudioImpl
|
||||
|
||||
public func legacyDecodeOpusAudio(path: String, outputPath: String) -> Signal<String, NoError> {
|
||||
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
|
||||
}
|
||||
}
|
0
submodules/WatchCommon/BUILD
Normal file
0
submodules/WatchCommon/BUILD
Normal file
25
submodules/WatchCommon/Host/BUILD
Normal file
25
submodules/WatchCommon/Host/BUILD
Normal file
@ -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",
|
||||
],
|
||||
)
|
@ -0,0 +1,36 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
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
|
@ -0,0 +1,16 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -0,0 +1,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeBotCommandInfo : NSObject <NSCoding>
|
||||
{
|
||||
NSString *_command;
|
||||
NSString *_commandDescription;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSString *command;
|
||||
@property (nonatomic, readonly) NSString *commandDescription;
|
||||
|
||||
@end
|
@ -0,0 +1,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeBotInfo : NSObject <NSCoding>
|
||||
{
|
||||
NSString *_shortDescription;
|
||||
NSArray *_commandList;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSString *shortDescription;
|
||||
@property (nonatomic, readonly) NSArray *commandList;
|
||||
|
||||
@end
|
@ -0,0 +1,46 @@
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
#import <WatchCommon/TGBridgeMessage.h>
|
||||
|
||||
@interface TGBridgeChat : NSObject <NSCoding>
|
||||
|
||||
@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<NSNumber *> *)involvedUserIds;
|
||||
- (NSArray<NSNumber *> *)participantsUserIds;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGBridgeChatKey;
|
||||
extern NSString *const TGBridgeChatsArrayKey;
|
@ -0,0 +1,14 @@
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
|
||||
@class SSignal;
|
||||
|
||||
@interface TGBridgeChatMessages : NSObject <NSCoding>
|
||||
{
|
||||
NSArray *_messages;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSArray *messages;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGBridgeChatMessageListViewKey;
|
@ -0,0 +1,95 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
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 <NSCoding>
|
||||
|
||||
@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 <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int64_t identifier;
|
||||
|
||||
- (instancetype)initWithIdentifier:(int64_t)identifier;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgeFile : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly, strong) NSData *data;
|
||||
@property (nonatomic, readonly, strong) NSDictionary *metadata;
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgePing : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int32_t sessionId;
|
||||
|
||||
- (instancetype)initWithSessionId:(int32_t)sessionId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgeSubscriptionListRequest : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int32_t sessionId;
|
||||
|
||||
- (instancetype)initWithSessionId:(int32_t)sessionId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgeSubscriptionList : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly, strong) NSArray *subscriptions;
|
||||
|
||||
- (instancetype)initWithArray:(NSArray *)array;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
typedef NS_ENUM(int32_t, TGBridgeResponseType) {
|
||||
TGBridgeResponseTypeUndefined,
|
||||
TGBridgeResponseTypeNext,
|
||||
TGBridgeResponseTypeFailed,
|
||||
TGBridgeResponseTypeCompleted
|
||||
};
|
||||
|
||||
@interface TGBridgeResponse : NSObject <NSCoding>
|
||||
|
||||
@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
|
@ -0,0 +1,13 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -0,0 +1,18 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@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
|
@ -0,0 +1,23 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -0,0 +1,9 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@interface TGBridgeForwardedMessageMediaAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, assign) int64_t peerId;
|
||||
@property (nonatomic, assign) int32_t mid;
|
||||
@property (nonatomic, assign) int32_t date;
|
||||
|
||||
@end
|
@ -0,0 +1,10 @@
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@interface TGBridgeImageMediaAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, assign) int64_t imageId;
|
||||
@property (nonatomic, assign) CGSize dimensions;
|
||||
|
||||
@end
|
@ -0,0 +1,19 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@interface TGBridgeVenueAttachment : NSObject <NSCoding>
|
||||
|
||||
@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
|
@ -0,0 +1,15 @@
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
@class TGBridgeLocationMediaAttachment;
|
||||
|
||||
@interface TGBridgeLocationVenue : NSObject <NSCoding>
|
||||
|
||||
@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
|
@ -0,0 +1,11 @@
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
|
||||
@interface TGBridgeMediaAttachment : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) NSInteger mediaType;
|
||||
|
||||
+ (NSInteger)mediaType;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGBridgeMediaAttachmentTypeKey;
|
@ -0,0 +1,65 @@
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
#import <WatchCommon/TGBridgeImageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeVideoMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeAudioMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeDocumentMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeLocationMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeContactMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeActionMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeReplyMessageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeForwardedMessageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeWebPageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeMessageEntitiesAttachment.h>
|
||||
#import <WatchCommon/TGBridgeUnsupportedMediaAttachment.h>
|
||||
|
||||
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 <NSCoding>
|
||||
|
||||
@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<NSNumber *> *)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;
|
@ -0,0 +1,59 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeMessageEntity : NSObject <NSCoding>
|
||||
|
||||
@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
|
@ -0,0 +1,8 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeMessageEntities.h>
|
||||
|
||||
@interface TGBridgeMessageEntitiesAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, strong) NSArray *entities;
|
||||
|
||||
@end
|
@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgePeerNotificationSettings : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, assign) int32_t muteFor;
|
||||
|
||||
@end
|
@ -0,0 +1,9 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@class TGBridgeBotReplyMarkup;
|
||||
|
||||
@interface TGBridgeReplyMarkupMediaAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, strong) TGBridgeBotReplyMarkup *replyMarkup;
|
||||
|
||||
@end
|
@ -0,0 +1,10 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@class TGBridgeMessage;
|
||||
|
||||
@interface TGBridgeReplyMessageMediaAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, assign) int32_t mid;
|
||||
@property (nonatomic, strong) TGBridgeMessage *message;
|
||||
|
||||
@end
|
@ -0,0 +1,268 @@
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
@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
|
@ -0,0 +1,9 @@
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@interface TGBridgeUnsupportedMediaAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, strong) NSString *compactTitle;
|
||||
@property (nonatomic, strong) NSString *title;
|
||||
@property (nonatomic, strong) NSString *subtitle;
|
||||
|
||||
@end
|
@ -0,0 +1,59 @@
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
|
||||
@class TGBridgeBotInfo;
|
||||
@class TGBridgeUserChange;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TGBridgeUserKind) {
|
||||
TGBridgeUserKindGeneric,
|
||||
TGBridgeUserKindBot,
|
||||
TGBridgeUserKindSmartBot
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TGBridgeBotKind) {
|
||||
TGBridgeBotKindGeneric,
|
||||
TGBridgeBotKindPrivate
|
||||
};
|
||||
|
||||
@interface TGBridgeUser : NSObject <NSCoding, NSCopying>
|
||||
|
||||
@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 <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int32_t userIdentifier;
|
||||
@property (nonatomic, readonly) NSDictionary *fields;
|
||||
|
||||
- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGBridgeUsersDictionaryKey;
|
@ -0,0 +1,12 @@
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -0,0 +1,23 @@
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -0,0 +1,29 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <WatchCommon/TGBridgeActionMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeAudioMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeBotCommandInfo.h>
|
||||
#import <WatchCommon/TGBridgeBotInfo.h>
|
||||
#import <WatchCommon/TGBridgeChat.h>
|
||||
#import <WatchCommon/TGBridgeChatMessages.h>
|
||||
#import <WatchCommon/TGBridgeCommon.h>
|
||||
#import <WatchCommon/TGBridgeContactMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeContext.h>
|
||||
#import <WatchCommon/TGBridgeDocumentMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeForwardedMessageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeImageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeLocationMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeLocationVenue.h>
|
||||
#import <WatchCommon/TGBridgeMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeMessage.h>
|
||||
#import <WatchCommon/TGBridgeMessageEntities.h>
|
||||
#import <WatchCommon/TGBridgeMessageEntitiesAttachment.h>
|
||||
#import <WatchCommon/TGBridgeReplyMarkupMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeReplyMessageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeSubscriptions.h>
|
||||
#import <WatchCommon/TGBridgeUnsupportedMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeUser.h>
|
||||
#import <WatchCommon/TGBridgeVideoMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgeWebPageMediaAttachment.h>
|
||||
#import <WatchCommon/TGBridgePeerIdAdapter.h>
|
||||
#import <WatchCommon/TGBridgePeerNotificationSettings.h>
|
@ -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
|
@ -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
|
25
submodules/WatchCommon/Host/Sources/TGBridgeBotCommandInfo.m
Normal file
25
submodules/WatchCommon/Host/Sources/TGBridgeBotCommandInfo.m
Normal file
@ -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
|
25
submodules/WatchCommon/Host/Sources/TGBridgeBotInfo.m
Normal file
25
submodules/WatchCommon/Host/Sources/TGBridgeBotInfo.m
Normal file
@ -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
|
148
submodules/WatchCommon/Host/Sources/TGBridgeChat.m
Normal file
148
submodules/WatchCommon/Host/Sources/TGBridgeChat.m
Normal file
@ -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<NSNumber *> *)involvedUserIds
|
||||
{
|
||||
NSMutableSet<NSNumber *> *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<NSNumber *> *)participantsUserIds
|
||||
{
|
||||
NSMutableSet<NSNumber *> *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
|
27
submodules/WatchCommon/Host/Sources/TGBridgeChatMessages.m
Normal file
27
submodules/WatchCommon/Host/Sources/TGBridgeChatMessages.m
Normal file
@ -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
|
295
submodules/WatchCommon/Host/Sources/TGBridgeCommon.m
Normal file
295
submodules/WatchCommon/Host/Sources/TGBridgeCommon.m
Normal file
@ -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
|
@ -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
|
101
submodules/WatchCommon/Host/Sources/TGBridgeContext.m
Normal file
101
submodules/WatchCommon/Host/Sources/TGBridgeContext.m
Normal file
@ -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
|
@ -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
|
@ -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
|
@ -0,0 +1,33 @@
|
||||
#import "TGBridgeImageMediaAttachment.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
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
|
@ -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
|
66
submodules/WatchCommon/Host/Sources/TGBridgeLocationVenue.m
Normal file
66
submodules/WatchCommon/Host/Sources/TGBridgeLocationVenue.m
Normal file
@ -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
|
@ -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
|
242
submodules/WatchCommon/Host/Sources/TGBridgeMessage.m
Normal file
242
submodules/WatchCommon/Host/Sources/TGBridgeMessage.m
Normal file
@ -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<NSNumber *> *)involvedUserIds
|
||||
{
|
||||
NSMutableSet<NSNumber *> *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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
1048
submodules/WatchCommon/Host/Sources/TGBridgeSubscriptions.m
Normal file
1048
submodules/WatchCommon/Host/Sources/TGBridgeSubscriptions.m
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
286
submodules/WatchCommon/Host/Sources/TGBridgeUser.m
Normal file
286
submodules/WatchCommon/Host/Sources/TGBridgeUser.m
Normal file
@ -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
|
@ -0,0 +1,39 @@
|
||||
#import "TGBridgeVideoMediaAttachment.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
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
|
@ -0,0 +1,65 @@
|
||||
#import "TGBridgeWebPageMediaAttachment.h"
|
||||
#import "TGBridgeImageMediaAttachment.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
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
|
@ -0,0 +1,36 @@
|
||||
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
|
||||
|
||||
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
|
@ -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
|
@ -0,0 +1,16 @@
|
||||
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -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
|
@ -0,0 +1,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeBotCommandInfo : NSObject <NSCoding>
|
||||
{
|
||||
NSString *_command;
|
||||
NSString *_commandDescription;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSString *command;
|
||||
@property (nonatomic, readonly) NSString *commandDescription;
|
||||
|
||||
@end
|
@ -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
|
12
submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.h
Normal file
12
submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.h
Normal file
@ -0,0 +1,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TGBridgeBotInfo : NSObject <NSCoding>
|
||||
{
|
||||
NSString *_shortDescription;
|
||||
NSArray *_commandList;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSString *shortDescription;
|
||||
@property (nonatomic, readonly) NSArray *commandList;
|
||||
|
||||
@end
|
25
submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.m
Normal file
25
submodules/WatchCommon/Watch/Sources/TGBridgeBotInfo.m
Normal file
@ -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
|
46
submodules/WatchCommon/Watch/Sources/TGBridgeChat.h
Normal file
46
submodules/WatchCommon/Watch/Sources/TGBridgeChat.h
Normal file
@ -0,0 +1,46 @@
|
||||
#import <WatchCommonWatch/TGBridgeCommon.h>
|
||||
#import <WatchCommonWatch/TGBridgeMessage.h>
|
||||
|
||||
@interface TGBridgeChat : NSObject <NSCoding>
|
||||
|
||||
@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;
|
139
submodules/WatchCommon/Watch/Sources/TGBridgeChat.m
Normal file
139
submodules/WatchCommon/Watch/Sources/TGBridgeChat.m
Normal file
@ -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
|
14
submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.h
Normal file
14
submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.h
Normal file
@ -0,0 +1,14 @@
|
||||
#import <WatchCommonWatch/TGBridgeCommon.h>
|
||||
|
||||
@class SSignal;
|
||||
|
||||
@interface TGBridgeChatMessages : NSObject <NSCoding>
|
||||
{
|
||||
NSArray *_messages;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSArray *messages;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const TGBridgeChatMessageListViewKey;
|
27
submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.m
Normal file
27
submodules/WatchCommon/Watch/Sources/TGBridgeChatMessages.m
Normal file
@ -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
|
95
submodules/WatchCommon/Watch/Sources/TGBridgeCommon.h
Normal file
95
submodules/WatchCommon/Watch/Sources/TGBridgeCommon.h
Normal file
@ -0,0 +1,95 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
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 <NSCoding>
|
||||
|
||||
@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 <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int64_t identifier;
|
||||
|
||||
- (instancetype)initWithIdentifier:(int64_t)identifier;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgeFile : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly, strong) NSData *data;
|
||||
@property (nonatomic, readonly, strong) NSDictionary *metadata;
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgePing : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int32_t sessionId;
|
||||
|
||||
- (instancetype)initWithSessionId:(int32_t)sessionId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgeSubscriptionListRequest : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly) int32_t sessionId;
|
||||
|
||||
- (instancetype)initWithSessionId:(int32_t)sessionId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TGBridgeSubscriptionList : NSObject <NSCoding>
|
||||
|
||||
@property (nonatomic, readonly, strong) NSArray *subscriptions;
|
||||
|
||||
- (instancetype)initWithArray:(NSArray *)array;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
typedef NS_ENUM(int32_t, TGBridgeResponseType) {
|
||||
TGBridgeResponseTypeUndefined,
|
||||
TGBridgeResponseTypeNext,
|
||||
TGBridgeResponseTypeFailed,
|
||||
TGBridgeResponseTypeCompleted
|
||||
};
|
||||
|
||||
@interface TGBridgeResponse : NSObject <NSCoding>
|
||||
|
||||
@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
|
295
submodules/WatchCommon/Watch/Sources/TGBridgeCommon.m
Normal file
295
submodules/WatchCommon/Watch/Sources/TGBridgeCommon.m
Normal file
@ -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
|
@ -0,0 +1,13 @@
|
||||
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -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
|
18
submodules/WatchCommon/Watch/Sources/TGBridgeContext.h
Normal file
18
submodules/WatchCommon/Watch/Sources/TGBridgeContext.h
Normal file
@ -0,0 +1,18 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@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
|
101
submodules/WatchCommon/Watch/Sources/TGBridgeContext.m
Normal file
101
submodules/WatchCommon/Watch/Sources/TGBridgeContext.m
Normal file
@ -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
|
@ -0,0 +1,23 @@
|
||||
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
|
||||
|
||||
@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
|
@ -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
|
@ -0,0 +1,9 @@
|
||||
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
|
||||
|
||||
@interface TGBridgeForwardedMessageMediaAttachment : TGBridgeMediaAttachment
|
||||
|
||||
@property (nonatomic, assign) int64_t peerId;
|
||||
@property (nonatomic, assign) int32_t mid;
|
||||
@property (nonatomic, assign) int32_t date;
|
||||
|
||||
@end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user