diff options
author | Joas Schilling <coding@schilljs.com> | 2023-09-20 21:54:41 +0200 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2023-09-21 09:58:13 +0200 |
commit | 2e7b140d2e99ffe94120589a8428ab0cb968ac2b (patch) | |
tree | bf7e29efa57b5ddba8b95301ac8e27fa5fd40318 | |
parent | d5be55a14c18e473f12781c7609d3c6c81763ff6 (diff) |
fix(notifications): Allow sessions to mark themselves as inactive
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r-- | appinfo/info.xml | 2 | ||||
-rw-r--r-- | appinfo/routes/routesRoomController.php | 2 | ||||
-rw-r--r-- | docs/capabilities.md | 3 | ||||
-rw-r--r-- | docs/constants.md | 4 | ||||
-rw-r--r-- | docs/participant.md | 12 | ||||
-rw-r--r-- | lib/Capabilities.php | 1 | ||||
-rw-r--r-- | lib/Controller/RoomController.php | 12 | ||||
-rw-r--r-- | lib/Migration/Version18000Date20230920182747.php | 55 | ||||
-rw-r--r-- | lib/Model/Session.php | 13 | ||||
-rw-r--r-- | lib/Service/ParticipantService.php | 7 | ||||
-rw-r--r-- | lib/Service/SessionService.php | 12 | ||||
-rw-r--r-- | tests/integration/features/bootstrap/FeatureContext.php | 17 | ||||
-rw-r--r-- | tests/integration/features/chat/notifications.feature | 16 | ||||
-rw-r--r-- | tests/php/CapabilitiesTest.php | 1 |
14 files changed, 150 insertions, 7 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index 9bb0c815b..3d1b4ff1d 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m ]]></description> - <version>18.0.0-dev.5</version> + <version>18.0.0-dev.6</version> <licence>agpl</licence> <author>Daniel Calviño Sánchez</author> diff --git a/appinfo/routes/routesRoomController.php b/appinfo/routes/routesRoomController.php index 7542b8884..3127831e1 100644 --- a/appinfo/routes/routesRoomController.php +++ b/appinfo/routes/routesRoomController.php @@ -84,6 +84,8 @@ return [ ['name' => 'Room#resendInvitations', 'url' => '/api/{apiVersion}/room/{token}/participants/resend-invitations', 'verb' => 'POST', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::leaveRoom() */ ['name' => 'Room#leaveRoom', 'url' => '/api/{apiVersion}/room/{token}/participants/active', 'verb' => 'DELETE', 'requirements' => $requirementsWithToken], + /** @see \OCA\Talk\Controller\RoomController::setSessionState() */ + ['name' => 'Room#setSessionState', 'url' => '/api/{apiVersion}/room/{token}/participants/state', 'verb' => 'PUT', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::promoteModerator() */ ['name' => 'Room#promoteModerator', 'url' => '/api/{apiVersion}/room/{token}/moderators', 'verb' => 'POST', 'requirements' => $requirementsWithToken], /** @see \OCA\Talk\Controller\RoomController::demoteModerator() */ diff --git a/docs/capabilities.md b/docs/capabilities.md index f953abe00..07f5868eb 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -126,3 +126,6 @@ * `remind-me-later` - Support for "Remind me later" for chat messages exists * `bots-v1` - Support of the first version for Bots and Webhooks is available * `markdown-messages` - Chat messages support markdown and are rendered automatically + +## 18 +* `session-state` - Sessions can mark themselves as inactive, so the participant receives notifications again diff --git a/docs/constants.md b/docs/constants.md index b46977036..f5c4b2105 100644 --- a/docs/constants.md +++ b/docs/constants.md @@ -109,6 +109,10 @@ * `bots` - Used by commands (actor-id is the used `/command`) and the changelog conversation (actor-id is `changelog`) * `bridged` - Users whose messages are bridged in by the [Matterbridge integration](matterbridge.md) +### Session states +* `0` - Inactive (Notifications should still be sent, even though the user has this session in the room) +* `1` - Active (No notifications should be sent) + ## Call ### Start call diff --git a/docs/participant.md b/docs/participant.md index af875420f..a1bd5fce9 100644 --- a/docs/participant.md +++ b/docs/participant.md @@ -160,6 +160,18 @@ + `403 Forbidden` When the current user is not a moderator or owner + `404 Not Found` When the given attendee was not found in the conversation +## Set session state + +* Required capability: `session-state` +* Method: `PUT` +* Endpoint: `/room/{token}/participants/state` + +* Response: + - Status code: + + `200 OK` + + `400 Bad Request` When the provided state is invalid (see [constants list](constants.md#participant-state)) + + `404 Not Found` When the conversation could not be found for the participant + ## Leave a conversation (not available for call and chat anymore) * Method: `DELETE` diff --git a/lib/Capabilities.php b/lib/Capabilities.php index 54e263aff..ec470790b 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -122,6 +122,7 @@ class Capabilities implements IPublicCapability { 'remind-me-later', 'bots-v1', 'markdown-messages', + 'session-state', ], 'config' => [ 'attachments' => [ diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index e4c959845..3016626ea 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -1226,6 +1226,18 @@ class RoomController extends AEnvironmentAwareController { } #[PublicPage] + #[RequireParticipant] + public function setSessionState(int $state): DataResponse { + try { + $this->sessionService->updateSessionState($this->participant->getSession(), $state); + } catch (\InvalidArgumentException $e) { + return new DataResponse([], Http::STATUS_BAD_REQUEST); + } + + return new DataResponse(); + } + + #[PublicPage] public function leaveRoom(string $token): DataResponse { $sessionId = $this->session->getSessionForRoom($token); $this->session->removeSessionForRoom($token); diff --git a/lib/Migration/Version18000Date20230920182747.php b/lib/Migration/Version18000Date20230920182747.php new file mode 100644 index 000000000..6a7d96f26 --- /dev/null +++ b/lib/Migration/Version18000Date20230920182747.php @@ -0,0 +1,55 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2023, Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Talk\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version18000Date20230920182747 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure(): ISchemaWrapper $schemaClosure + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('talk_sessions'); + if (!$table->hasColumn('state')) { + $table->addColumn('state', Types::SMALLINT, [ + 'default' => 1, // active + 'unsigned' => true, + ]); + return $schema; + } + return null; + } +} diff --git a/lib/Model/Session.php b/lib/Model/Session.php index c2c70e8eb..a6309b950 100644 --- a/lib/Model/Session.php +++ b/lib/Model/Session.php @@ -29,10 +29,6 @@ use OCP\AppFramework\Db\Entity; * A session is the "I'm online in this conversation" state of Talk, you get one * when opening the conversation while the inCall flag tells if you are just * online (chatting), or in a call (with audio, camera or even sip). - * Currently it's limited to 1 per attendee, but the plan is to remove this - * restriction in the future, so e.g. in the future you can join with your phone - * on the SIP bridge, have your video/screenshare on the laptop and chat in the - * mobile app. * * @method void setAttendeeId(int $attendeeId) * @method string getAttendeeId() @@ -42,8 +38,13 @@ use OCP\AppFramework\Db\Entity; * @method int getInCall() * @method void setLastPing(int $lastPing) * @method int getLastPing() + * @method void setState(int $state) + * @method int getState() */ class Session extends Entity { + public const STATE_INACTIVE = 0; + public const STATE_ACTIVE = 1; + public const SESSION_TIMEOUT = 30; public const SESSION_TIMEOUT_KILL = self::SESSION_TIMEOUT * 3 + 10; @@ -59,11 +60,15 @@ class Session extends Entity { /** @var int */ protected $lastPing; + /** @var int */ + protected $state; + public function __construct() { $this->addType('attendeeId', 'int'); $this->addType('sessionId', 'string'); $this->addType('inCall', 'int'); $this->addType('lastPing', 'int'); + $this->addType('state', 'int'); } /** diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php index 0f9439796..43cc08610 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -1358,10 +1358,13 @@ class ParticipantService { $helper->selectAttendeesTable($query); $helper->selectSessionsTable($query); $query->from('talk_attendees', 'a') - // Currently we only care if the user has a session at all, so we can select any: #ThisIsFine + // Currently we only care if the user has an active session at all, so we can select any ->leftJoin( 'a', 'talk_sessions', 's', - $query->expr()->eq('s.attendee_id', 'a.id') + $query->expr()->andX( + $query->expr()->eq('s.attendee_id', 'a.id'), + $query->expr()->eq('s.state', $query->createNamedParameter(Session::STATE_ACTIVE, IQueryBuilder::PARAM_INT)) + ) ) ->where($query->expr()->eq('a.room_id', $query->createNamedParameter($room->getId(), IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('a.notification_level', $query->createNamedParameter($notificationLevel, IQueryBuilder::PARAM_INT))); diff --git a/lib/Service/SessionService.php b/lib/Service/SessionService.php index 9ab548926..bb529320e 100644 --- a/lib/Service/SessionService.php +++ b/lib/Service/SessionService.php @@ -66,6 +66,18 @@ class SessionService { } /** + * @throws \InvalidArgumentException + */ + public function updateSessionState(Session $session, int $state): void { + if (!in_array($state, [Session::STATE_INACTIVE, Session::STATE_ACTIVE], true)) { + throw new \InvalidArgumentException('state'); + } + + $session->setState($state); + $this->sessionMapper->update($session); + } + + /** * @param int[] $ids */ public function deleteSessionsById(array $ids): void { diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 2881f3b9a..71f8b9226 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -1065,6 +1065,23 @@ class FeatureContext implements Context, SnippetAcceptingContext { } /** + * @Then /^user "([^"]*)" sets session state to (\d) in room "([^"]*)" with (\d+) \((v4)\)$/ + * + * @param string $user + * @param string $identifier + * @param int $statusCode + * @param string $apiVersion + */ + public function userSessionState(string $user, int $state, string $identifier, int $statusCode, string $apiVersion): void { + $this->setCurrentUser($user); + $this->sendRequest( + 'PUT', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/participants/state', + ['state' => $state] + ); + $this->assertStatusCode($this->response, $statusCode); + } + + /** * @Then /^user "([^"]*)" views call-URL of room "([^"]*)" with (\d+)$/ * * @param string $user diff --git a/tests/integration/features/chat/notifications.feature b/tests/integration/features/chat/notifications.feature index 527f6201d..d35fb2f2f 100644 --- a/tests/integration/features/chat/notifications.feature +++ b/tests/integration/features/chat/notifications.feature @@ -15,6 +15,22 @@ Feature: chat/notifications Then user "participant2" has the following notifications | app | object_type | object_id | subject | + Scenario: Normal message when recipient is online but inactive + When user "participant1" creates room "one-to-one room" (v4) + | roomType | 1 | + | invite | participant2 | + # Join and leave to clear the invite notification + Given user "participant2" joins room "one-to-one room" with 200 (v4) + Given user "participant2" sets session state to 2 in room "one-to-one room" with 400 (v4) + When user "participant1" sends message "Message 1" to room "one-to-one room" with 201 + Given user "participant2" sets session state to 0 in room "one-to-one room" with 200 (v4) + When user "participant1" sends message "Message 2" to room "one-to-one room" with 201 + Given user "participant2" sets session state to 1 in room "one-to-one room" with 200 (v4) + When user "participant1" sends message "Message 3" to room "one-to-one room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + | spreed | chat | one-to-one room/Message 2 | participant1-displayname sent you a private message | + Scenario: Normal message when recipient is offline in the one-to-one When user "participant1" creates room "one-to-one room" (v4) | roomType | 1 | diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php index ccb457121..d168c0259 100644 --- a/tests/php/CapabilitiesTest.php +++ b/tests/php/CapabilitiesTest.php @@ -140,6 +140,7 @@ class CapabilitiesTest extends TestCase { 'remind-me-later', 'bots-v1', 'markdown-messages', + 'session-state', 'message-expiration', 'reactions', ]; |