Skip to content

Commit aeb213d

Browse files
authored
Merge pull request #2005 from nextcloud/feat/noid/app-intent-support
AppIntent support
2 parents 87b2b79 + c99f3c1 commit aeb213d

27 files changed

+1010
-113
lines changed

NextcloudTalk.xcodeproj/project.pbxproj

+417-3
Large diffs are not rendered by default.

NextcloudTalk/AppDelegate.m

+16
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,22 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull N
136136
[[NCUserInterfaceController sharedInstance] presentCallKitCallInRoom:roomToken withVideoEnabled:videoCallIntent];
137137
}
138138
}
139+
140+
// A INSendMessageIntent is usually a Siri/Shortcut suggestion and automatically created when we donate a INSendMessageIntent
141+
if ([userActivity.interaction.intent isKindOfClass:[INSendMessageIntent class]]) {
142+
// For a INSendMessageIntent we don't receive a conversationIdentifier, see NCIntentController
143+
INSendMessageIntent *intent = (INSendMessageIntent *)userActivity.interaction.intent;
144+
INPerson *recipient = intent.recipients.firstObject;
145+
146+
if (recipient && recipient.customIdentifier && recipient.customIdentifier.length > 0) {
147+
NCRoom *room = [[NCDatabaseManager sharedInstance] roomWithInternalId:recipient.customIdentifier];
148+
149+
if (room) {
150+
[[NCRoomsManager sharedInstance] startChatInRoom:room];
151+
}
152+
}
153+
}
154+
139155
return YES;
140156
}
141157

NextcloudTalk/NCDatabaseManager.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ extern NSString * const NCDatabaseManagerRoomCapabilitiesChangedNotification;
103103

104104
- (NSInteger)numberOfAccounts;
105105
- (TalkAccount *)activeAccount;
106-
- (NSArray *)allAccounts;
107-
- (NSArray *)inactiveAccounts;
106+
- (NSArray<TalkAccount *> *)allAccounts;
107+
- (NSArray<TalkAccount *> *)inactiveAccounts;
108108
- (TalkAccount * _Nullable)talkAccountForAccountId:(NSString *)accountId;
109109
- (TalkAccount *)talkAccountForUserId:(NSString *)userId inServer:(NSString *)server;
110110
- (void)setActiveAccountWithAccountId:(NSString *)accountId;
@@ -122,6 +122,7 @@ extern NSString * const NCDatabaseManagerRoomCapabilitiesChangedNotification;
122122

123123
// Rooms
124124
- (NCRoom * _Nullable)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId;
125+
- (NCRoom * _Nullable)roomWithInternalId:(NSString *)internalId;
125126

126127
// FederatedCapabilities
127128
- (FederatedCapabilities * __nullable)federatedCapabilitiesForAccountId:(NSString *)accountId remoteServer:(NSString *)remoteServer roomToken:(NSString *)roomToken;

NextcloudTalk/NCDatabaseManager.m

+14-3
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ - (TalkAccount *)activeAccount
174174
return nil;
175175
}
176176

177-
- (NSArray *)allAccounts
177+
- (NSArray<TalkAccount *> *)allAccounts;
178178
{
179179
NSMutableArray *allAccounts = [NSMutableArray new];
180180
for (TalkAccount *managedAccount in [TalkAccount allObjects]) {
@@ -184,7 +184,7 @@ - (NSArray *)allAccounts
184184
return allAccounts;
185185
}
186186

187-
- (NSArray *)inactiveAccounts
187+
- (NSArray<TalkAccount *> *)inactiveAccounts
188188
{
189189
NSMutableArray *inactiveAccounts = [NSMutableArray new];
190190
for (TalkAccount *managedInactiveAccount in [TalkAccount objectsWhere:(@"active = false")]) {
@@ -361,7 +361,7 @@ - (void)updateLastModifiedSinceForAccountId:(NSString *)accountId with:(nonnull
361361

362362
#pragma mark - Rooms
363363

364-
- (NCRoom *)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
364+
- (NCRoom * _Nullable)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
365365
{
366366
NCRoom *unmanagedRoom = nil;
367367
NSPredicate *query = [NSPredicate predicateWithFormat:@"token = %@ AND accountId = %@", token, accountId];
@@ -372,6 +372,17 @@ - (NCRoom *)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
372372
return unmanagedRoom;
373373
}
374374

375+
- (NCRoom * _Nullable)roomWithInternalId:(NSString *)internalId
376+
{
377+
NCRoom *unmanagedRoom = nil;
378+
NSPredicate *query = [NSPredicate predicateWithFormat:@"internalId = %@", internalId];
379+
NCRoom *managedRoom = [NCRoom objectsWithPredicate:query].firstObject;
380+
if (managedRoom) {
381+
unmanagedRoom = [[NCRoom alloc] initWithValue:managedRoom];
382+
}
383+
return unmanagedRoom;
384+
}
385+
375386
#pragma mark - Talk capabilities
376387

377388
- (void)setTalkCapabilities:(NSDictionary *)capabilitiesDict onTalkCapabilitiesObject:(TalkCapabilities *)capabilities

NextcloudTalk/NCDatabaseManager.swift

+31
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,35 @@ import Foundation
3838
}
3939
}
4040
}
41+
42+
// MARK: - Rooms
43+
44+
func roomsForAccountId(_ accountId: String, withRealm realm: RLMRealm?) -> [NCRoom] {
45+
let query = NSPredicate(format: "accountId = %@", accountId)
46+
var managedRooms: RLMResults<AnyObject>
47+
48+
if let realm {
49+
managedRooms = NCRoom.objects(in: realm, with: query)
50+
} else {
51+
managedRooms = NCRoom.objects(with: query)
52+
}
53+
54+
// Create an unmanaged copy of the rooms
55+
var unmanagedRooms: [NCRoom] = []
56+
57+
for case let managedRoom as NCRoom in managedRooms {
58+
if managedRoom.isBreakoutRoom, managedRoom.lobbyState == .moderatorsOnly {
59+
continue
60+
}
61+
62+
unmanagedRooms.append(NCRoom(value: managedRoom))
63+
}
64+
65+
// Sort by favorites first, then by lastActivity
66+
unmanagedRooms.sort { first, second in
67+
(first.isFavorite ? 1 : 0, first.lastActivity) > (second.isFavorite ? 1 : 0, second.lastActivity)
68+
}
69+
70+
return unmanagedRooms
71+
}
4172
}

