fix(threads): Expose a flag if a message is part of a thread

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2025-06-26 12:28:12 +02:00
parent b9c022848f
commit 594fad7209
No known key found for this signature in database
GPG key ID: F72FA5B49FFA96B0
5 changed files with 61 additions and 6 deletions

View file

@ -47,6 +47,7 @@ use OCA\Talk\Service\ProxyCacheMessageService;
use OCA\Talk\Service\ReminderService;
use OCA\Talk\Service\RoomFormatter;
use OCA\Talk\Service\SessionService;
use OCA\Talk\Service\ThreadService;
use OCA\Talk\Share\Helper\Preloader;
use OCP\App\IAppManager;
use OCP\AppFramework\Db\DoesNotExistException;
@ -110,6 +111,7 @@ class ChatController extends AEnvironmentAwareOCSController {
protected AttachmentService $attachmentService,
protected AvatarService $avatarService,
protected ReminderService $reminderService,
protected ThreadService $threadService,
private GuestManager $guestManager,
private MessageParser $messageParser,
protected Preloader $sharePreloader,
@ -178,9 +180,10 @@ class ChatController extends AEnvironmentAwareOCSController {
return new DataResponse(null, Http::STATUS_CREATED, $headers);
}
$data = $chatMessage->toArray($this->getResponseFormat());
$isThread = $this->threadService->validateThread((int)$comment->getTopmostParentId());
$data = $chatMessage->toArray($this->getResponseFormat(), $isThread);
if ($parentMessage instanceof Message) {
$data['parent'] = $parentMessage->toArray($this->getResponseFormat());
$data['parent'] = $parentMessage->toArray($this->getResponseFormat(), $isThread);
}
$headers = [];
@ -626,6 +629,8 @@ class ChatController extends AEnvironmentAwareOCSController {
}
$this->sharePreloader->preloadShares($comments);
$potentialThreadIds = array_map(static fn (IComment $comment) => (int)$comment->getTopmostParentId(), $comments);
$threadMap = array_flip($this->threadService->validateThreadIds($potentialThreadIds));
$i = 0;
$now = $this->timeFactory->getDateTime();
@ -650,7 +655,8 @@ class ChatController extends AEnvironmentAwareOCSController {
$parentIds[$id] = $comment->getParentId();
}
$messages[] = $message->toArray($this->getResponseFormat());
$threadId = (int)$comment->getTopmostParentId() ?: $id;
$messages[] = $message->toArray($this->getResponseFormat(), isset($threadMap[$threadId]));
$commentIdToIndex[$id] = $i;
$i++;
}

View file

@ -184,7 +184,7 @@ class Message {
* @psalm-param 'json'|'xml' $format
* @return TalkChatMessage
*/
public function toArray(string $format): array {
public function toArray(string $format, ?bool $isThread = null): array {
$expireDate = $this->getComment()->getExpireDate();
$reactions = $this->getComment()->getReactions();
@ -194,8 +194,11 @@ class Message {
$reactions = new \stdClass();
}
$id = (int)$this->getComment()->getId();
$threadId = (int)$this->getComment()->getTopmostParentId() ?: $id;
$data = [
'id' => (int)$this->getComment()->getId(),
'id' => $id,
'token' => $this->getRoom()->getToken(),
'actorType' => $this->getActorType(),
'actorId' => $this->getActorId(),
@ -210,8 +213,11 @@ class Message {
'reactions' => $reactions,
'expirationTimestamp' => $expireDate ? $expireDate->getTimestamp() : 0,
'markdown' => $this->getMessageType() === ChatManager::VERB_SYSTEM ? false : true,
'threadId' => (int)$this->getComment()->getTopmostParentId(),
'threadId' => $threadId,
];
if ($isThread === true) {
$data['isThread'] = true;
}
if ($this->lastEditActorType && $this->lastEditActorId && $this->lastEditTimestamp) {
$data['lastEditActorType'] = $this->lastEditActorType;

View file

@ -119,6 +119,7 @@ namespace OCA\Talk;
* lastEditTimestamp?: int,
* silent?: bool,
* threadId?: int,
* isThread?: bool,
* }
*
* @psalm-type TalkChatProxyMessage = TalkBaseMessage

View file

@ -126,4 +126,42 @@ class ThreadService {
'last_message_id' => $row['last_message_id'] ?? 0,
];
}
public function validateThreadIds(array $potentialThreadIds): array {
$query = $this->connection->getQueryBuilder();
$query->select('id')
->from('talk_threads')
->where($query->expr()->in(
'id',
$query->createNamedParameter($potentialThreadIds, IQueryBuilder::PARAM_INT_ARRAY),
IQueryBuilder::PARAM_INT_ARRAY)
);
$ids = [];
$result = $query->executeQuery();
while ($row = $result->fetch()) {
$ids[] = (int)$row['id'];
}
$result->closeCursor();
return $ids;
}
public function validateThread(int $potentialThreadId): bool {
$query = $this->connection->getQueryBuilder();
$query->select('id')
->from('talk_threads')
->where($query->expr()->eq(
'id',
$query->createNamedParameter($potentialThreadId, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT)
);
$result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();
return $row !== false;
}
}

View file

@ -30,6 +30,7 @@ use OCA\Talk\Service\ProxyCacheMessageService;
use OCA\Talk\Service\ReminderService;
use OCA\Talk\Service\RoomFormatter;
use OCA\Talk\Service\SessionService;
use OCA\Talk\Service\ThreadService;
use OCA\Talk\Share\Helper\Preloader;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
@ -68,6 +69,7 @@ class ChatControllerTest extends TestCase {
protected AttachmentService&MockObject $attachmentService;
protected AvatarService&MockObject $avatarService;
protected ReminderService&MockObject $reminderService;
protected ThreadService&MockObject $threadService;
protected GuestManager&MockObject $guestManager;
protected MessageParser&MockObject $messageParser;
protected Preloader&MockObject $sharePreloader;
@ -111,6 +113,7 @@ class ChatControllerTest extends TestCase {
$this->attachmentService = $this->createMock(AttachmentService::class);
$this->avatarService = $this->createMock(AvatarService::class);
$this->reminderService = $this->createMock(ReminderService::class);
$this->threadService = $this->createMock(ThreadService::class);
$this->guestManager = $this->createMock(GuestManager::class);
$this->messageParser = $this->createMock(MessageParser::class);
$this->sharePreloader = $this->createMock(Preloader::class);
@ -161,6 +164,7 @@ class ChatControllerTest extends TestCase {
$this->attachmentService,
$this->avatarService,
$this->reminderService,
$this->threadService,
$this->guestManager,
$this->messageParser,
$this->sharePreloader,