diff --git a/lib/Dashboard/TalkWidget.php b/lib/Dashboard/TalkWidget.php index a5feb0b6c73..f30bf74ea12 100644 --- a/lib/Dashboard/TalkWidget.php +++ b/lib/Dashboard/TalkWidget.php @@ -31,12 +31,14 @@ use OCA\Talk\Config; use OCA\Talk\Manager; use OCA\Talk\Model\BreakoutRoom; +use OCA\Talk\Model\Message; use OCA\Talk\Participant; use OCA\Talk\Room; use OCA\Talk\Service\AvatarService; use OCA\Talk\Service\ParticipantService; +use OCA\Talk\Service\ProxyCacheMessageService; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Comments\IComment; use OCP\Dashboard\IAPIWidget; use OCP\Dashboard\IButtonWidget; use OCP\Dashboard\IConditionalWidget; @@ -64,6 +66,7 @@ public function __construct( protected ParticipantService $participantService, protected MessageParser $messageParser, protected ChatManager $chatManager, + protected ProxyCacheMessageService $pcmService, protected ITimeFactory $timeFactory, ) { } @@ -156,13 +159,13 @@ public function getItems(string $userId, ?string $since = null, int $limit = 7): $participant = $this->participantService->getParticipant($room, $userId); $attendee = $participant->getAttendee(); - if ($attendee->getLastMentionMessage() > $attendee->getLastReadMessage()) { + if (($room->isFederatedConversation() && $attendee->getLastMentionMessage()) + || (!$room->isFederatedConversation() && $attendee->getLastMentionMessage() > $attendee->getLastReadMessage())) { return true; } return ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) - && $room->getLastMessage() - && $room->getLastMessage()->getId() > $attendee->getLastReadMessage() + && $room->getLastMessageId() > $attendee->getLastReadMessage() && $this->chatManager->getUnreadCount($room, $attendee->getLastReadMessage()) > 0; }); @@ -200,14 +203,15 @@ public function getItemsV2(string $userId, ?string $since = null, int $limit = 7 continue; } - if ($attendee->getLastMentionMessage() > $attendee->getLastReadMessage()) { + if (($room->isFederatedConversation() && $attendee->getLastMentionMessage()) + || (!$room->isFederatedConversation() && $attendee->getLastMentionMessage() > $attendee->getLastReadMessage())) { // Really mentioned $mentions[] = $room; continue; } if (($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) - && $room->getLastMessage()?->getId() > $attendee->getLastReadMessage()) { + && $room->getLastMessageId() > $attendee->getLastReadMessage()) { // If there are "unread" messages in one-to-one or former one-to-one // we check if they are actual messages or system messages not // considered by the read-marker @@ -242,33 +246,29 @@ protected function prepareRoom(Room $room, string $userId): WidgetItem { $participant = $this->participantService->getParticipant($room, $userId); $subtitle = ''; - $lastMessage = $room->getLastMessage(); - if ($lastMessage instanceof IComment) { + if ($room->getLastMessageId() && $room->isFederatedConversation()) { + try { + $cachedMessage = $this->pcmService->findByRemote( + $room->getRemoteServer(), + $room->getRemoteToken(), + $room->getLastMessageId(), + ); + $message = $this->messageParser->createMessageFromProxyCache($room, $participant, $cachedMessage, $this->l10n); + $subtitle = $this->getSubtitleFromMessage($message); + } catch (DoesNotExistException $e) { + // Fallback to empty subtitle + } + } elseif ($room->getLastMessageId() && !$room->isFederatedConversation()) { $message = $this->messageParser->createMessage($room, $participant, $room->getLastMessage(), $this->l10n); $this->messageParser->parseMessage($message); - - $now = $this->timeFactory->getDateTime(); - $expireDate = $message->getComment()->getExpireDate(); - if ((!$expireDate instanceof \DateTime || $expireDate >= $now) - && $message->getVisibility()) { - $placeholders = $replacements = []; - - foreach ($message->getMessageParameters() as $placeholder => $parameter) { - $placeholders[] = '{' . $placeholder . '}'; - if ($parameter['type'] === 'user' || $parameter['type'] === 'guest') { - $replacements[] = '@' . $parameter['name']; - } else { - $replacements[] = $parameter['name']; - } - } - - $subtitle = str_replace($placeholders, $replacements, $message->getMessage()); - } + $subtitle = $this->getSubtitleFromMessage($message); } + $attendee = $participant->getAttendee(); if ($room->getCallFlag() !== Participant::FLAG_DISCONNECTED) { $subtitle = $this->l10n->t('Call in progress'); - } elseif ($participant->getAttendee()->getLastMentionMessage() > $participant->getAttendee()->getLastReadMessage()) { + } elseif (($room->isFederatedConversation() && $attendee->getLastMentionMessage()) + || (!$room->isFederatedConversation() && $attendee->getLastMentionMessage() > $attendee->getLastReadMessage())) { $subtitle = $this->l10n->t('You were mentioned'); } @@ -280,6 +280,30 @@ protected function prepareRoom(Room $room, string $userId): WidgetItem { ); } + protected function getSubtitleFromMessage(Message $message): string { + $expireDate = $message->getExpirationDateTime(); + if ($expireDate instanceof \DateTimeInterface + && $expireDate <= $this->timeFactory->getDateTime()) { + return ''; + } + + if (!$message->getVisibility()) { + return ''; + } + + $placeholders = $replacements = []; + foreach ($message->getMessageParameters() as $placeholder => $parameter) { + $placeholders[] = '{' . $placeholder . '}'; + if ($parameter['type'] === 'user' || $parameter['type'] === 'guest') { + $replacements[] = '@' . $parameter['name']; + } else { + $replacements[] = $parameter['name']; + } + } + + return str_replace($placeholders, $replacements, $message->getMessage()); + } + protected function sortRooms(Room $roomA, Room $roomB): int { if ($roomA->getCallFlag() !== $roomB->getCallFlag()) { return $roomA->getCallFlag() !== Participant::FLAG_DISCONNECTED ? -1 : 1; diff --git a/lib/Manager.php b/lib/Manager.php index 5b6f7c1838d..e4be038c654 100644 --- a/lib/Manager.php +++ b/lib/Manager.php @@ -1326,7 +1326,10 @@ public function isGuestUser(string $userId): bool { } protected function loadLastMessageInfo(IQueryBuilder $query): void { - $query->leftJoin('r', 'comments', 'c', $query->expr()->eq('r.last_message', 'c.id')); + $query->leftJoin('r', 'comments', 'c', $query->expr()->andX( + $query->expr()->eq('r.last_message', 'c.id'), + $query->expr()->emptyString('r.remote_server'), + )); $query->selectAlias('c.id', 'comment_id'); $query->selectAlias('c.parent_id', 'comment_parent_id'); $query->selectAlias('c.topmost_parent_id', 'comment_topmost_parent_id'); diff --git a/lib/Room.php b/lib/Room.php index 0f2f5cfce43..2ff0872afb2 100644 --- a/lib/Room.php +++ b/lib/Room.php @@ -356,7 +356,11 @@ public function setLastMessageId(int $lastMessageId): void { } public function getLastMessage(): ?IComment { - if ($this->lastMessageId && $this->lastMessage === null && !$this->isFederatedConversation()) { + if ($this->isFederatedConversation()) { + return null; + } + + if ($this->lastMessageId && $this->lastMessage === null) { $this->lastMessage = $this->manager->loadLastCommentInfo($this->lastMessageId); if ($this->lastMessage === null) { $this->lastMessageId = 0; diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 8104c2ef672..6888e9ba4ac 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -2357,12 +2357,14 @@ public function userGetsDashboardWidgetItems($user, $widgetId, $apiVersion = 'v1 $actualItems = $data[$widgetId]['items']; } + $actualItems = array_filter($actualItems, static fn ($item) => $item['title'] !== 'Note to self' && $item['title'] !== 'Talk updates ✅'); + if (empty($expectedItems)) { Assert::assertEmpty($actualItems); return; } - Assert::assertCount(count($expectedItems), $actualItems); + Assert::assertCount(count($expectedItems), $actualItems, json_encode($actualItems, JSON_PRETTY_PRINT)); foreach ($expectedItems as $key => $item) { $token = self::$identifierToToken[$item['link']]; diff --git a/tests/integration/features/federation/chat.feature b/tests/integration/features/federation/chat.feature index 67b8160fb41..de9aee824f9 100644 --- a/tests/integration/features/federation/chat.feature +++ b/tests/integration/features/federation/chat.feature @@ -246,7 +246,18 @@ Feature: federation/chat Given user "participant2" joins room "LOCAL::room" with 200 (v4) Given user "participant2" leaves room "LOCAL::room" with 200 (v4) And user "participant2" sends message "Message 1" to room "LOCAL::room" with 201 + Then user "participant2" sees the following entries for dashboard widgets "spreed" (v1) + | title | subtitle | link | iconUrl | sinceId | overlayIconUrl | + Then user "participant2" sees the following entries for dashboard widgets "spreed" (v2) + | title | subtitle | link | iconUrl | sinceId | overlayIconUrl | + | room | Message 1 | LOCAL::room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | When user "participant1" sends reply "Message 1-1" on message "Message 1" to room "room" with 201 + Then user "participant2" sees the following entries for dashboard widgets "spreed" (v1) + | title | subtitle | link | iconUrl | sinceId | overlayIconUrl | + | room | You were mentioned | LOCAL::room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | + Then user "participant2" sees the following entries for dashboard widgets "spreed" (v2) + | title | subtitle | link | iconUrl | sinceId | overlayIconUrl | + | room | You were mentioned | LOCAL::room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | And user "participant1" sends message 'Hi @"federated_user/participant2@{$REMOTE_URL}" bye' to room "room" with 201 And user "participant1" sends message 'Hi @all bye' to room "room" with 201 Then user "participant2" has the following notifications