diff --git a/docs/chat.md b/docs/chat.md index 161aceb1860..1909f2dff0b 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -500,6 +500,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob | `status` | string | Optional: Only available with `includeStatus=true` and for users with a set status | | `statusIcon` | string | Optional: Only available with `includeStatus=true` and for users with a set status | | `statusMessage` | string | Optional: Only available with `includeStatus=true` and for users with a set status | +| `details` | string | Optional: Only provided for the "Everyone" option and can be used as a subline directly | ## System messages diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index 1c761d36f9c..fea3bcdff05 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -39,6 +39,7 @@ use OCP\ICache; use OCP\ICacheFactory; use OCP\IDBConnection; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\Notification\IManager as INotificationManager; @@ -97,6 +98,7 @@ public function __construct( protected IReferenceManager $referenceManager, protected ILimiter $rateLimiter, protected IRequest $request, + protected IL10N $l, protected LoggerInterface $logger, ) { $this->cache = $cacheFactory->createDistributed(CachePrefix::CHAT_LAST_MESSAGE_ID); @@ -961,12 +963,14 @@ public function addConversationNotify(array $results, string $search, Room $room $roomDisplayName = $room->getDisplayName(''); } if ($search === '' || $this->searchIsPartOfConversationNameOrAtAll($search, $roomDisplayName)) { - array_unshift($results, [ + $participantCount = $this->participantService->getNumberOfUsers($room); + $results[] = [ 'id' => 'all', 'label' => $roomDisplayName, + 'details' => $this->l->n('All %n participant', 'All %n participants', $participantCount), 'source' => 'calls', 'mentionId' => 'all', - ]); + ]; } return $results; } diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index c1665b45dd3..0439896a6a0 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -56,6 +56,7 @@ * label: string, * source: string, * mentionId: string, + * details: ?string, * status: ?string, * statusClearAt: ?int, * statusIcon: ?string, diff --git a/openapi-full.json b/openapi-full.json index 757d0c1247c..2f2b1b90a4e 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -437,6 +437,7 @@ "label", "source", "mentionId", + "details", "status", "statusClearAt", "statusIcon", @@ -455,6 +456,10 @@ "mentionId": { "type": "string" }, + "details": { + "type": "string", + "nullable": true + }, "status": { "type": "string", "nullable": true diff --git a/openapi.json b/openapi.json index 86294c350d6..3c08d8beb51 100644 --- a/openapi.json +++ b/openapi.json @@ -378,6 +378,7 @@ "label", "source", "mentionId", + "details", "status", "statusClearAt", "statusIcon", @@ -396,6 +397,10 @@ "mentionId": { "type": "string" }, + "details": { + "type": "string", + "nullable": true + }, "status": { "type": "string", "nullable": true diff --git a/src/components/NewMessage/NewMessage.vue b/src/components/NewMessage/NewMessage.vue index 81e7679492c..ebd72c17077 100644 --- a/src/components/NewMessage/NewMessage.vue +++ b/src/components/NewMessage/NewMessage.vue @@ -911,7 +911,7 @@ export default { if (possibleMention.source === 'calls') { possibleMention.icon = 'icon-user-forced-white' possibleMention.iconUrl = getConversationAvatarOcsUrl(this.token, isDarkTheme) - possibleMention.subline = t('spreed', 'Everyone') + possibleMention.subline = possibleMention?.details ? possibleMention.details : t('spreed', 'Everyone') } else if (possibleMention.source === ATTENDEE.ACTOR_TYPE.GROUPS) { possibleMention.icon = 'icon-group-forced-white' possibleMention.subline = t('spreed', 'Group') diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index 60f16a8c050..51e0f631eed 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -1877,6 +1877,7 @@ export type components = { label: string; source: string; mentionId: string; + details: string | null; status: string | null; /** Format: int64 */ statusClearAt: number | null; diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index 854ed99dc72..43dfd4134e3 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -1378,6 +1378,7 @@ export type components = { label: string; source: string; mentionId: string; + details: string | null; status: string | null; /** Format: int64 */ statusClearAt: number | null; diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 9cd7513e2fa..3d67880d186 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -3080,8 +3080,13 @@ public function userGetsTheFollowingCandidateMentionsInRoomFor($user, $identifie Assert::assertEmpty($mentions); return; } + $expected = $formData->getHash(); + if (empty($expected)) { + Assert::assertEmpty($mentions); + return; + } - Assert::assertCount(count($formData->getHash()), $mentions, 'Mentions count does not match' . "\n" . json_encode($mentions, JSON_PRETTY_PRINT)); + Assert::assertCount(count($expected), $mentions, 'Mentions count does not match' . "\n" . json_encode($mentions, JSON_PRETTY_PRINT)); usort($mentions, function ($a, $b) { if ($a['source'] === $b['source']) { @@ -3090,7 +3095,6 @@ public function userGetsTheFollowingCandidateMentionsInRoomFor($user, $identifie return $a['source'] <=> $b['source']; }); - $expected = $formData->getHash(); usort($expected, function ($a, $b) { if ($a['source'] === $b['source']) { return $a['label'] <=> $b['label']; @@ -3098,6 +3102,8 @@ public function userGetsTheFollowingCandidateMentionsInRoomFor($user, $identifie return $a['source'] <=> $b['source']; }); + $checkDetails = array_key_exists('details', $expected[0]); + foreach ($expected as $key => $row) { if ($row['id'] === 'GUEST_ID') { Assert::assertMatchesRegularExpression('/^guest\/[0-9a-f]{40}$/', $mentions[$key]['id']); @@ -3124,6 +3130,11 @@ public function userGetsTheFollowingCandidateMentionsInRoomFor($user, $identifie unset($row['avatar']); } unset($mentions[$key]['avatar']); + if (!$checkDetails) { + unset($mentions[$key]['details']); + } elseif (empty($row['details'])) { + unset($row['details']); + } Assert::assertEquals($row, $mentions[$key]); } } diff --git a/tests/integration/features/chat-1/mentions.feature b/tests/integration/features/chat-1/mentions.feature index c3140dddf35..78d32f8167c 100644 --- a/tests/integration/features/chat-1/mentions.feature +++ b/tests/integration/features/chat-1/mentions.feature @@ -48,8 +48,8 @@ Feature: chat/mentions | roomType | 2 | | roomName | room | Then user "participant1" gets the following candidate mentions in room "group room" for "" with 200 - | id | label | source | mentionId | - | all | room | calls | all | + | id | label | source | mentionId | details | + | all | room | calls | all | All 1 participant | Scenario: get mentions in a group room When user "participant1" creates room "group room" (v4) @@ -223,7 +223,6 @@ Feature: chat/mentions | id | label | source | mentionId | | GUEST_ID | FooBar | guests | GUEST_ID | And user "guest1" gets the following candidate mentions in room "public room" for "oob" with 200 - | id | label | source | mentionId | And user "guest2" gets the following candidate mentions in room "public room" for "oob" with 200 | id | label | source | mentionId | | GUEST_ID | FooBar | guests | GUEST_ID | diff --git a/tests/php/Chat/ChatManagerTest.php b/tests/php/Chat/ChatManagerTest.php index 6160e01b8c9..07bbcdfdd42 100644 --- a/tests/php/Chat/ChatManagerTest.php +++ b/tests/php/Chat/ChatManagerTest.php @@ -28,6 +28,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\ICacheFactory; use OCP\IDBConnection; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\Notification\IManager as INotificationManager; @@ -58,6 +59,7 @@ class ChatManagerTest extends TestCase { protected ILimiter&MockObject $rateLimiter; protected IRequest&MockObject $request; protected LoggerInterface&MockObject $logger; + protected IL10N&MockObject $l; protected ?ChatManager $chatManager = null; public function setUp(): void { @@ -77,8 +79,15 @@ public function setUp(): void { $this->referenceManager = $this->createMock(IReferenceManager::class); $this->rateLimiter = $this->createMock(ILimiter::class); $this->request = $this->createMock(IRequest::class); + $this->l = $this->createMock(IL10N::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->l->method('n') + ->willReturnCallback(function (string $singular, string $plural, int $count, array $parameters = []) { + $text = $count === 1 ? $singular : $plural; + return vsprintf(str_replace('%n', (string) $count, $text), $parameters); + }); + $this->chatManager = $this->getManager(); } @@ -108,6 +117,7 @@ protected function getManager(array $methods = []): ChatManager { $this->referenceManager, $this->rateLimiter, $this->request, + $this->l, $this->logger, ]) ->onlyMethods($methods) @@ -131,6 +141,7 @@ protected function getManager(array $methods = []): ChatManager { $this->referenceManager, $this->rateLimiter, $this->request, + $this->l, $this->logger, ); } @@ -690,7 +701,8 @@ public static function dataAddConversationNotify(): array { '', ['getType' => Room::TYPE_ONE_TO_ONE], [], - [] + null, + [], ], [ '', @@ -699,13 +711,15 @@ public static function dataAddConversationNotify(): array { 'actor_type' => Attendee::ACTOR_USERS, 'actor_id' => 'user', ])], - [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all']] + 324, + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 324 participants']] ], [ '', ['getMentionPermissions' => 1], ['hasModeratorPermissions' => false], - [] + null, + [], ], [ 'all', @@ -714,7 +728,8 @@ public static function dataAddConversationNotify(): array { 'actor_type' => Attendee::ACTOR_USERS, 'actor_id' => 'user', ])], - [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all']] + 1, + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 1 participant']], ], [ 'all', @@ -726,7 +741,8 @@ public static function dataAddConversationNotify(): array { ]), 'hasModeratorPermissions' => true, ], - [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all']] + 8, + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 8 participants']], ], [ 'here', @@ -735,7 +751,8 @@ public static function dataAddConversationNotify(): array { 'actor_type' => Attendee::ACTOR_GUESTS, 'actor_id' => 'guest', ])], - [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all']] + 12, + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 12 participants']], ], ]; } @@ -743,7 +760,7 @@ public static function dataAddConversationNotify(): array { /** * @dataProvider dataAddConversationNotify */ - public function testAddConversationNotify(string $search, array $roomMocks, array $participantMocks, array $expected): void { + public function testAddConversationNotify(string $search, array $roomMocks, array $participantMocks, ?int $totalCount, array $expected): void { $room = $this->createMock(Room::class); foreach ($roomMocks as $method => $return) { $room->expects($this->once()) @@ -758,6 +775,11 @@ public function testAddConversationNotify(string $search, array $roomMocks, arra ->willReturn($return); } + if ($totalCount !== null) { + $this->participantService->method('getNumberOfUsers') + ->willReturn($totalCount); + } + $actual = $this->chatManager->addConversationNotify([], $search, $room, $participant); $this->assertEquals($expected, $actual); }