Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppIntent support #2005

Merged
merged 8 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
420 changes: 417 additions & 3 deletions NextcloudTalk.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions NextcloudTalk/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,22 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull N
[[NCUserInterfaceController sharedInstance] presentCallKitCallInRoom:roomToken withVideoEnabled:videoCallIntent];
}
}

// A INSendMessageIntent is usually a Siri/Shortcut suggestion and automatically created when we donate a INSendMessageIntent
if ([userActivity.interaction.intent isKindOfClass:[INSendMessageIntent class]]) {
// For a INSendMessageIntent we don't receive a conversationIdentifier, see NCIntentController
INSendMessageIntent *intent = (INSendMessageIntent *)userActivity.interaction.intent;
INPerson *recipient = intent.recipients.firstObject;

if (recipient && recipient.customIdentifier && recipient.customIdentifier.length > 0) {
NCRoom *room = [[NCDatabaseManager sharedInstance] roomWithInternalId:recipient.customIdentifier];

if (room) {
[[NCRoomsManager sharedInstance] startChatInRoom:room];
}
}
}

return YES;
}

Expand Down
5 changes: 3 additions & 2 deletions NextcloudTalk/NCDatabaseManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ extern NSString * const NCDatabaseManagerRoomCapabilitiesChangedNotification;

- (NSInteger)numberOfAccounts;
- (TalkAccount *)activeAccount;
- (NSArray *)allAccounts;
- (NSArray *)inactiveAccounts;
- (NSArray<TalkAccount *> *)allAccounts;
- (NSArray<TalkAccount *> *)inactiveAccounts;
- (TalkAccount * _Nullable)talkAccountForAccountId:(NSString *)accountId;
- (TalkAccount *)talkAccountForUserId:(NSString *)userId inServer:(NSString *)server;
- (void)setActiveAccountWithAccountId:(NSString *)accountId;
Expand All @@ -122,6 +122,7 @@ extern NSString * const NCDatabaseManagerRoomCapabilitiesChangedNotification;

// Rooms
- (NCRoom * _Nullable)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId;
- (NCRoom * _Nullable)roomWithInternalId:(NSString *)internalId;

// FederatedCapabilities
- (FederatedCapabilities * __nullable)federatedCapabilitiesForAccountId:(NSString *)accountId remoteServer:(NSString *)remoteServer roomToken:(NSString *)roomToken;
Expand Down
17 changes: 14 additions & 3 deletions NextcloudTalk/NCDatabaseManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ - (TalkAccount *)activeAccount
return nil;
}

- (NSArray *)allAccounts
- (NSArray<TalkAccount *> *)allAccounts;
{
NSMutableArray *allAccounts = [NSMutableArray new];
for (TalkAccount *managedAccount in [TalkAccount allObjects]) {
Expand All @@ -184,7 +184,7 @@ - (NSArray *)allAccounts
return allAccounts;
}

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

#pragma mark - Rooms

- (NCRoom *)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
- (NCRoom * _Nullable)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
{
NCRoom *unmanagedRoom = nil;
NSPredicate *query = [NSPredicate predicateWithFormat:@"token = %@ AND accountId = %@", token, accountId];
Expand All @@ -372,6 +372,17 @@ - (NCRoom *)roomWithToken:(NSString *)token forAccountId:(NSString *)accountId
return unmanagedRoom;
}

- (NCRoom * _Nullable)roomWithInternalId:(NSString *)internalId
{
NCRoom *unmanagedRoom = nil;
NSPredicate *query = [NSPredicate predicateWithFormat:@"internalId = %@", internalId];
NCRoom *managedRoom = [NCRoom objectsWithPredicate:query].firstObject;
if (managedRoom) {
unmanagedRoom = [[NCRoom alloc] initWithValue:managedRoom];
}
return unmanagedRoom;
}

#pragma mark - Talk capabilities

- (void)setTalkCapabilities:(NSDictionary *)capabilitiesDict onTalkCapabilitiesObject:(TalkCapabilities *)capabilities
Expand Down
31 changes: 31 additions & 0 deletions NextcloudTalk/NCDatabaseManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,35 @@ import Foundation
}
}
}

// MARK: - Rooms

func roomsForAccountId(_ accountId: String, withRealm realm: RLMRealm?) -> [NCRoom] {
let query = NSPredicate(format: "accountId = %@", accountId)
var managedRooms: RLMResults<AnyObject>

if let realm {
managedRooms = NCRoom.objects(in: realm, with: query)
} else {
managedRooms = NCRoom.objects(with: query)
}

// Create an unmanaged copy of the rooms
var unmanagedRooms: [NCRoom] = []

for case let managedRoom as NCRoom in managedRooms {
if managedRoom.isBreakoutRoom, managedRoom.lobbyState == .moderatorsOnly {
continue
}

unmanagedRooms.append(NCRoom(value: managedRoom))
}

// Sort by favorites first, then by lastActivity
unmanagedRooms.sort { first, second in
(first.isFavorite ? 1 : 0, first.lastActivity) > (second.isFavorite ? 1 : 0, second.lastActivity)
}

return unmanagedRooms
}
}
15 changes: 13 additions & 2 deletions NextcloudTalk/NCIntentController.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ - (void)getInteractionForRoom:(NCRoom *)room withTitle:(NSString *)title withCom
displayName:title
image:image
contactIdentifier:nil
customIdentifier:nil];
customIdentifier:room.internalId];

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

