From 08e21d5a8a9c1f9d22802f5277f0078e0efdac3c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 5 Mar 2024 12:27:02 +0100 Subject: [PATCH] feat(chat): Allow marking chat as read without a last message ID This is required for federated conversations where the clients might not know the hosted message ID but only the proxy ID instead Signed-off-by: Joas Schilling --- docs/capabilities.md | 1 + docs/chat.md | 6 +++--- lib/Capabilities.php | 1 + lib/Controller/ChatController.php | 8 +++++--- openapi-full.json | 4 ++-- openapi.json | 4 ++-- src/types/openapi/openapi-full.ts | 6 +++--- src/types/openapi/openapi.ts | 6 +++--- tests/integration/features/bootstrap/FeatureContext.php | 2 +- tests/integration/features/chat-2/unread-messages.feature | 4 ++++ tests/php/CapabilitiesTest.php | 1 + 11 files changed, 26 insertions(+), 17 deletions(-) diff --git a/docs/capabilities.md b/docs/capabilities.md index 257b747455f3..c0d746cd78df 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -143,3 +143,4 @@ * `delete-messages-unlimited` - Whether messages can be deleted at any time (used to be restricted to 6 hours after posting) * `edit-messages` - Whether messages can be edited (restricted to 24 hours after posting) * `silent-send-state` - Whether messages contain a flag that they were sent silently +* `chat-read-last` - Whether chat can be marked read without giving a message ID (will fall back to the conversations last message ID) diff --git a/docs/chat.md b/docs/chat.md index cbf279963ae2..358e0b976053 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -416,9 +416,9 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob * Endpoint: `/chat/{token}/read` * Data: -| field | type | Description | -|-------------------|------|--------------------------| -| `lastReadMessage` | int | The last read message ID | +| field | type | Description | +|-------------------|----------|----------------------------------------------------------------------| +| `lastReadMessage` | int/null | The last read message ID (Optional with `chat-read-last` capability) | * Response: - Status code: diff --git a/lib/Capabilities.php b/lib/Capabilities.php index cb78f226d01c..f7648cacb2ba 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -139,6 +139,7 @@ public function getCapabilities(): array { 'delete-messages-unlimited', 'edit-messages', 'silent-send-state', + 'chat-read-last', ], 'config' => [ 'attachments' => [ diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index 66efec4b281a..c1837ac74e3a 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -1043,11 +1043,12 @@ public function clearHistory(): DataResponse { return new DataResponse($data, $bridge['enabled'] ? Http::STATUS_ACCEPTED : Http::STATUS_OK, $headers); } + /** * Set the read marker to a specific message * - * @param int $lastReadMessage ID if the last read message - * @psalm-param non-negative-int $lastReadMessage + * @param int|null $lastReadMessage ID if the last read message (Optional only with `chat-read-last` capability) + * @psalm-param non-negative-int|null $lastReadMessage * @return DataResponse * * 200: Read marker set successfully @@ -1055,13 +1056,14 @@ public function clearHistory(): DataResponse { #[FederationSupported] #[PublicPage] #[RequireAuthenticatedParticipant] - public function setReadMarker(int $lastReadMessage): DataResponse { + public function setReadMarker(?int $lastReadMessage = null): DataResponse { if ($this->room->getRemoteServer() !== '') { /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */ $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class); return $proxy->setReadMarker($this->room, $this->participant, $this->getResponseFormat(), $lastReadMessage); } + $lastReadMessage = $lastReadMessage ?? $this->room->getLastMessageId(); $this->participantService->updateLastReadMessage($this->participant, $lastReadMessage); $attendee = $this->participant->getAttendee(); diff --git a/openapi-full.json b/openapi-full.json index 195e2659e925..931ef296dced 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -6266,11 +6266,11 @@ { "name": "lastReadMessage", "in": "query", - "description": "ID if the last read message", - "required": true, + "description": "ID if the last read message (Optional only with `chat-read-last` capability)", "schema": { "type": "integer", "format": "int64", + "nullable": true, "minimum": 0 } }, diff --git a/openapi.json b/openapi.json index e7b9d2292132..297c180914eb 100644 --- a/openapi.json +++ b/openapi.json @@ -6148,11 +6148,11 @@ { "name": "lastReadMessage", "in": "query", - "description": "ID if the last read message", - "required": true, + "description": "ID if the last read message (Optional only with `chat-read-last` capability)", "schema": { "type": "integer", "format": "int64", + "nullable": true, "minimum": 0 } }, diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index a2e752054c1e..6e379f3d35c6 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -2431,9 +2431,9 @@ export type operations = { /** Set the read marker to a specific message */ "chat-set-read-marker": { parameters: { - query: { - /** @description ID if the last read message */ - lastReadMessage: number; + query?: { + /** @description ID if the last read message (Optional only with `chat-read-last` capability) */ + lastReadMessage?: number | null; }; header: { /** @description Required to be true for the API request to pass */ diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index c20dd9568705..ec40b172cffb 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -2268,9 +2268,9 @@ export type operations = { /** Set the read marker to a specific message */ "chat-set-read-marker": { parameters: { - query: { - /** @description ID if the last read message */ - lastReadMessage: number; + query?: { + /** @description ID if the last read message (Optional only with `chat-read-last` capability) */ + lastReadMessage?: number | null; }; header: { /** @description Required to be true for the API request to pass */ diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index a6b2d540b751..a462025eccbf 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -2365,7 +2365,7 @@ public function userReadsMessageInRoom($user, $message, $identifier, $statusCode $this->setCurrentUser($user); $this->sendRequest( 'POST', '/apps/spreed/api/' . $apiVersion . '/chat/' . self::$identifierToToken[$identifier] . '/read', - new TableNode([['lastReadMessage', self::$textToMessageId[$message]]]) + $message === 'NULL' ? null : new TableNode([['lastReadMessage', self::$textToMessageId[$message]]]), ); $this->assertStatusCode($this->response, $statusCode); } diff --git a/tests/integration/features/chat-2/unread-messages.feature b/tests/integration/features/chat-2/unread-messages.feature index a15014189964..17032492c2df 100644 --- a/tests/integration/features/chat-2/unread-messages.feature +++ b/tests/integration/features/chat-2/unread-messages.feature @@ -73,6 +73,10 @@ Feature: chat-2/unread-messages Then user "participant2" is participant of room "group room" (v4) | unreadMessages | | 1 | + When user "participant2" reads message "NULL" in room "group room" with 200 + Then user "participant2" is participant of room "group room" (v4) + | unreadMessages | + | 0 | diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php index 00d569fb914a..1c1862a32153 100644 --- a/tests/php/CapabilitiesTest.php +++ b/tests/php/CapabilitiesTest.php @@ -147,6 +147,7 @@ public function setUp(): void { 'delete-messages-unlimited', 'edit-messages', 'silent-send-state', + 'chat-read-last', 'message-expiration', 'reactions', ];