From 6cc1cc2702e4f011f67e825dbd8d3d7917ff4fb4 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Wed, 14 Jul 2021 18:17:38 +0800 Subject: [PATCH 1/4] feat: friendship --- AVOS/AVOS.xcodeproj/project.pbxproj | 8 ++ AVOS/LeanCloudObjc/Foundation.h | 1 + .../Sources/Foundation/Object/LCObjectUtils.h | 12 +- .../Sources/Foundation/Object/LCObjectUtils.m | 7 ++ AVOS/Sources/Foundation/User/LCFriendship.h | 66 ++++++++++ AVOS/Sources/Foundation/User/LCFriendship.m | 118 ++++++++++++++++++ LeanCloudObjc.podspec | 1 + 7 files changed, 207 insertions(+), 6 deletions(-) create mode 100644 AVOS/Sources/Foundation/User/LCFriendship.h create mode 100644 AVOS/Sources/Foundation/User/LCFriendship.m diff --git a/AVOS/AVOS.xcodeproj/project.pbxproj b/AVOS/AVOS.xcodeproj/project.pbxproj index 9595809a..b9664a64 100644 --- a/AVOS/AVOS.xcodeproj/project.pbxproj +++ b/AVOS/AVOS.xcodeproj/project.pbxproj @@ -305,6 +305,8 @@ D39724C424A5CD3C0099A518 /* RTMBaseTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39724C324A5CD3C0099A518 /* RTMBaseTestCase.swift */; }; D39724C624A852400099A518 /* IMClientTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39724C524A852400099A518 /* IMClientTestCase.swift */; }; D3A397F124A5A4670087D6F8 /* RTMConnectionTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A397F024A5A4670087D6F8 /* RTMConnectionTestCase.swift */; }; + D3A3FA39269ECD40002531C7 /* LCFriendship.h in Headers */ = {isa = PBXBuildFile; fileRef = D3A3FA37269ECD40002531C7 /* LCFriendship.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D3A3FA3A269ECD40002531C7 /* LCFriendship.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A3FA38269ECD40002531C7 /* LCFriendship.m */; }; D3AD74AB24BC216200D1BBEE /* LCUserTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3AD74AA24BC216200D1BBEE /* LCUserTestCase.swift */; }; /* End PBXBuildFile section */ @@ -570,6 +572,8 @@ D39724C324A5CD3C0099A518 /* RTMBaseTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTMBaseTestCase.swift; sourceTree = ""; }; D39724C524A852400099A518 /* IMClientTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMClientTestCase.swift; sourceTree = ""; }; D3A397F024A5A4670087D6F8 /* RTMConnectionTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTMConnectionTestCase.swift; sourceTree = ""; }; + D3A3FA37269ECD40002531C7 /* LCFriendship.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LCFriendship.h; sourceTree = ""; }; + D3A3FA38269ECD40002531C7 /* LCFriendship.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LCFriendship.m; sourceTree = ""; }; D3AD74AA24BC216200D1BBEE /* LCUserTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LCUserTestCase.swift; sourceTree = ""; }; D3C53FCB2106D84A00D48686 /* LCIMClientProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LCIMClientProtocol.h; sourceTree = ""; }; D3D6E43923544F520048E58F /* LCGPBExtensionRegistry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCGPBExtensionRegistry.m; sourceTree = ""; }; @@ -1016,6 +1020,8 @@ 8C841A9E1A5A7A0000C5C6C4 /* LCUser.h */, 8C841AA01A5A7A0000C5C6C4 /* LCUser_Internal.h */, 8C841A9F1A5A7A0000C5C6C4 /* LCUser.m */, + D3A3FA37269ECD40002531C7 /* LCFriendship.h */, + D3A3FA38269ECD40002531C7 /* LCFriendship.m */, ); path = User; sourceTree = ""; @@ -1391,6 +1397,7 @@ D30B6AB424A09E35006ABE09 /* LCDynamicObject_Internal.h in Headers */, D30B6ACA24A09ED1006ABE09 /* LCIMConversationMemberInfo_Internal.h in Headers */, D30B6A6B24A09D62006ABE09 /* LCQuery.h in Headers */, + D3A3FA39269ECD40002531C7 /* LCFriendship.h in Headers */, D30B6B4024A09F84006ABE09 /* LCIMTypedMessage.h in Headers */, D30B6A7C24A09DBE006ABE09 /* LCStatus.h in Headers */, D30B6AE124A09F1E006ABE09 /* LCGPBCodedInputStream_PackagePrivate.h in Headers */, @@ -1687,6 +1694,7 @@ D30B6AF024A09F1E006ABE09 /* LCGPBEmpty.pbobjc.m in Sources */, D30B6A8B24A09DCA006ABE09 /* LCURLSessionManager.m in Sources */, D30B6AAC24A09E35006ABE09 /* LCKeyValueStore.m in Sources */, + D3A3FA3A269ECD40002531C7 /* LCFriendship.m in Sources */, D30B6B3F24A09F79006ABE09 /* LCIMConversationQueryCacheStore.m in Sources */, D30B6B0D24A09F1F006ABE09 /* LCGPBUnknownFieldSet.m in Sources */, D30B6B5224A09FAD006ABE09 /* LCIMBlockHelper.m in Sources */, diff --git a/AVOS/LeanCloudObjc/Foundation.h b/AVOS/LeanCloudObjc/Foundation.h index beea66c6..cfe333cb 100644 --- a/AVOS/LeanCloudObjc/Foundation.h +++ b/AVOS/LeanCloudObjc/Foundation.h @@ -42,6 +42,7 @@ // User #import "LCUser.h" +#import "LCFriendship.h" // CloudCode #import "LCCloud.h" diff --git a/AVOS/Sources/Foundation/Object/LCObjectUtils.h b/AVOS/Sources/Foundation/Object/LCObjectUtils.h index 32b2508f..59a5d346 100644 --- a/AVOS/Sources/Foundation/Object/LCObjectUtils.h +++ b/AVOS/Sources/Foundation/Object/LCObjectUtils.h @@ -72,12 +72,12 @@ #pragma mark - batch request from operation list -+(BOOL)isUserClass:(NSString *)className; -+(BOOL)isRoleClass:(NSString *)className; -+(BOOL)isFileClass:(NSString *)className; -+(BOOL)isInstallationClass:(NSString *)className; -+(NSString *)objectPath:(NSString *)className - objectId:(NSString *)objectId; ++ (BOOL)isUserClass:(NSString *)className; ++ (BOOL)isRoleClass:(NSString *)className; ++ (BOOL)isFileClass:(NSString *)className; ++ (BOOL)isInstallationClass:(NSString *)className; ++ (BOOL)isFriendshipRequestClass:(NSString *)className; ++ (NSString *)objectPath:(NSString *)className objectId:(NSString *)objectId; #pragma mark - Array utils +(BOOL)safeAdd:(NSDictionary *)dict diff --git a/AVOS/Sources/Foundation/Object/LCObjectUtils.m b/AVOS/Sources/Foundation/Object/LCObjectUtils.m index e47a393a..91486487 100644 --- a/AVOS/Sources/Foundation/Object/LCObjectUtils.m +++ b/AVOS/Sources/Foundation/Object/LCObjectUtils.m @@ -21,6 +21,7 @@ #import "LCGeoPoint_Internal.h" #import "LCRelation_Internal.h" #import "LCUtils.h" +#import "LCFriendship.h" @implementation LCDate @@ -570,6 +571,8 @@ +(LCObject *)lcObjectForClass:(NSString *)className { } else if ([LCObjectUtils isRoleClass:className]) { // TODO object = [LCRole role]; + } else if ([LCObjectUtils isFriendshipRequestClass:className]) { + object = [[LCFriendshipRequest alloc] init]; } else { object = [LCObject objectWithClassName:className]; } @@ -705,6 +708,10 @@ +(BOOL)isInstallationClass:(NSString *)className return [className isEqualToString:[LCInstallation className]]; } ++ (BOOL)isFriendshipRequestClass:(NSString *)className { + return [className isEqualToString:[LCFriendshipRequest className]]; +} + +(NSString *)classEndPoint:(NSString *)className objectId:(NSString *)objectId { diff --git a/AVOS/Sources/Foundation/User/LCFriendship.h b/AVOS/Sources/Foundation/User/LCFriendship.h new file mode 100644 index 00000000..3f9521b8 --- /dev/null +++ b/AVOS/Sources/Foundation/User/LCFriendship.h @@ -0,0 +1,66 @@ +// +// LCFriendship.h +// LeanCloudObjc +// +// Created by pzheng on 2021/07/14. +// Copyright © 2021 LeanCloud Inc. All rights reserved. +// + +#import +#import "LCObject.h" + +@class LCQuery; + +NS_ASSUME_NONNULL_BEGIN + +/// The request of becoming friends. +@interface LCFriendshipRequest : LCObject + +/// The name of this class. ++ (NSString *)className; + +/// New a query for this class. ++ (LCQuery *)query; + +@end + +/// Friendship. +@interface LCFriendship : NSObject + +/// The request for becoming friends. +/// @param userId The ID of the target user. +/// @param callback Result callback. ++ (void)requestWithUserId:(NSString *)userId + callback:(void (^)(BOOL succeeded, NSError * _Nullable error))callback; + +/// The request for becoming friends. +/// @param userId The ID of the target user. +/// @param attributes Custom key-value attributes. +/// @param callback Result callback. ++ (void)requestWithUserId:(NSString *)userId + attributes:(NSDictionary * _Nullable)attributes + callback:(void (^)(BOOL succeeded, NSError * _Nullable error))callback; + +/// Accept a friendship request. +/// @param request See `LCFriendshipRequest`. +/// @param callback Result callback. ++ (void)acceptRequest:(LCFriendshipRequest *)request + callback:(void (^)(BOOL succeeded, NSError * _Nullable error))callback; + +/// Accept a friendship request. +/// @param request See `LCFriendshipRequest`. +/// @param attributes Custom key-value attributes. +/// @param callback Result callback. ++ (void)acceptRequest:(LCFriendshipRequest *)request + attributes:(NSDictionary * _Nullable)attributes + callback:(void (^)(BOOL succeeded, NSError * _Nullable error))callback; + +/// Decline a friendship request. +/// @param request See `LCFriendshipRequest`. +/// @param callback Result callback. ++ (void)declineRequest:(LCFriendshipRequest *)request + callback:(void (^)(BOOL succeeded, NSError * _Nullable error))callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AVOS/Sources/Foundation/User/LCFriendship.m b/AVOS/Sources/Foundation/User/LCFriendship.m new file mode 100644 index 00000000..1e5425e3 --- /dev/null +++ b/AVOS/Sources/Foundation/User/LCFriendship.m @@ -0,0 +1,118 @@ +// +// LCFriendship.m +// LeanCloudObjc +// +// Created by pzheng on 2021/07/14. +// Copyright © 2021 LeanCloud Inc. All rights reserved. +// + +#import "LCFriendship.h" +#import "LCUser_Internal.h" +#import "LCErrorUtils.h" +#import "LCPaasClient_internal.h" +#import "LCObjectUtils.h" +#import "LCQuery.h" + +@implementation LCFriendshipRequest + ++ (NSString *)className { + return @"_FriendshipRequest"; +} + ++ (LCQuery *)query { + LCQuery *query = [LCQuery queryWithClassName:[self className]]; + [query whereKey:@"friend" equalTo:[LCUser currentUser]]; + [query whereKey:@"status" equalTo:@"pending"]; + return query; +} + +@end + +@implementation LCFriendship + ++ (void)requestWithUserId:(NSString *)userId callback:(void (^)(BOOL, NSError * _Nullable))callback { + [self requestWithUserId:userId attributes:nil callback:callback]; +} + ++ (void)requestWithUserId:(NSString *)userId attributes:(NSDictionary *)attributes callback:(void (^)(BOOL, NSError * _Nullable))callback { + if (!userId) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"Parameter `userId` invalid.", nil); + callback(false, error); + return; + } + LCUser *currentUser = [LCUser currentUser]; + if (!currentUser.sessionToken) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"Please signin an user.", nil); + callback(false, error); + return; + } + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + parameters[@"user"] = [LCObjectUtils dictionaryFromObjectPointer:currentUser]; + parameters[@"friend"] = [LCObjectUtils dictionaryFromObjectPointer:[LCUser objectWithObjectId:userId]]; + if (attributes) { + parameters[@"friendship"] = attributes; + } + [[LCPaasClient sharedInstance] postObject:@"users/friendshipRequests" + withParameters:parameters + block:^(id _Nullable object, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(!error, error); + }); + }]; +} + ++ (void)acceptRequest:(LCFriendshipRequest *)request callback:(void (^)(BOOL, NSError * _Nullable))callback { + [self acceptRequest:request attributes:nil callback:callback]; +} + ++ (void)acceptRequest:(LCFriendshipRequest *)request attributes:(NSDictionary *)attributes callback:(void (^)(BOOL, NSError * _Nullable))callback { + if (!request.objectId) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"Parameter `request` invalid.", nil); + callback(false, error); + return; + } + LCUser *currentUser = [LCUser currentUser]; + if (!currentUser.sessionToken) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"Please signin an user.", nil); + callback(false, error); + return; + } + NSString *path = [NSString stringWithFormat:@"users/friendshipRequests/%@/accept", request.objectId]; + NSDictionary *parameters; + if (attributes) { + parameters = @{ @"friendship" : attributes }; + } + [[LCPaasClient sharedInstance] putObject:path + withParameters:parameters + sessionToken:currentUser.sessionToken + block:^(id _Nullable object, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(!error, error); + }); + }]; +} + ++ (void)declineRequest:(LCFriendshipRequest *)request callback:(void (^)(BOOL, NSError * _Nullable))callback { + if (!request.objectId) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"Parameter `request` invalid.", nil); + callback(false, error); + return; + } + LCUser *currentUser = [LCUser currentUser]; + if (!currentUser.sessionToken) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"Please signin an user.", nil); + callback(false, error); + return; + } + NSString *path = [NSString stringWithFormat:@"users/friendshipRequests/%@/decline", request.objectId]; + [[LCPaasClient sharedInstance] putObject:path + withParameters:nil + sessionToken:currentUser.sessionToken + block:^(id _Nullable object, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(!error, error); + }); + }]; +} + +@end diff --git a/LeanCloudObjc.podspec b/LeanCloudObjc.podspec index 20ed3de8..637a6731 100644 --- a/LeanCloudObjc.podspec +++ b/LeanCloudObjc.podspec @@ -51,6 +51,7 @@ Pod::Spec.new do |s| 'AVOS/Sources/Foundation/Search/LCSearchSortBuilder.h', 'AVOS/Sources/Foundation/Status/LCStatus.h', 'AVOS/Sources/Foundation/User/LCUser.h', + 'AVOS/Sources/Foundation/User/LCFriendship.h', 'AVOS/Sources/Foundation/Utils/LCLogger.h', 'AVOS/Sources/Foundation/LCAvailability.h' From 9d5ebb0cfc709a321d24332029d8a5b1153101f4 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Thu, 15 Jul 2021 19:23:58 +0800 Subject: [PATCH 2/4] test: add case --- AVOS/LeanCloudObjcTests/LCUserTestCase.swift | 194 +++++++++++++++++++ AVOS/Sources/Foundation/Status/LCStatus.h | 90 --------- AVOS/Sources/Foundation/User/LCFriendship.m | 1 + AVOS/Sources/Foundation/User/LCUser.h | 93 +++++++++ AVOS/Sources/Foundation/User/LCUser.m | 79 ++++---- 5 files changed, 328 insertions(+), 129 deletions(-) diff --git a/AVOS/LeanCloudObjcTests/LCUserTestCase.swift b/AVOS/LeanCloudObjcTests/LCUserTestCase.swift index 36d7e415..484c03a1 100644 --- a/AVOS/LeanCloudObjcTests/LCUserTestCase.swift +++ b/AVOS/LeanCloudObjcTests/LCUserTestCase.swift @@ -109,4 +109,198 @@ class LCUserTestCase: BaseTestCase { LCUser.logOut() */ } + + func testFriendshipRequestAccept() { + let openid_1 = uuid + let openid_2 = uuid + + let user_1 = LCUser() + expecting { exp in + user_1.login(withAuthData: ["openid": openid_1], platformId: "test", options: nil) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + + guard let _ = user_1.objectId, + LCUser.current() === user_1 else { + XCTFail() + return + } + LCUser.logOut() + + let user_2 = LCUser() + expecting { exp in + user_2.login(withAuthData: ["openid": openid_2], platformId: "test", options: nil) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + + guard let _ = user_2.objectId, + LCUser.current() === user_2 else { + XCTFail() + return + } + + expecting { exp in + LCFriendship.request(withUserId: user_1.objectId!, attributes: ["group": "sport"]) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + + LCUser.logOut() + + var query: LCQuery! + expecting(description: "Accept Friendship Request", count: 3) { exp in + user_1.login(withAuthData: ["openid": openid_1], platformId: "test", options: nil) { succeeded, error in + XCTAssertNil(error) + exp.fulfill() + if succeeded { + query = LCFriendshipRequest.query() + query.findObjectsInBackground { requests, error in + let request = requests?.first as? LCFriendshipRequest + let friend = request?["friend"] as? LCUser + let user = request?["user"] as? LCUser + XCTAssertNotNil(request) + XCTAssertNotNil(friend) + XCTAssertNotNil(user) + XCTAssertNil(error) + exp.fulfill() + if let request = request { + LCFriendship.accept(request, attributes: ["group": "music"]) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + } + } + } + } + + expecting { exp in + user_1.getFolloweeObjects { followees, error in + let followee = followees?.first as? LCObject + XCTAssertNotNil(followee) + XCTAssertEqual(followee?["group"] as? String, "music") + XCTAssertNil(error) + exp.fulfill() + } + } + + LCUser.logOut() + + expecting(count: 4) { exp in + user_2.login(withAuthData: ["openid": openid_2], platformId: "test", options: nil) { succeeded, error in + XCTAssertNil(error) + exp.fulfill() + if succeeded { + user_2.getFolloweeObjects { followees, error in + let followee = followees?.first as? LCObject + XCTAssertNotNil(followee) + XCTAssertEqual(followee?["group"] as? String, "sport") + XCTAssertNil(error) + exp.fulfill() + if let followee = followee { + followee["group"] = "music" + followee.saveInBackground { succeeded, error in + XCTAssertNil(error) + exp.fulfill() + if succeeded { + user_2.unfollow(user_1.objectId!) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + } + } + } + } + } + } + + LCUser.logOut() + } + + func testFriendshipRequestDecline() { + let openid_1 = uuid + let openid_2 = uuid + + let user_1 = LCUser() + expecting { exp in + user_1.login(withAuthData: ["openid": openid_1], platformId: "test", options: nil) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + + guard let _ = user_1.objectId, + LCUser.current() === user_1 else { + XCTFail() + return + } + LCUser.logOut() + + let user_2 = LCUser() + expecting { exp in + user_2.login(withAuthData: ["openid": openid_2], platformId: "test", options: nil) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + + guard let _ = user_2.objectId, + LCUser.current() === user_2 else { + XCTFail() + return + } + + expecting { exp in + LCFriendship.request(withUserId: user_1.objectId!, attributes: ["group": "sport"]) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + + LCUser.logOut() + + var query: LCQuery! + expecting(description: "Decline Friendship Request", count: 3) { exp in + user_1.login(withAuthData: ["openid": openid_1], platformId: "test", options: nil) { succeeded, error in + XCTAssertNil(error) + exp.fulfill() + if succeeded { + query = LCFriendshipRequest.query() + query.findObjectsInBackground { requests, error in + let request = requests?.first as? LCFriendshipRequest + let friend = request?["friend"] as? LCUser + let user = request?["user"] as? LCUser + XCTAssertNotNil(request) + XCTAssertNotNil(friend) + XCTAssertNotNil(user) + XCTAssertNil(error) + exp.fulfill() + if let request = request { + LCFriendship.declineRequest(request) { succeeded, error in + XCTAssertTrue(succeeded) + XCTAssertNil(error) + exp.fulfill() + } + } + } + } + } + } + + LCUser.logOut() + } } diff --git a/AVOS/Sources/Foundation/Status/LCStatus.h b/AVOS/Sources/Foundation/Status/LCStatus.h index 35970d9c..e74ed741 100644 --- a/AVOS/Sources/Foundation/Status/LCStatus.h +++ b/AVOS/Sources/Foundation/Status/LCStatus.h @@ -164,96 +164,6 @@ typedef void (^LCStatusResultBlock)(LCStatus * _Nullable status, NSError * _Null -(void)sendInBackgroundWithBlock:(LCBooleanResultBlock)block; @end -/** - * 用户好友关系 - */ -@interface LCUser(Friendship) - -/* @name 好友关系 */ - -/** - * 获取用户粉丝LCQuery - * - * @param userObjectId 用户ID - * - * @return 用于查询的LCQuery - */ -+(LCQuery*)followerQuery:(NSString*)userObjectId; - -/** - * 获取本用户粉丝LCQuery - * - * @return 用于查询的LCQuery - */ --(LCQuery*)followerQuery; - -/** - * 获取用户关注LCQuery - * - * @param userObjectId 用户ID - * - * @return 用于查询的LCQuery - */ -+(LCQuery*)followeeQuery:(NSString*)userObjectId; - -/** - * 获取本用户关注LCQuery - * - * @return 用于查询的LCQuery - */ --(LCQuery*)followeeQuery; - -/** - * 通过ID来关注其他用户 - * @warning 如果需要被关注者收到消息 需要手动给他发送一条LCStatus. - * @param userId 要关注的用户objectId - * @param callback 回调结果 - */ --(void)follow:(NSString*)userId andCallback:(LCBooleanResultBlock)callback; - -/** - * 通过ID来关注其他用户 - * @warning 如果需要被关注者收到消息 需要手动给他发送一条LCStatus. - * @param userId 要关注的用户objectId - * @param dictionary 添加的自定义属性 - * @param callback 回调结果 - */ --(void)follow:(NSString*)userId userDictionary:(nullable NSDictionary *)dictionary andCallback:(LCBooleanResultBlock)callback; - -/** - * 通过ID来取消关注其他用户 - * - * @param userId 要取消关注的用户objectId - * @param callback 回调结果 - * - */ --(void)unfollow:(NSString*)userId andCallback:(LCBooleanResultBlock)callback; - -/** - * 获取当前用户粉丝的列表 - * - * @param callback 回调结果 - */ --(void)getFollowers:(LCArrayResultBlock)callback; - -/** - * 获取当前用户所关注的列表 - * - * @param callback 回调结果 - * - */ --(void)getFollowees:(LCArrayResultBlock)callback; - -/** - * 同时获取当前用户的粉丝和关注列表 - * - * @param callback 回调结果, 列表字典包含`followers`数组和`followees`数组 - */ --(void)getFollowersAndFollowees:(LCDictionaryResultBlock)callback; - - -@end - /** * 查询LCStatus */ diff --git a/AVOS/Sources/Foundation/User/LCFriendship.m b/AVOS/Sources/Foundation/User/LCFriendship.m index 1e5425e3..c27cbe2f 100644 --- a/AVOS/Sources/Foundation/User/LCFriendship.m +++ b/AVOS/Sources/Foundation/User/LCFriendship.m @@ -23,6 +23,7 @@ + (LCQuery *)query { LCQuery *query = [LCQuery queryWithClassName:[self className]]; [query whereKey:@"friend" equalTo:[LCUser currentUser]]; [query whereKey:@"status" equalTo:@"pending"]; + [query includeKey:@"user"]; return query; } diff --git a/AVOS/Sources/Foundation/User/LCUser.h b/AVOS/Sources/Foundation/User/LCUser.h index b07505f7..fcd24662 100644 --- a/AVOS/Sources/Foundation/User/LCUser.h +++ b/AVOS/Sources/Foundation/User/LCUser.h @@ -501,4 +501,97 @@ FOUNDATION_EXPORT LeanCloudSocialPlatform const LeanCloudSocialPlatformWeiXin; @end +/** + * 用户好友关系 + */ +@interface LCUser (Friendship) + +/* @name 好友关系 */ + +/** + * 获取用户粉丝LCQuery + * + * @param userObjectId 用户ID + * + * @return 用于查询的LCQuery + */ ++ (LCQuery *)followerQuery:(NSString *)userObjectId; + +/** + * 获取本用户粉丝LCQuery + * + * @return 用于查询的LCQuery + */ +- (LCQuery *)followerQuery; + +/** + * 获取用户关注LCQuery + * + * @param userObjectId 用户ID + * + * @return 用于查询的LCQuery + */ ++ (LCQuery *)followeeQuery:(NSString *)userObjectId; + +/** + * 获取本用户关注LCQuery + * + * @return 用于查询的LCQuery + */ +- (LCQuery *)followeeQuery; + +/** + * 通过ID来关注其他用户 + * @warning 如果需要被关注者收到消息 需要手动给他发送一条LCStatus. + * @param userId 要关注的用户objectId + * @param callback 回调结果 + */ +- (void)follow:(NSString *)userId andCallback:(LCBooleanResultBlock)callback; + +/** + * 通过ID来关注其他用户 + * @warning 如果需要被关注者收到消息 需要手动给他发送一条LCStatus. + * @param userId 要关注的用户objectId + * @param dictionary 添加的自定义属性 + * @param callback 回调结果 + */ +- (void)follow:(NSString *)userId userDictionary:(nullable NSDictionary *)dictionary andCallback:(LCBooleanResultBlock)callback; + +/** + * 通过ID来取消关注其他用户 + * + * @param userId 要取消关注的用户objectId + * @param callback 回调结果 + * + */ +- (void)unfollow:(NSString *)userId andCallback:(LCBooleanResultBlock)callback; + +/** + * 获取当前用户粉丝的列表 + * + * @param callback 回调结果 + */ +- (void)getFollowers:(LCArrayResultBlock)callback; + +/** + * 获取当前用户所关注的列表 + * + * @param callback 回调结果 + * + */ +- (void)getFollowees:(LCArrayResultBlock)callback; + +/// Get followee objects. +/// @param callback Result callback. +- (void)getFolloweeObjectsWithCallback:(void (^)(NSArray * _Nullable objects, NSError * _Nullable error))callback; + +/** + * 同时获取当前用户的粉丝和关注列表 + * + * @param callback 回调结果, 列表字典包含`followers`数组和`followees`数组 + */ +- (void)getFollowersAndFollowees:(LCDictionaryResultBlock)callback; + +@end + NS_ASSUME_NONNULL_END diff --git a/AVOS/Sources/Foundation/User/LCUser.m b/AVOS/Sources/Foundation/User/LCUser.m index fd19ce68..739ff2a9 100644 --- a/AVOS/Sources/Foundation/User/LCUser.m +++ b/AVOS/Sources/Foundation/User/LCUser.m @@ -1277,12 +1277,12 @@ - (NSArray *)linkedServiceNames { @implementation LCUser (Friendship) -+(LCQuery*)followerQuery:(NSString*)userObjectId{ - LCFriendQuery *query=[LCFriendQuery queryWithClassName:@"_Follower"]; - query.targetFeild=@"follower"; ++ (LCQuery *)followerQuery:(NSString *)userObjectId { + LCFriendQuery *query = [LCFriendQuery queryWithClassName:@"_Follower"]; + query.targetFeild = @"follower"; - LCUser *user=[self user]; - user.objectId=userObjectId; + LCUser *user = [self user]; + user.objectId = userObjectId; [query whereKey:@"user" equalTo:user]; [query includeKey:@"follower"]; @@ -1291,12 +1291,12 @@ +(LCQuery*)followerQuery:(NSString*)userObjectId{ return query; } -+(LCQuery*)followeeQuery:(NSString*)userObjectId{ - LCFriendQuery *query=[LCFriendQuery queryWithClassName:@"_Followee"]; - query.targetFeild=@"followee"; ++ (LCQuery *)followeeQuery:(NSString *)userObjectId { + LCFriendQuery *query = [LCFriendQuery queryWithClassName:@"_Followee"]; + query.targetFeild = @"followee"; - LCUser *user=[self user]; - user.objectId=userObjectId; + LCUser *user = [self user]; + user.objectId = userObjectId; [query whereKey:@"user" equalTo:user]; [query includeKey:@"followee"]; @@ -1305,77 +1305,78 @@ +(LCQuery*)followeeQuery:(NSString*)userObjectId{ return query; } --(LCQuery*)followeeQuery{ +- (LCQuery *)followeeQuery { return [LCUser followeeQuery:self.objectId]; } --(LCQuery*)followerQuery{ +- (LCQuery *)followerQuery { return [LCUser followerQuery:self.objectId]; } --(void)follow:(NSString*)userId andCallback:(LCBooleanResultBlock)callback{ +- (void)follow:(NSString *)userId andCallback:(LCBooleanResultBlock)callback { [self follow:userId userDictionary:nil andCallback:callback]; } --(void)follow:(NSString*)userId userDictionary:(NSDictionary *)dictionary andCallback:(LCBooleanResultBlock)callback{ +- (void)follow:(NSString *)userId userDictionary:(NSDictionary *)dictionary andCallback:(LCBooleanResultBlock)callback { if (![self isAuthDataExistInMemory]) { callback(NO, LCError(kLCErrorUserCannotBeAlteredWithoutSession, nil, nil)); return; } NSDictionary *dict = [LCObjectUtils dictionaryFromObject:dictionary]; - NSString *path=[NSString stringWithFormat:@"users/self/friendship/%@",userId]; + NSString *path = [NSString stringWithFormat:@"users/self/friendship/%@", userId]; - [[LCPaasClient sharedInstance] postObject:path withParameters:dict block:^(NSDictionary *object, NSError *error) { + [[LCPaasClient sharedInstance] postObject:path withParameters:dict block:^(id object, NSError *error) { [LCUtils callBooleanResultBlock:callback error:error]; }]; } --(void)unfollow:(NSString *)userId andCallback:(LCBooleanResultBlock)callback{ +- (void)unfollow:(NSString *)userId andCallback:(LCBooleanResultBlock)callback { if (![self isAuthDataExistInMemory]) { callback(NO, LCError(kLCErrorUserCannotBeAlteredWithoutSession, nil, nil)); return; } - NSString *path=[NSString stringWithFormat:@"users/self/friendship/%@",userId]; + NSString *path = [NSString stringWithFormat:@"users/self/friendship/%@", userId]; - [[LCPaasClient sharedInstance] deleteObject:path withParameters:nil block:^(NSDictionary *object, NSError *error) { + [[LCPaasClient sharedInstance] deleteObject:path withParameters:nil block:^(id object, NSError *error) { [LCUtils callBooleanResultBlock:callback error:error]; }]; } --(void)getFollowers:(LCArrayResultBlock)callback{ - - LCQuery *query= [LCUser followerQuery:self.objectId]; +- (void)getFollowers:(LCArrayResultBlock)callback { + LCQuery *query = [LCUser followerQuery:self.objectId]; [query findObjectsInBackgroundWithBlock:callback]; - } --(void)getFollowees:(LCArrayResultBlock)callback{ - - LCQuery *query= [LCUser followeeQuery:self.objectId]; - +- (void)getFollowees:(LCArrayResultBlock)callback { + LCQuery *query = [LCUser followeeQuery:self.objectId]; [query findObjectsInBackgroundWithBlock:callback]; - } --(void)getFollowersAndFollowees:(LCDictionaryResultBlock)callback{ - NSString *path=[NSString stringWithFormat:@"users/%@/followersAndFollowees?include=follower,followee",self.objectId]; +- (void)getFolloweeObjectsWithCallback:(void (^)(NSArray * _Nullable, NSError * _Nullable))callback { + LCQuery *query = [LCQuery queryWithClassName:@"_Followee"]; + [query whereKey:@"user" equalTo:self]; + [query includeKey:@"followee"]; + [query findObjectsInBackgroundWithBlock:callback]; +} + +- (void)getFollowersAndFollowees:(LCDictionaryResultBlock)callback { + NSString *path = [NSString stringWithFormat:@"users/%@/followersAndFollowees?include=follower,followee", self.objectId]; [[LCPaasClient sharedInstance] getObject:path withParameters:nil block:^(NSDictionary *object, NSError *error) { - if (error==nil) { - NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithCapacity:2]; + if (!error) { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:2]; @try { - NSArray *orig=nil; - NSArray *result=nil; + NSArray *orig; + NSArray *result; - orig=[object[@"followees"] valueForKeyPath:@"followee"]; - result=[LCObjectUtils arrayFromArray:orig]; + orig = [object[@"followees"] valueForKeyPath:@"followee"]; + result = [LCObjectUtils arrayFromArray:orig]; [dict setObject:result forKey:@"followees"]; - orig=[object[@"followers"] valueForKeyPath:@"follower"]; - result=[LCObjectUtils arrayFromArray:orig]; + orig = [object[@"followers"] valueForKeyPath:@"follower"]; + result = [LCObjectUtils arrayFromArray:orig]; [dict setObject:result forKey:@"followers"]; - } @catch (NSException *exception) { error = LCErrorInternal(@"wrong format return"); From c4b4e7b0f9b53ad7743bcd3965453f8de3bad5d6 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 16 Jul 2021 11:41:06 +0800 Subject: [PATCH 3/4] chore: tweak --- AVOS/LeanCloudObjcTests/LCUserTestCase.swift | 6 ++++-- AVOS/Sources/Foundation/User/LCUser.h | 7 +++---- AVOS/Sources/Foundation/User/LCUser.m | 14 +++++++------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/AVOS/LeanCloudObjcTests/LCUserTestCase.swift b/AVOS/LeanCloudObjcTests/LCUserTestCase.swift index 484c03a1..3844ec38 100644 --- a/AVOS/LeanCloudObjcTests/LCUserTestCase.swift +++ b/AVOS/LeanCloudObjcTests/LCUserTestCase.swift @@ -184,7 +184,8 @@ class LCUserTestCase: BaseTestCase { } expecting { exp in - user_1.getFolloweeObjects { followees, error in + query = user_1.followeeObjectsQuery() + query.findObjectsInBackground { followees, error in let followee = followees?.first as? LCObject XCTAssertNotNil(followee) XCTAssertEqual(followee?["group"] as? String, "music") @@ -200,7 +201,8 @@ class LCUserTestCase: BaseTestCase { XCTAssertNil(error) exp.fulfill() if succeeded { - user_2.getFolloweeObjects { followees, error in + query = user_2.followeeObjectsQuery() + query.findObjectsInBackground { followees, error in let followee = followees?.first as? LCObject XCTAssertNotNil(followee) XCTAssertEqual(followee?["group"] as? String, "sport") diff --git a/AVOS/Sources/Foundation/User/LCUser.h b/AVOS/Sources/Foundation/User/LCUser.h index fcd24662..e09afd59 100644 --- a/AVOS/Sources/Foundation/User/LCUser.h +++ b/AVOS/Sources/Foundation/User/LCUser.h @@ -540,6 +540,9 @@ FOUNDATION_EXPORT LeanCloudSocialPlatform const LeanCloudSocialPlatformWeiXin; */ - (LCQuery *)followeeQuery; +/// New query for followee objects. +- (LCQuery *)followeeObjectsQuery; + /** * 通过ID来关注其他用户 * @warning 如果需要被关注者收到消息 需要手动给他发送一条LCStatus. @@ -581,10 +584,6 @@ FOUNDATION_EXPORT LeanCloudSocialPlatform const LeanCloudSocialPlatformWeiXin; */ - (void)getFollowees:(LCArrayResultBlock)callback; -/// Get followee objects. -/// @param callback Result callback. -- (void)getFolloweeObjectsWithCallback:(void (^)(NSArray * _Nullable objects, NSError * _Nullable error))callback; - /** * 同时获取当前用户的粉丝和关注列表 * diff --git a/AVOS/Sources/Foundation/User/LCUser.m b/AVOS/Sources/Foundation/User/LCUser.m index 739ff2a9..dc62707c 100644 --- a/AVOS/Sources/Foundation/User/LCUser.m +++ b/AVOS/Sources/Foundation/User/LCUser.m @@ -1313,6 +1313,13 @@ - (LCQuery *)followerQuery { return [LCUser followerQuery:self.objectId]; } +- (LCQuery *)followeeObjectsQuery { + LCQuery *query = [LCQuery queryWithClassName:@"_Followee"]; + [query whereKey:@"user" equalTo:self]; + [query includeKey:@"followee"]; + return query; +} + - (void)follow:(NSString *)userId andCallback:(LCBooleanResultBlock)callback { [self follow:userId userDictionary:nil andCallback:callback]; } @@ -1353,13 +1360,6 @@ - (void)getFollowees:(LCArrayResultBlock)callback { [query findObjectsInBackgroundWithBlock:callback]; } -- (void)getFolloweeObjectsWithCallback:(void (^)(NSArray * _Nullable, NSError * _Nullable))callback { - LCQuery *query = [LCQuery queryWithClassName:@"_Followee"]; - [query whereKey:@"user" equalTo:self]; - [query includeKey:@"followee"]; - [query findObjectsInBackgroundWithBlock:callback]; -} - - (void)getFollowersAndFollowees:(LCDictionaryResultBlock)callback { NSString *path = [NSString stringWithFormat:@"users/%@/followersAndFollowees?include=follower,followee", self.objectId]; From 7a99d49ddd122fcdcde1debf1006d9382d4fbcd0 Mon Sep 17 00:00:00 2001 From: zapcannon87 Date: Fri, 16 Jul 2021 11:48:48 +0800 Subject: [PATCH 4/4] release: 13.1.0 --- AVOS/Sources/Foundation/UserAgent.h | 2 +- LeanCloudObjc.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AVOS/Sources/Foundation/UserAgent.h b/AVOS/Sources/Foundation/UserAgent.h index 12ebd12d..b48cc384 100644 --- a/AVOS/Sources/Foundation/UserAgent.h +++ b/AVOS/Sources/Foundation/UserAgent.h @@ -1 +1 @@ -#define SDK_VERSION @"13.0.1" +#define SDK_VERSION @"13.1.0" diff --git a/LeanCloudObjc.podspec b/LeanCloudObjc.podspec index 637a6731..4ac853da 100644 --- a/LeanCloudObjc.podspec +++ b/LeanCloudObjc.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'LeanCloudObjc' - s.version = '13.0.1' + s.version = '13.1.0' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud Objective-C SDK' s.authors = 'LeanCloud'