- (void)donateSendMessageIntentForRoom:(NCRoom *)room
{
// When the system suggest to write a message to "someone", we don't receive the conversationIdentifier.
// Therefore we also add a recipient here, although it's technically not a "Person", but a "Room".
INPersonHandle *handle = [[INPersonHandle alloc] initWithValue:nil type:INPersonHandleTypeUnknown];
INPerson *recipient = [[INPerson alloc]
initWithPersonHandle:handle
nameComponents:nil
displayName:room.displayName
image:nil
contactIdentifier:nil
customIdentifier:room.internalId];

INSpeakableString *groupName = [[INSpeakableString alloc] initWithSpokenPhrase:room.displayName];
INSendMessageIntent *sendMessageIntent = [[INSendMessageIntent alloc] initWithRecipients:nil
INSendMessageIntent *sendMessageIntent = [[INSendMessageIntent alloc] initWithRecipients:@[recipient]
outgoingMessageType:INOutgoingMessageTypeOutgoingMessageText
content:nil
speakableGroupName:groupName
Expand Down
2 changes: 1 addition & 1 deletion NextcloudTalk/NCRoomsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ - (void)joinChat:(NSNotification *)notification

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

for (NCRoom *room in accountRooms) {
if (room.type == kNCRoomTypeOneToOne && [room.name isEqualToString:userId]) {
Expand Down
29 changes: 0 additions & 29 deletions NextcloudTalk/NCRoomsManagerExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -363,35 +363,6 @@ import Foundation

// MARK: - Room

public func roomsForAccountId(_ accountId: String, withRealm realm: RLMRealm?) -> [NCRoom] {
let query = NSPredicate(format: "accountId = %@", accountId)
var managedRooms: RLMResults<AnyObject>

if let realm {
managedRooms = NCRoom.objects(in: realm, with: query)
} else {
managedRooms = NCRoom.objects(with: query)
}

// Create an unmanaged copy of the rooms
var unmanagedRooms: [NCRoom] = []

for case let managedRoom as NCRoom in managedRooms {
if managedRoom.isBreakoutRoom, managedRoom.lobbyState == .moderatorsOnly {
continue
}

unmanagedRooms.append(NCRoom(value: managedRoom))
}

// Sort by favorites first, then by lastActivity
unmanagedRooms.sort { first, second in
(first.isFavorite ? 1 : 0, first.lastActivity) > (second.isFavorite ? 1 : 0, second.lastActivity)
}

return unmanagedRooms
}

public func resendOfflineMessagesWithCompletionBlock(_ block: SendOfflineMessagesCompletionBlock?) {
// Try to send offline messages for all rooms
self.resendOfflineMessages(forToken: nil, withCompletionBlock: block)
Expand Down
2 changes: 1 addition & 1 deletion NextcloudTalk/RoomsTableViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ - (NSArray *)getFilters
- (void)refreshRoomList
{
TalkAccount *account = [[NCDatabaseManager sharedInstance] activeAccount];
NSArray *accountRooms = [[NCRoomsManager sharedInstance] roomsForAccountId:account.accountId withRealm:nil];
NSArray *accountRooms = [[NCDatabaseManager sharedInstance] roomsForAccountId:account.accountId withRealm:nil];
_allRooms = [[NSMutableArray alloc] initWithArray:accountRooms];
_rooms = [[NSMutableArray alloc] initWithArray:accountRooms];

Expand Down
4 changes: 3 additions & 1 deletion NextcloudTalk/UserStatusAbsenceSwiftUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ struct UserStatusAbsenceSwiftUIView: View {
})

if absenceStatus.hasReplacementSet {
Button("Reset replacement") {
Button {
absenceStatus.replacementUserId = nil
absenceStatus.replacementUserDisplayName = nil
} label: {
Text("Reset replacement", comment: "Replacement in case of out of office")
}
.foregroundStyle(.primary)
}
Expand Down
9 changes: 9 additions & 0 deletions NextcloudTalk/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
/* No comment provided by engineer. */
"Account not configured" = "Account not configured";

/* No comment provided by engineer. */
"Account not found" = "Account not found";

/* No comment provided by engineer. */
"Accounts" = "Accounts";

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

/* No comment provided by engineer. */
"An error occurred while setting user status" = "An error occurred while setting user status";

/* No comment provided by engineer. */
"An error occurred while sharing the file" = "An error occurred while sharing the file";

Expand Down Expand Up @@ -1675,6 +1681,9 @@
/* No comment provided by engineer. */
"Resend invitations" = "Resend invitations";

/* Replacement in case of out of office */
"Reset replacement" = "Reset replacement";

/* Results of a poll */
"Results" = "Results";

Expand Down
34 changes: 34 additions & 0 deletions NextcloudTalkTests/Unit/UnitNCDatabaseManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,38 @@ final class UnitNCDatabaseManager: TestBaseRealm {
capabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: activeAccount.accountId)
XCTAssertEqual(capabilities?.externalSignalingServerVersion, testVersionUpdated)
}

func testRoomsForAccount() throws {
let nonFavOld = addRoom(withToken: "NonFavOld") { room in
room.lastActivity = 100
}

let nonFavNew = addRoom(withToken: "NonFavNew") { room in
room.lastActivity = 1000
}

let favOld = addRoom(withToken: "FavOld") { room in
room.lastActivity = 100
room.isFavorite = true
}

let favNew = addRoom(withToken: "FavNew") { room in
room.lastActivity = 1000
room.isFavorite = true
}

// Add an unrelated room, which should not be returned
addRoom(withToken: "Unrelated", withAccountId: "foo")

let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
let rooms = NCDatabaseManager.sharedInstance().roomsForAccountId(activeAccount.accountId, withRealm: nil)
let expectedOrder = [favNew, favOld, nonFavNew, nonFavOld]

XCTAssertEqual(rooms.count, 4)

// Check if the order is correct
for (index, element) in rooms.enumerated() {
XCTAssertEqual(expectedOrder[index].token, element.token)
}
}
}
33 changes: 0 additions & 33 deletions NextcloudTalkTests/Unit/UnitNCRoomsManagerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,37 +50,4 @@ final class UnitNCRoomsManagerTest: TestBaseRealm {
XCTAssertFalse(realmMessage.isOfflineMessage)
}

func testRoomsForAccount() throws {
let nonFavOld = addRoom(withToken: "NonFavOld") { room in
room.lastActivity = 100
}

let nonFavNew = addRoom(withToken: "NonFavNew") { room in
room.lastActivity = 1000
}

let favOld = addRoom(withToken: "FavOld") { room in
room.lastActivity = 100
room.isFavorite = true
}

let favNew = addRoom(withToken: "FavNew") { room in
room.lastActivity = 1000
room.isFavorite = true
}

// Add an unrelated room, which should not be returned
addRoom(withToken: "Unrelated", withAccountId: "foo")

let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
let rooms = NCRoomsManager.sharedInstance().roomsForAccountId(activeAccount.accountId, withRealm: nil)
let expectedOrder = [favNew, favOld, nonFavNew, nonFavOld]

XCTAssertEqual(rooms.count, 4)

// Check if the order is correct
for (index, element) in rooms.enumerated() {
XCTAssertEqual(expectedOrder[index].token, element.token)
}
}
}
4 changes: 4 additions & 0 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ target "BroadcastUploadExtension" do
common_dependencies
end

target "TalkIntents" do
common_dependencies_ext
end

pre_install do |installer|
puts 'pre_install begin....'

Expand Down
38 changes: 1 addition & 37 deletions ShareExtension/ShareViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ - (void)setupShareViewForAccount:(TalkAccount *)account
[self setProfileButtonForAccount:_shareAccount];
}

NSArray *accountRooms = [self roomsForAccountId:_shareAccount.accountId];
NSArray *accountRooms = [[NCDatabaseManager sharedInstance] roomsForAccountId:_shareAccount.accountId withRealm:_realm];
_rooms = [[NSMutableArray alloc] initWithArray:accountRooms];
_serverCapabilities = [self getServerCapabilitesForAccount:_shareAccount withRealm:_realm];

Expand Down Expand Up @@ -351,42 +351,6 @@ - (void)showAccountSelector
[self presentViewController:optionsActionSheet animated:YES completion:nil];
}

#pragma mark - Rooms

- (NSArray *)roomsForAccountId:(NSString *)accountId
{
NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", accountId];
RLMResults *managedRooms = [NCRoom objectsInRealm:_realm withPredicate:query];;

// Create an unmanaged copy of the rooms
NSMutableArray *unmanagedRooms = [NSMutableArray new];
for (NCRoom *managedRoom in managedRooms) {
NCRoom *unmanagedRoom = [[NCRoom alloc] initWithValue:managedRoom];
// Filter out breakout rooms with lobby enabled
if ([unmanagedRoom isBreakoutRoom] && unmanagedRoom.lobbyState == NCRoomLobbyStateModeratorsOnly) {
continue;
}
[unmanagedRooms addObject:unmanagedRoom];
}
// Sort by favorites
NSSortDescriptor *favoriteSorting = [NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES comparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
NCRoom *first = (NCRoom*)obj1;
NCRoom *second = (NCRoom*)obj2;
BOOL favorite1 = first.isFavorite;
BOOL favorite2 = second.isFavorite;
if (favorite1 != favorite2) {
return favorite2 - favorite1;
}
return NSOrderedSame;
}];
// Sort by lastActivity
NSSortDescriptor *valueDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastActivity" ascending:NO];
NSArray *descriptors = [NSArray arrayWithObjects:favoriteSorting, valueDescriptor, nil];
[unmanagedRooms sortUsingDescriptors:descriptors];

return unmanagedRooms;
}

#pragma mark - Shared items

- (void)setSharedItemToShareConfirmationViewController:(ShareConfirmationViewController *)shareConfirmationVC
Expand Down
Loading
Loading