NextcloudTalk/NCIntentController.m

+13-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ - (void)getInteractionForRoom:(NCRoom *)room withTitle:(NSString *)title withCom
5858
displayName:title
5959
image:image
6060
contactIdentifier:nil
61-
customIdentifier:nil];
61+
customIdentifier:room.internalId];
6262

6363
INSendMessageIntent *sendMessageIntent = [[INSendMessageIntent alloc] initWithRecipients:nil
6464
outgoingMessageType:INOutgoingMessageTypeOutgoingMessageText
@@ -87,8 +87,19 @@ - (void)getInteractionForRoom:(NCRoom *)room withTitle:(NSString *)title withCom
8787

8888
- (void)donateSendMessageIntentForRoom:(NCRoom *)room
8989
{
90+
// When the system suggest to write a message to "someone", we don't receive the conversationIdentifier.
91+
// Therefore we also add a recipient here, although it's technically not a "Person", but a "Room".
92+
INPersonHandle *handle = [[INPersonHandle alloc] initWithValue:nil type:INPersonHandleTypeUnknown];
93+
INPerson *recipient = [[INPerson alloc]
94+
initWithPersonHandle:handle
95+
nameComponents:nil
96+
displayName:room.displayName
97+
image:nil
98+
contactIdentifier:nil
99+
customIdentifier:room.internalId];
100+
90101
INSpeakableString *groupName = [[INSpeakableString alloc] initWithSpokenPhrase:room.displayName];
91-
INSendMessageIntent *sendMessageIntent = [[INSendMessageIntent alloc] initWithRecipients:nil
102+
INSendMessageIntent *sendMessageIntent = [[INSendMessageIntent alloc] initWithRecipients:@[recipient]
92103
outgoingMessageType:INOutgoingMessageTypeOutgoingMessageText
93104
content:nil
94105
speakableGroupName:groupName

NextcloudTalk/NCRoomsManager.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ - (void)joinChat:(NSNotification *)notification
682682

683683
- (void)joinOrCreateChatWithUser:(NSString *)userId usingAccountId:(NSString *)accountId
684684
{
685-
NSArray *accountRooms = [[NCRoomsManager sharedInstance] roomsForAccountId:accountId withRealm:nil];
685+
NSArray *accountRooms = [[NCDatabaseManager sharedInstance] roomsForAccountId:accountId withRealm:nil];
686686

687687
for (NCRoom *room in accountRooms) {
688688
if (room.type == kNCRoomTypeOneToOne && [room.name isEqualToString:userId]) {

NextcloudTalk/NCRoomsManagerExtensions.swift

-29
Original file line numberDiff line numberDiff line change
@@ -363,35 +363,6 @@ import Foundation
363363

364364
// MARK: - Room
365365

366-
public func roomsForAccountId(_ accountId: String, withRealm realm: RLMRealm?) -> [NCRoom] {
367-
let query = NSPredicate(format: "accountId = %@", accountId)
368-
var managedRooms: RLMResults<AnyObject>
369-
370-
if let realm {
371-
managedRooms = NCRoom.objects(in: realm, with: query)
372-
} else {
373-
managedRooms = NCRoom.objects(with: query)
374-
}
375-
376-
// Create an unmanaged copy of the rooms
377-
var unmanagedRooms: [NCRoom] = []
378-
379-
for case let managedRoom as NCRoom in managedRooms {
380-
if managedRoom.isBreakoutRoom, managedRoom.lobbyState == .moderatorsOnly {
381-
continue
382-
}
383-
384-
unmanagedRooms.append(NCRoom(value: managedRoom))
385-
}
386-
387-
// Sort by favorites first, then by lastActivity
388-
unmanagedRooms.sort { first, second in
389-
(first.isFavorite ? 1 : 0, first.lastActivity) > (second.isFavorite ? 1 : 0, second.lastActivity)
390-
}
391-
392-
return unmanagedRooms
393-
}
394-
395366
public func resendOfflineMessagesWithCompletionBlock(_ block: SendOfflineMessagesCompletionBlock?) {
396367
// Try to send offline messages for all rooms
397368
self.resendOfflineMessages(forToken: nil, withCompletionBlock: block)

NextcloudTalk/RoomsTableViewController.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ - (NSArray *)getFilters
788788
- (void)refreshRoomList
789789
{
790790
TalkAccount *account = [[NCDatabaseManager sharedInstance] activeAccount];
791-
NSArray *accountRooms = [[NCRoomsManager sharedInstance] roomsForAccountId:account.accountId withRealm:nil];
791+
NSArray *accountRooms = [[NCDatabaseManager sharedInstance] roomsForAccountId:account.accountId withRealm:nil];
792792
_allRooms = [[NSMutableArray alloc] initWithArray:accountRooms];
793793
_rooms = [[NSMutableArray alloc] initWithArray:accountRooms];
794794

NextcloudTalk/UserStatusAbsenceSwiftUIView.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ struct UserStatusAbsenceSwiftUIView: View {
4747
})
4848

4949
if absenceStatus.hasReplacementSet {
50-
Button("Reset replacement") {
50+
Button {
5151
absenceStatus.replacementUserId = nil
5252
absenceStatus.replacementUserDisplayName = nil
53+
} label: {
54+
Text("Reset replacement", comment: "Replacement in case of out of office")
5355
}
5456
.foregroundStyle(.primary)
5557
}

NextcloudTalk/en.lproj/Localizable.strings

+9
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@
7979
/* No comment provided by engineer. */
8080
"Account not configured" = "Account not configured";
8181

82+
/* No comment provided by engineer. */
83+
"Account not found" = "Account not found";
84+
8285
/* No comment provided by engineer. */
8386
"Accounts" = "Accounts";
8487

@@ -310,6 +313,9 @@
310313
/* No comment provided by engineer. */
311314
"An error occurred while setting the avatar" = "An error occurred while setting the avatar";
312315

316+
/* No comment provided by engineer. */
317+
"An error occurred while setting user status" = "An error occurred while setting user status";
318+
313319
/* No comment provided by engineer. */
314320
"An error occurred while sharing the file" = "An error occurred while sharing the file";
315321

@@ -1675,6 +1681,9 @@
16751681
/* No comment provided by engineer. */
16761682
"Resend invitations" = "Resend invitations";
16771683

1684+
/* Replacement in case of out of office */
1685+
"Reset replacement" = "Reset replacement";
1686+
16781687
/* Results of a poll */
16791688
"Results" = "Results";
16801689

NextcloudTalkTests/Unit/UnitNCDatabaseManager.swift

+34
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,38 @@ final class UnitNCDatabaseManager: TestBaseRealm {
2525
capabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: activeAccount.accountId)
2626
XCTAssertEqual(capabilities?.externalSignalingServerVersion, testVersionUpdated)
2727
}
28+
29+
func testRoomsForAccount() throws {
30+
let nonFavOld = addRoom(withToken: "NonFavOld") { room in
31+
room.lastActivity = 100
32+
}
33+
34+
let nonFavNew = addRoom(withToken: "NonFavNew") { room in
35+
room.lastActivity = 1000
36+
}
37+
38+
let favOld = addRoom(withToken: "FavOld") { room in
39+
room.lastActivity = 100
40+
room.isFavorite = true
41+
}
42+
43+
let favNew = addRoom(withToken: "FavNew") { room in
44+
room.lastActivity = 1000
45+
room.isFavorite = true
46+
}
47+
48+
// Add an unrelated room, which should not be returned
49+
addRoom(withToken: "Unrelated", withAccountId: "foo")
50+
51+
let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
52+
let rooms = NCDatabaseManager.sharedInstance().roomsForAccountId(activeAccount.accountId, withRealm: nil)
53+
let expectedOrder = [favNew, favOld, nonFavNew, nonFavOld]
54+
55+
XCTAssertEqual(rooms.count, 4)
56+
57+
// Check if the order is correct
58+
for (index, element) in rooms.enumerated() {
59+
XCTAssertEqual(expectedOrder[index].token, element.token)
60+
}
61+
}
2862
}

NextcloudTalkTests/Unit/UnitNCRoomsManagerTest.swift

-33
Original file line numberDiff line numberDiff line change
@@ -50,37 +50,4 @@ final class UnitNCRoomsManagerTest: TestBaseRealm {
5050
XCTAssertFalse(realmMessage.isOfflineMessage)
5151
}
5252

53-
func testRoomsForAccount() throws {
54-
let nonFavOld = addRoom(withToken: "NonFavOld") { room in
55-
room.lastActivity = 100
56-
}
57-
58-
let nonFavNew = addRoom(withToken: "NonFavNew") { room in
59-
room.lastActivity = 1000
60-
}
61-
62-
let favOld = addRoom(withToken: "FavOld") { room in
63-
room.lastActivity = 100
64-
room.isFavorite = true
65-
}
66-
67-
let favNew = addRoom(withToken: "FavNew") { room in
68-
room.lastActivity = 1000
69-
room.isFavorite = true
70-
}
71-
72-
// Add an unrelated room, which should not be returned
73-
addRoom(withToken: "Unrelated", withAccountId: "foo")
74-
75-
let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
76-
let rooms = NCRoomsManager.sharedInstance().roomsForAccountId(activeAccount.accountId, withRealm: nil)
77-
let expectedOrder = [favNew, favOld, nonFavNew, nonFavOld]
78-
79-
XCTAssertEqual(rooms.count, 4)
80-
81-
// Check if the order is correct
82-
for (index, element) in rooms.enumerated() {
83-
XCTAssertEqual(expectedOrder[index].token, element.token)
84-
}
85-
}
8653
}

Podfile

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ target "BroadcastUploadExtension" do
3838
common_dependencies
3939
end
4040

41+
target "TalkIntents" do
42+
common_dependencies_ext
43+
end
44+
4145
pre_install do |installer|
4246
puts 'pre_install begin....'
4347

ShareExtension/ShareViewController.m

+1-37
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ - (void)setupShareViewForAccount:(TalkAccount *)account
312312
[self setProfileButtonForAccount:_shareAccount];
313313
}
314314

315-
NSArray *accountRooms = [self roomsForAccountId:_shareAccount.accountId];
315+
NSArray *accountRooms = [[NCDatabaseManager sharedInstance] roomsForAccountId:_shareAccount.accountId withRealm:_realm];
316316
_rooms = [[NSMutableArray alloc] initWithArray:accountRooms];
317317
_serverCapabilities = [self getServerCapabilitesForAccount:_shareAccount withRealm:_realm];
318318

@@ -351,42 +351,6 @@ - (void)showAccountSelector
351351
[self presentViewController:optionsActionSheet animated:YES completion:nil];
352352
}
353353

354-
#pragma mark - Rooms
355-
356-
- (NSArray *)roomsForAccountId:(NSString *)accountId
357-
{
358-
NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
359-
RLMResults *managedRooms = [NCRoom objectsInRealm:_realm withPredicate:query];;
360-
361-
// Create an unmanaged copy of the rooms
362-
NSMutableArray *unmanagedRooms = [NSMutableArray new];
363-
for (NCRoom *managedRoom in managedRooms) {
364-
NCRoom *unmanagedRoom = [[NCRoom alloc] initWithValue:managedRoom];
365-
// Filter out breakout rooms with lobby enabled
366-
if ([unmanagedRoom isBreakoutRoom] && unmanagedRoom.lobbyState == NCRoomLobbyStateModeratorsOnly) {
367-
continue;
368-
}
369-
[unmanagedRooms addObject:unmanagedRoom];
370-
}
371-
// Sort by favorites
372-
NSSortDescriptor *favoriteSorting = [NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES comparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
373-
NCRoom *first = (NCRoom*)obj1;
374-
NCRoom *second = (NCRoom*)obj2;
375-
BOOL favorite1 = first.isFavorite;
376-
BOOL favorite2 = second.isFavorite;
377-
if (favorite1 != favorite2) {
378-
return favorite2 - favorite1;
379-
}
380-
return NSOrderedSame;
381-
}];
382-
// Sort by lastActivity
383-
NSSortDescriptor *valueDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastActivity" ascending:NO];
384-
NSArray *descriptors = [NSArray arrayWithObjects:favoriteSorting, valueDescriptor, nil];
385-
[unmanagedRooms sortUsingDescriptors:descriptors];
386-
387-
return unmanagedRooms;
388-
}
389-
390354
#pragma mark - Shared items
391355

392356
- (void)setSharedItemToShareConfirmationViewController:(ShareConfirmationViewController *)shareConfirmationVC

0 commit comments

Comments
 (0)