346 lines
12 KiB
Objective-C
346 lines
12 KiB
Objective-C
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
#import "OCTSubmanagerFriendsImpl.h"
|
|
#import "OCTLogging.h"
|
|
#import "OCTTox.h"
|
|
#import "OCTFriend.h"
|
|
#import "OCTFriendRequest.h"
|
|
#import "OCTRealmManager.h"
|
|
//#import "Firebase.h"
|
|
|
|
@implementation OCTSubmanagerFriendsImpl
|
|
@synthesize dataSource = _dataSource;
|
|
|
|
#pragma mark - Public
|
|
|
|
- (BOOL)sendFriendRequestToAddress:(NSString *)address message:(NSString *)message error:(NSError **)error
|
|
{
|
|
NSParameterAssert(address);
|
|
NSParameterAssert(message);
|
|
|
|
OCTTox *tox = [self.dataSource managerGetTox];
|
|
|
|
OCTToxFriendNumber friendNumber = [tox addFriendWithAddress:address message:message error:error];
|
|
|
|
if (friendNumber == kOCTToxFriendNumberFailure) {
|
|
return NO;
|
|
}
|
|
|
|
[self.dataSource managerSaveTox];
|
|
|
|
return [self createFriendWithFriendNumber:friendNumber error:error];
|
|
}
|
|
|
|
- (BOOL)approveFriendRequest:(OCTFriendRequest *)friendRequest error:(NSError **)error
|
|
{
|
|
NSParameterAssert(friendRequest);
|
|
|
|
OCTTox *tox = [self.dataSource managerGetTox];
|
|
|
|
OCTToxFriendNumber friendNumber = [tox addFriendWithNoRequestWithPublicKey:friendRequest.publicKey error:error];
|
|
|
|
if (friendNumber == kOCTToxFriendNumberFailure) {
|
|
return NO;
|
|
}
|
|
|
|
[self.dataSource managerSaveTox];
|
|
|
|
[[self.dataSource managerGetRealmManager] deleteObject:friendRequest];
|
|
|
|
return [self createFriendWithFriendNumber:friendNumber error:error];
|
|
}
|
|
|
|
- (void)removeFriendRequest:(OCTFriendRequest *)friendRequest
|
|
{
|
|
NSParameterAssert(friendRequest);
|
|
|
|
[[self.dataSource managerGetRealmManager] deleteObject:friendRequest];
|
|
}
|
|
|
|
- (BOOL)removeFriend:(OCTFriend *)friend error:(NSError **)error
|
|
{
|
|
NSParameterAssert(friend);
|
|
|
|
OCTTox *tox = [self.dataSource managerGetTox];
|
|
|
|
if (! [tox deleteFriendWithFriendNumber:friend.friendNumber error:error]) {
|
|
return NO;
|
|
}
|
|
|
|
[self.dataSource managerSaveTox];
|
|
|
|
[[self.dataSource managerGetRealmManager] deleteObject:friend];
|
|
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark - Private category
|
|
|
|
- (void)configure
|
|
{
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
OCTTox *tox = [self.dataSource managerGetTox];
|
|
|
|
[realmManager updateObjectsWithClass:[OCTFriend class] predicate:nil updateBlock:^(OCTFriend *friend) {
|
|
// Tox may change friendNumber after relaunch, resetting them.
|
|
friend.friendNumber = kOCTToxFriendNumberFailure;
|
|
}];
|
|
|
|
for (NSNumber *friendNumber in [tox friendsArray]) {
|
|
OCTToxFriendNumber number = [friendNumber intValue];
|
|
NSError *error;
|
|
|
|
NSString *publicKey = [tox publicKeyFromFriendNumber:number error:&error];
|
|
|
|
if (! publicKey) {
|
|
@throw [NSException exceptionWithName:@"Cannot find publicKey for existing friendNumber, Tox save data is broken"
|
|
reason:error.debugDescription
|
|
userInfo:nil];
|
|
}
|
|
|
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"publicKey == %@", publicKey];
|
|
RLMResults *results = [realmManager objectsWithClass:[OCTFriend class] predicate:predicate];
|
|
|
|
if (results.count == 0) {
|
|
// It seems that friend is in Tox but isn't in Realm. Let's add it.
|
|
[self createFriendWithFriendNumber:number error:nil];
|
|
continue;
|
|
}
|
|
|
|
OCTFriend *friend = [results firstObject];
|
|
|
|
// Reset some fields for friends.
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.friendNumber = number;
|
|
theFriend.status = OCTToxUserStatusNone;
|
|
theFriend.isConnected = NO;
|
|
theFriend.connectionStatus = OCTToxConnectionStatusNone;
|
|
theFriend.isTyping = NO;
|
|
NSDate *dateOffline = [tox friendGetLastOnlineWithFriendNumber:number error:nil];
|
|
theFriend.lastSeenOnlineInterval = [dateOffline timeIntervalSince1970];
|
|
}];
|
|
}
|
|
|
|
// Remove all OCTFriend's which aren't bounded to tox. User cannot interact with them anyway.
|
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"friendNumber == %d", kOCTToxFriendNumberFailure];
|
|
RLMResults *results = [realmManager objectsWithClass:[OCTFriend class] predicate:predicate];
|
|
|
|
for (OCTFriend *friend in results) {
|
|
[realmManager deleteObject:friend];
|
|
}
|
|
}
|
|
|
|
#pragma mark - OCTToxDelegate
|
|
|
|
- (void)tox:(OCTTox *)tox friendRequestWithMessage:(NSString *)message publicKey:(NSString *)publicKey
|
|
{
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
|
|
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"publicKey == %@", publicKey];
|
|
RLMResults *results = [realmManager objectsWithClass:[OCTFriendRequest class] predicate:predicate];
|
|
if (results.count > 0) {
|
|
// friendRequest already exists
|
|
return;
|
|
}
|
|
|
|
results = [realmManager objectsWithClass:[OCTFriend class] predicate:predicate];
|
|
if (results.count > 0) {
|
|
// friend with such publicKey already exists
|
|
return;
|
|
}
|
|
|
|
OCTFriendRequest *request = [OCTFriendRequest new];
|
|
request.publicKey = publicKey;
|
|
request.message = message;
|
|
request.dateInterval = [[NSDate date] timeIntervalSince1970];
|
|
|
|
[realmManager addObject:request];
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendNameUpdate:(NSString *)name friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
[self.dataSource managerSaveTox];
|
|
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.name = name;
|
|
|
|
if (name.length && [theFriend.nickname isEqualToString:theFriend.publicKey]) {
|
|
theFriend.nickname = name;
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendPushTokenUpdate:(NSString *)pushToken friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.pushToken = pushToken;
|
|
}];
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendStatusMessageUpdate:(NSString *)statusMessage friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
[self.dataSource managerSaveTox];
|
|
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.statusMessage = statusMessage;
|
|
}];
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendStatusUpdate:(OCTToxUserStatus)status friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
[self.dataSource managerSaveTox];
|
|
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.status = status;
|
|
}];
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendIsTypingUpdate:(BOOL)isTyping friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.isTyping = isTyping;
|
|
}];
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendSetMsgv3Capability:(BOOL)msgv3Capability friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
//TODO(Tha_14): Remove this after removing msgv3 stuff
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
theFriend.msgv3Capability = 0;
|
|
}];
|
|
|
|
}
|
|
|
|
- (void)tox:(OCTTox *)tox friendConnectionStatusChanged:(OCTToxConnectionStatus)status friendNumber:(OCTToxFriendNumber)friendNumber
|
|
{
|
|
[self.dataSource managerSaveTox];
|
|
|
|
OCTRealmManager *realmManager = [self.dataSource managerGetRealmManager];
|
|
NSString *publicKey = [[self.dataSource managerGetTox] publicKeyFromFriendNumber:friendNumber error:nil];
|
|
OCTFriend *friend = [realmManager friendWithPublicKey:publicKey];
|
|
|
|
[realmManager updateObject:friend withBlock:^(OCTFriend *theFriend) {
|
|
|
|
if ((status != OCTToxConnectionStatusNone)
|
|
&& (theFriend.connectionStatus == OCTToxConnectionStatusNone))
|
|
{
|
|
// Friend is coming online now
|
|
|
|
// OCTToxCapabilities f_caps = [tox friendGetCapabilitiesWithFriendNumber:friendNumber];
|
|
// OCTLogVerbose(@"f_caps=%lu", f_caps);
|
|
// NSString* cap_string = [NSString stringWithFormat:@"%lu", f_caps];
|
|
// theFriend.capabilities2 = cap_string;
|
|
|
|
}
|
|
theFriend.isConnected = (status != OCTToxConnectionStatusNone);
|
|
theFriend.connectionStatus = status;
|
|
|
|
if (! theFriend.isConnected) {
|
|
// Friend is offline now
|
|
NSDate *dateOffline = [tox friendGetLastOnlineWithFriendNumber:friendNumber error:nil];
|
|
NSTimeInterval timeSince = [dateOffline timeIntervalSince1970];
|
|
theFriend.lastSeenOnlineInterval = timeSince;
|
|
}
|
|
}];
|
|
|
|
[[self.dataSource managerGetNotificationCenter] postNotificationName:kOCTFriendConnectionStatusChangeNotification object:friend];
|
|
}
|
|
|
|
#pragma mark - Private
|
|
|
|
- (BOOL)createFriendWithFriendNumber:(OCTToxFriendNumber)friendNumber error:(NSError **)userError
|
|
{
|
|
OCTTox *tox = [self.dataSource managerGetTox];
|
|
NSError *error;
|
|
|
|
OCTFriend *friend = [OCTFriend new];
|
|
|
|
friend.friendNumber = friendNumber;
|
|
|
|
friend.publicKey = [tox publicKeyFromFriendNumber:friendNumber error:&error];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
friend.name = [tox friendNameWithFriendNumber:friendNumber error:&error];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
friend.statusMessage = [tox friendStatusMessageWithFriendNumber:friendNumber error:&error];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
friend.status = [tox friendStatusWithFriendNumber:friendNumber error:&error];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
friend.connectionStatus = [tox friendConnectionStatusWithFriendNumber:friendNumber error:&error];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
NSDate *lastSeenOnline = [tox friendGetLastOnlineWithFriendNumber:friendNumber error:&error];
|
|
friend.lastSeenOnlineInterval = [lastSeenOnline timeIntervalSince1970];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
friend.isTyping = [tox isFriendTypingWithFriendNumber:friendNumber error:&error];
|
|
if ([self checkForError:error andAssignTo:userError]) {
|
|
return NO;
|
|
}
|
|
|
|
friend.isConnected = (friend.connectionStatus != OCTToxConnectionStatusNone);
|
|
friend.nickname = friend.name.length ? friend.name : friend.publicKey;
|
|
|
|
[[self.dataSource managerGetRealmManager] addObject:friend];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)checkForError:(NSError *)toCheck andAssignTo:(NSError **)toAssign
|
|
{
|
|
if (! toCheck) {
|
|
return NO;
|
|
}
|
|
|
|
if (toAssign) {
|
|
*toAssign = toCheck;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|