Browse Source
			
			
			Merge pull request #6730 from nextcloud/feature/1920/api-reactions-to-message
			
				
		Merge pull request #6730 from nextcloud/feature/1920/api-reactions-to-message
	
		
	
			
				Api reactions to messagepull/6912/head
							committed by
							
								 GitHub
								GitHub
							
						
					
				
				
				  
				  No known key found for this signature in database
				  
				  	
						GPG Key ID: 4AEE18F83AFDEB23
				  	
				  
				
			
		
		
		
	
				 24 changed files with 933 additions and 16 deletions
			
			
		- 
					130.drone.yml
- 
					1appinfo/routes.php
- 
					41appinfo/routes/routesReactionController.php
- 
					1docs/capabilities.md
- 
					2docs/chat.md
- 
					1docs/index.md
- 
					66docs/reaction.md
- 
					9lib/Capabilities.php
- 
					44lib/Chat/Notifier.php
- 
					12lib/Chat/Parser/Listener.php
- 
					52lib/Chat/Parser/ReactionParser.php
- 
					153lib/Chat/ReactionManager.php
- 
					130lib/Controller/ReactionController.php
- 
					29lib/Exceptions/ReactionAlreadyExistsException.php
- 
					29lib/Exceptions/ReactionNotSupportedException.php
- 
					29lib/Exceptions/ReactionOutOfContextException.php
- 
					2lib/Manager.php
- 
					2lib/Model/Message.php
- 
					23lib/Notification/Notifier.php
- 
					1mkdocs.yml
- 
					63tests/integration/features/bootstrap/FeatureContext.php
- 
					68tests/integration/features/reaction/react.feature
- 
					11tests/php/CapabilitiesTest.php
- 
					50tests/php/Chat/NotifierTest.php
| @ -0,0 +1,41 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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/>. | |||
|  * | |||
|  */ | |||
| 
 | |||
| return [ | |||
| 	'ocs' => [ | |||
| 		['name' => 'Reaction#react', 'url' => '/api/{apiVersion}/reaction/{token}/{messageId}', 'verb' => 'POST', 'requirements' => [ | |||
| 			'apiVersion' => 'v1', | |||
| 			'token' => '^[a-z0-9]{4,30}$', | |||
| 		]], | |||
| 		['name' => 'Reaction#delete', 'url' => '/api/{apiVersion}/reaction/{token}/{messageId}', 'verb' => 'DELETE', 'requirements' => [ | |||
| 			'apiVersion' => 'v1', | |||
| 			'token' => '^[a-z0-9]{4,30}$', | |||
| 		]], | |||
| 		['name' => 'Reaction#getReactions', 'url' => '/api/{apiVersion}/reaction/{token}/{messageId}', 'verb' => 'GET', 'requirements' => [ | |||
| 			'apiVersion' => 'v1', | |||
| 			'token' => '^[a-z0-9]{4,30}$', | |||
| 		]], | |||
| 	], | |||
| ]; | |||
| @ -0,0 +1,66 @@ | |||
| # Reaction API | |||
| 
 | |||
| Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1` | |||
| 
 | |||
| ## React to a message | |||
| 
 | |||
| * Required capability: `reactions` | |||
| * Method: `POST` | |||
| * Endpoint: `/reaction/{token}/{messageId}` | |||
| * Data: | |||
| 
 | |||
|     field | type | Description | |||
|     ---|---|--- | |||
|     `reaction` | string | the reaction emoji | |||
| 
 | |||
| * Response: | |||
|     - Status code: | |||
|         + `200 OK` Reaction already exists | |||
|         + `201 Created` | |||
|         + `400 Bad Request` In case of no reaction support, message out of reactions context or any other error | |||
|         + `404 Not Found` When the conversation or message to react could not be found for the participant | |||
|         + `409 Conflict` User already did this reaction to this message | |||
| 
 | |||
| ## Delete a reaction | |||
| 
 | |||
| * Required capability: `reactions` | |||
| * Method: `DELETE` | |||
| * Endpoint: `/reaction/{token}/{messageId}` | |||
| * Data: | |||
| 
 | |||
|     field | type | Description | |||
|     ---|---|--- | |||
|     `reaction` | string | the reaction emoji | |||
| 
 | |||
| * Response: | |||
|     - Status code: | |||
|         + `201 Created` | |||
|         + `400 Bad Request` In case of no reaction support, message out of reactions context or any other error | |||
|         + `404 Not Found` When the conversation or message to react or reaction could not be found for the participant | |||
| 
 | |||
| ## Retrieve reactions of a message by type | |||
| 
 | |||
| * Required capability: `reactions` | |||
| * Method: `GET` | |||
| * Endpoint: `/reaction/{token}/{messageId}` | |||
| * Data: | |||
| 
 | |||
|     field | type | Description | |||
|     ---|---|--- | |||
|     `reaction` | string | **Optional:** the reaction emoji | |||
| 
 | |||
| * Response: | |||
|     - Status code: | |||
|         + `200 OK` | |||
|         + `400 Bad Request` In case of no reaction support, message out of reactions context or any other error | |||
|         + `404 Not Found` When the conversation or message to react could not be found for the participant | |||
| 
 | |||
|     - Data: | |||
|         Array with data of reactions: | |||
| 
 | |||
|         field | type | Description | |||
|         ---|---|--- | |||
|         `actorType` | string | `guests` or `users` | |||
|         `actorId` | string | Actor id of the reacting participant | |||
|         `actorDisplayName` | string | Display name of the reaction author | |||
|         `timestamp` | int | Timestamp in seconds and UTC time zone | |||
| @ -0,0 +1,52 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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\Chat\Parser; | |||
| 
 | |||
| use OCA\Talk\Model\Message; | |||
| use OCP\IL10N; | |||
| 
 | |||
| class ReactionParser { | |||
| 	/** @var IL10N|null */ | |||
| 	private $l; | |||
| 	/** | |||
| 	 * @param Message $message | |||
| 	 * @throws \OutOfBoundsException | |||
| 	 */ | |||
| 	public function parseMessage(Message $message): void { | |||
| 		$comment = $message->getComment(); | |||
| 		if (!in_array($comment->getVerb(), ['reaction', 'reaction_deleted'])) { | |||
| 			throw new \OutOfBoundsException('Not a reaction'); | |||
| 		} | |||
| 		$this->l = $message->getL10n(); | |||
| 		$message->setMessageType('system'); | |||
| 		if ($comment->getVerb() === 'reaction_deleted') { | |||
| 			// This message is necessary to make compatible with old clients
 | |||
| 			$message->setMessage($this->l->t('Message deleted by author'), [], $comment->getVerb()); | |||
| 		} else { | |||
| 			$message->setMessage($message->getMessage(), [], $comment->getVerb()); | |||
| 		} | |||
| 	} | |||
| } | |||
| @ -0,0 +1,153 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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\Chat; | |||
| 
 | |||
| use OCA\Talk\Exceptions\ReactionAlreadyExistsException; | |||
| use OCA\Talk\Exceptions\ReactionNotSupportedException; | |||
| use OCA\Talk\Exceptions\ReactionOutOfContextException; | |||
| use OCA\Talk\Participant; | |||
| use OCA\Talk\Room; | |||
| use OCP\AppFramework\Utility\ITimeFactory; | |||
| use OCP\Comments\IComment; | |||
| use OCP\Comments\ICommentsManager; | |||
| use OCP\Comments\NotFoundException; | |||
| use OCP\IL10N; | |||
| 
 | |||
| class ReactionManager { | |||
| 	/** @var ICommentsManager|CommentsManager */ | |||
| 	private $commentsManager; | |||
| 	/** @var IL10N */ | |||
| 	private $l; | |||
| 	/** @var MessageParser */ | |||
| 	private $messageParser; | |||
| 	/** @var Notifier */ | |||
| 	private $notifier; | |||
| 	/** @var ITimeFactory */ | |||
| 	protected $timeFactory; | |||
| 
 | |||
| 	public function __construct(CommentsManager $commentsManager, | |||
| 								IL10N $l, | |||
| 								MessageParser $messageParser, | |||
| 								Notifier $notifier, | |||
| 								ITimeFactory $timeFactory) { | |||
| 		$this->commentsManager = $commentsManager; | |||
| 		$this->l = $l; | |||
| 		$this->messageParser = $messageParser; | |||
| 		$this->notifier = $notifier; | |||
| 		$this->timeFactory = $timeFactory; | |||
| 	} | |||
| 
 | |||
| 	public function addReactionMessage(Room $chat, Participant $participant, IComment $parentMessage, string $reaction): IComment { | |||
| 		try { | |||
| 			// Check if the user already reacted with the same reaction
 | |||
| 			$comment = $this->commentsManager->getReactionComment( | |||
| 				(int) $parentMessage->getId(), | |||
| 				$participant->getAttendee()->getActorType(), | |||
| 				$participant->getAttendee()->getActorId(), | |||
| 				$reaction | |||
| 			); | |||
| 			throw new ReactionAlreadyExistsException(); | |||
| 		} catch (NotFoundException $e) { | |||
| 		} | |||
| 
 | |||
| 		$comment = $this->commentsManager->create( | |||
| 			$participant->getAttendee()->getActorType(), | |||
| 			$participant->getAttendee()->getActorId(), | |||
| 			'chat', | |||
| 			(string) $chat->getId() | |||
| 		); | |||
| 		$comment->setParentId((string) $parentMessage->getId()); | |||
| 		$comment->setMessage($reaction); | |||
| 		$comment->setVerb('reaction'); | |||
| 		$this->commentsManager->save($comment); | |||
| 
 | |||
| 		$this->notifier->notifyReacted($chat, $parentMessage, $comment); | |||
| 		return $comment; | |||
| 	} | |||
| 
 | |||
| 	public function deleteReactionMessage(Participant $participant, int $messageId, string $reaction): IComment { | |||
| 		$comment = $this->commentsManager->getReactionComment( | |||
| 			$messageId, | |||
| 			$participant->getAttendee()->getActorType(), | |||
| 			$participant->getAttendee()->getActorId(), | |||
| 			$reaction | |||
| 		); | |||
| 		$comment->setMessage( | |||
| 			json_encode([ | |||
| 				'deleted_by_type' => $participant->getAttendee()->getActorType(), | |||
| 				'deleted_by_id' => $participant->getAttendee()->getActorId(), | |||
| 				'deleted_on' => $this->timeFactory->getDateTime()->getTimestamp(), | |||
| 			]) | |||
| 		); | |||
| 		$comment->setVerb('reaction_deleted'); | |||
| 		$this->commentsManager->save($comment); | |||
| 		return $comment; | |||
| 	} | |||
| 
 | |||
| 	public function retrieveReactionMessages(Room $chat, Participant $participant, int $messageId, ?string $reaction): array { | |||
| 		if ($reaction) { | |||
| 			$comments = $this->commentsManager->retrieveAllReactionsWithSpecificReaction($messageId, $reaction); | |||
| 		} else { | |||
| 			$comments = $this->commentsManager->retrieveAllReactions($messageId); | |||
| 		} | |||
| 
 | |||
| 		foreach ($comments as $comment) { | |||
| 			$message = $this->messageParser->createMessage($chat, $participant, $comment, $this->l); | |||
| 			$this->messageParser->parseMessage($message); | |||
| 
 | |||
| 			$reactions[$comment->getMessage()][] = [ | |||
| 				'actorType' => $comment->getActorType(), | |||
| 				'actorId' => $comment->getActorId(), | |||
| 				'actorDisplayName' => $message->getActorDisplayName(), | |||
| 				'timestamp' => $comment->getCreationDateTime()->getTimestamp(), | |||
| 			]; | |||
| 		} | |||
| 		return $reactions; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * @param Room $chat | |||
| 	 * @param string $messageId | |||
| 	 * @return IComment | |||
| 	 * @throws NotFoundException | |||
| 	 * @throws ReactionNotSupportedException | |||
| 	 * @throws ReactionOutOfContextException | |||
| 	 */ | |||
| 	public function getCommentToReact(Room $chat, string $messageId): IComment { | |||
| 		if (!$this->commentsManager->supportReactions()) { | |||
| 			throw new ReactionNotSupportedException(); | |||
| 		} | |||
| 		$comment = $this->commentsManager->get($messageId); | |||
| 
 | |||
| 		if ($comment->getObjectType() !== 'chat' | |||
| 			|| $comment->getObjectId() !== (string) $chat->getId() | |||
| 			|| $comment->getVerb() !== 'comment') { | |||
| 			throw new ReactionOutOfContextException(); | |||
| 		} | |||
| 
 | |||
| 		return $comment; | |||
| 	} | |||
| } | |||
| @ -0,0 +1,130 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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\Controller; | |||
| 
 | |||
| use OCA\Talk\Chat\ReactionManager; | |||
| use OCA\Talk\Exceptions\ReactionAlreadyExistsException; | |||
| use OCA\Talk\Exceptions\ReactionNotSupportedException; | |||
| use OCA\Talk\Exceptions\ReactionOutOfContextException; | |||
| use OCP\AppFramework\Http; | |||
| use OCP\AppFramework\Http\DataResponse; | |||
| use OCP\Comments\NotFoundException; | |||
| use OCP\IRequest; | |||
| 
 | |||
| class ReactionController extends AEnvironmentAwareController { | |||
| 	/** @var ReactionManager */ | |||
| 	private $reactionManager; | |||
| 
 | |||
| 	public function __construct(string $appName, | |||
| 								IRequest $request, | |||
| 								ReactionManager $reactionManager) { | |||
| 		parent::__construct($appName, $request); | |||
| 		$this->reactionManager = $reactionManager; | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * @NoAdminRequired | |||
| 	 * @RequireParticipant | |||
| 	 * @RequireReadWriteConversation | |||
| 	 * @RequireModeratorOrNoLobby | |||
| 	 * | |||
| 	 * @param int $messageId for reaction | |||
| 	 * @param string $reaction the reaction emoji | |||
| 	 * @return DataResponse | |||
| 	 */ | |||
| 	public function react(int $messageId, string $reaction): DataResponse { | |||
| 		try { | |||
| 			$chat = $this->getRoom(); | |||
| 			$participant = $this->getParticipant(); | |||
| 			$parentMessage = $this->reactionManager->getCommentToReact($chat, (string) $messageId); | |||
| 			$this->reactionManager->addReactionMessage($chat, $participant, $parentMessage, $reaction); | |||
| 		} catch (NotFoundException $e) { | |||
| 			return new DataResponse([], Http::STATUS_NOT_FOUND); | |||
| 		} catch (ReactionAlreadyExistsException $e) { | |||
| 			return new DataResponse([], Http::STATUS_OK); | |||
| 		} catch (ReactionNotSupportedException | ReactionOutOfContextException | \Exception $e) { | |||
| 			return new DataResponse([], Http::STATUS_BAD_REQUEST); | |||
| 		} | |||
| 		return new DataResponse([], Http::STATUS_CREATED); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * @NoAdminRequired | |||
| 	 * @RequireParticipant | |||
| 	 * @RequireReadWriteConversation | |||
| 	 * @RequireModeratorOrNoLobby | |||
| 	 * | |||
| 	 * @param int $messageId for reaction | |||
| 	 * @param string $reaction the reaction emoji | |||
| 	 * @return DataResponse | |||
| 	 */ | |||
| 	public function delete(int $messageId, string $reaction): DataResponse { | |||
| 		$participant = $this->getParticipant(); | |||
| 		try { | |||
| 			// Verify that messageId is part of the room
 | |||
| 			$this->reactionManager->getCommentToReact($this->getRoom(), (string) $messageId); | |||
| 		} catch (ReactionNotSupportedException | ReactionOutOfContextException | NotFoundException $e) { | |||
| 			return new DataResponse([], Http::STATUS_NOT_FOUND); | |||
| 		} | |||
| 
 | |||
| 		try { | |||
| 			$this->reactionManager->deleteReactionMessage( | |||
| 				$participant, | |||
| 				$messageId, | |||
| 				$reaction | |||
| 			); | |||
| 		} catch (NotFoundException $e) { | |||
| 			return new DataResponse([], Http::STATUS_NOT_FOUND); | |||
| 		} catch (\Exception $e) { | |||
| 			return new DataResponse([], Http::STATUS_BAD_REQUEST); | |||
| 		} | |||
| 
 | |||
| 		return new DataResponse([], Http::STATUS_OK); | |||
| 	} | |||
| 
 | |||
| 	/** | |||
| 	 * @NoAdminRequired | |||
| 	 * @RequireParticipant | |||
| 	 * @RequireReadWriteConversation | |||
| 	 * @RequireModeratorOrNoLobby | |||
| 	 * | |||
| 	 * @param int $messageId for reaction | |||
| 	 * @param string|null $reaction the reaction emoji | |||
| 	 * @return DataResponse | |||
| 	 */ | |||
| 	public function getReactions(int $messageId, ?string $reaction): DataResponse { | |||
| 		try { | |||
| 			// Verify that messageId is part of the room
 | |||
| 			$this->reactionManager->getCommentToReact($this->getRoom(), (string) $messageId); | |||
| 		} catch (ReactionNotSupportedException | ReactionOutOfContextException | NotFoundException $e) { | |||
| 			return new DataResponse([], Http::STATUS_NOT_FOUND); | |||
| 		} | |||
| 
 | |||
| 		$reactions = $this->reactionManager->retrieveReactionMessages($this->getRoom(), $this->getParticipant(), $messageId, $reaction); | |||
| 
 | |||
| 		return new DataResponse($reactions, Http::STATUS_OK); | |||
| 	} | |||
| } | |||
| @ -0,0 +1,29 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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\Exceptions; | |||
| 
 | |||
| class ReactionAlreadyExistsException extends \OutOfBoundsException { | |||
| } | |||
| @ -0,0 +1,29 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2022, Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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\Exceptions; | |||
| 
 | |||
| class ReactionNotSupportedException extends \Exception { | |||
| } | |||
| @ -0,0 +1,29 @@ | |||
| <?php | |||
| 
 | |||
| declare(strict_types=1); | |||
| /** | |||
|  * @copyright Copyright (c) 2022, Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @author Vitor Mattos <vitor@php.rio> | |||
|  * | |||
|  * @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\Exceptions; | |||
| 
 | |||
| class ReactionOutOfContextException extends \Exception { | |||
| } | |||
| @ -0,0 +1,68 @@ | |||
| Feature: reaction/react | |||
|   Background: | |||
|     Given user "participant1" exists | |||
|     Given user "participant2" exists | |||
| 
 | |||
|   Scenario: React to message with success | |||
|     Given user "participant1" creates room "room" (v4) | |||
|       | roomType | 3 | | |||
|       | roomName | room | | |||
|     And user "participant1" adds user "participant2" to room "room" with 200 (v4) | |||
|     And user "participant1" sends message "Message 1" to room "room" with 201 | |||
|     And user "participant2" react with "👍" on message "Message 1" to room "room" with 201 | |||
|     Then user "participant1" sees the following messages in room "room" with 200 | |||
|       | room | actorType | actorId      | actorDisplayName         | message   | messageParameters | reactions | | |||
|       | room | users     | participant1 | participant1-displayname | Message 1 | []                | {"👍":1}  | | |||
|     And user "participant1" react with "👍" on message "Message 1" to room "room" with 201 | |||
|     Then user "participant1" sees the following messages in room "room" with 200 | |||
|       | room | actorType | actorId      | actorDisplayName         | message   | messageParameters | reactions | | |||
|       | room | users     | participant1 | participant1-displayname | Message 1 | []                | {"👍":2}  | | |||
| 
 | |||
|   Scenario: React two times to same message with the same reaction | |||
|     Given user "participant1" creates room "room" (v4) | |||
|       | roomType | 3 | | |||
|       | roomName | room | | |||
|     And user "participant1" adds user "participant2" to room "room" with 200 (v4) | |||
|     And user "participant1" sends message "Message 1" to room "room" with 201 | |||
|     And user "participant2" react with "👍" on message "Message 1" to room "room" with 201 | |||
|     And user "participant2" react with "👍" on message "Message 1" to room "room" with 200 | |||
|     Then user "participant1" sees the following messages in room "room" with 200 | |||
|       | room | actorType | actorId      | actorDisplayName         | message   | messageParameters | reactions | | |||
|       | room | users     | participant1 | participant1-displayname | Message 1 | []                | {"👍":1}  | | |||
| 
 | |||
|   Scenario: Delete reaction to message with success | |||
|     Given user "participant1" creates room "room" (v4) | |||
|       | roomType | 3 | | |||
|       | roomName | room | | |||
|     And user "participant1" adds user "participant2" to room "room" with 200 (v4) | |||
|     And user "participant1" sends message "Message 1" to room "room" with 201 | |||
|     And user "participant2" react with "👍" on message "Message 1" to room "room" with 201 | |||
|     Then user "participant1" sees the following messages in room "room" with 200 | |||
|       | room | actorType | actorId      | actorDisplayName         | message   | messageParameters | reactions | | |||
|       | room | users     | participant1 | participant1-displayname | Message 1 | []                | {"👍":1}  | | |||
|     And user "participant2" delete react with "👍" on message "Message 1" to room "room" with 200 | |||
|     Then user "participant1" sees the following messages in room "room" with 200 | |||
|       | room | actorType | actorId      | actorDisplayName         | message   | messageParameters | reactions | | |||
|       | room | users     | participant1 | participant1-displayname | Message 1 | []                | []        | | |||
| 
 | |||
|   Scenario: Retrieve reactions of a message | |||
|     Given user "participant1" creates room "room" (v4) | |||
|       | roomType | 3 | | |||
|       | roomName | room | | |||
|     And user "participant1" adds user "participant2" to room "room" with 200 (v4) | |||
|     And user "participant1" sends message "Message 1" to room "room" with 201 | |||
|     And user "participant1" react with "👍" on message "Message 1" to room "room" with 201 | |||
|     And user "participant2" react with "👍" on message "Message 1" to room "room" with 201 | |||
|     Then user "participant1" retrieve reactions "👍" of message "Message 1" in room "room" with 200 | |||
|       | actorType | actorId      | actorDisplayName         | reaction | | |||
|       | users     | participant1 | participant1-displayname | 👍       | | |||
|       | users     | participant2 | participant2-displayname | 👍       | | |||
|     And user "participant2" react with "👎" on message "Message 1" to room "room" with 201 | |||
|     And user "participant1" retrieve reactions "👎" of message "Message 1" in room "room" with 200 | |||
|       | actorType | actorId      | actorDisplayName         | reaction | | |||
|       | users     | participant2 | participant2-displayname | 👎       | | |||
|     And user "participant1" retrieve reactions "all" of message "Message 1" in room "room" with 200 | |||
|       | actorType | actorId      | actorDisplayName         | reaction | | |||
|       | users     | participant1 | participant1-displayname | 👍       | | |||
|       | users     | participant2 | participant2-displayname | 👎       | | |||
|       | users     | participant2 | participant2-displayname | 👍       | | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue