fix(threds): Add thread info to messages

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2025-09-02 13:57:51 +02:00
parent a6ae1be1af
commit 22f876b199
No known key found for this signature in database
GPG key ID: F72FA5B49FFA96B0
7 changed files with 91 additions and 53 deletions

View file

@ -181,10 +181,14 @@ class ChatController extends AEnvironmentAwareOCSController {
return new DataResponse(null, Http::STATUS_CREATED, $headers);
}
$isThread = $this->threadService->validateThread($this->room->getId(), (int)$comment->getTopmostParentId());
$data = $chatMessage->toArray($this->getResponseFormat(), $isThread);
try {
$thread = $this->threadService->findByThreadId($this->room->getId(), (int)$comment->getTopmostParentId());
} catch (DoesNotExistException) {
$thread = null;
}
$data = $chatMessage->toArray($this->getResponseFormat(), $thread);
if ($parentMessage instanceof Message) {
$data['parent'] = $parentMessage->toArray($this->getResponseFormat(), $isThread);
$data['parent'] = $parentMessage->toArray($this->getResponseFormat(), $thread);
}
$headers = [];
@ -679,7 +683,7 @@ 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($this->room->getId(), $potentialThreadIds));
$threads = $this->threadService->findByThreadIds($this->room->getId(), $potentialThreadIds);
$i = 0;
$now = $this->timeFactory->getDateTime();
@ -705,7 +709,7 @@ class ChatController extends AEnvironmentAwareOCSController {
}
$threadId = (int)$comment->getTopmostParentId() ?: $id;
$messages[] = $message->toArray($this->getResponseFormat(), isset($threadMap[$threadId]));
$messages[] = $message->toArray($this->getResponseFormat(), $threads[$threadId] ?? null);
$commentIdToIndex[$id] = $i;
$i++;
}
@ -742,7 +746,7 @@ class ChatController extends AEnvironmentAwareOCSController {
if ($message->getVisibility()) {
$threadId = (int)$comment->getTopmostParentId() ?: $parentId;
$loadedParents[$parentId] = $message->toArray($this->getResponseFormat(), isset($threadMap[$threadId]));
$loadedParents[$parentId] = $message->toArray($this->getResponseFormat(), $threads[$threadId] ?? null);
$messages[$commentKey]['parent'] = $loadedParents[$parentId];
continue;
}
@ -979,8 +983,14 @@ class ChatController extends AEnvironmentAwareOCSController {
$message = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
$this->messageParser->parseMessage($message);
$data = $systemMessage->toArray($this->getResponseFormat());
$data['parent'] = $message->toArray($this->getResponseFormat());
try {
$thread = $this->threadService->findByThreadId($this->room->getId(), (int)$comment->getTopmostParentId());
} catch (DoesNotExistException) {
$thread = null;
}
$data = $systemMessage->toArray($this->getResponseFormat(), $thread);
$data['parent'] = $message->toArray($this->getResponseFormat(), $thread);
$hasBotOrBridge = !empty($this->botService->getBotsForToken($this->room->getToken(), Bot::FEATURE_WEBHOOK));
if (!$hasBotOrBridge) {
@ -1096,8 +1106,14 @@ class ChatController extends AEnvironmentAwareOCSController {
$parseMessage = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
$this->messageParser->parseMessage($parseMessage);
$data = $systemMessage->toArray($this->getResponseFormat());
$data['parent'] = $parseMessage->toArray($this->getResponseFormat());
try {
$thread = $this->threadService->findByThreadId($this->room->getId(), (int)$comment->getTopmostParentId());
} catch (DoesNotExistException) {
$thread = null;
}
$data = $systemMessage->toArray($this->getResponseFormat(), $thread);
$data['parent'] = $parseMessage->toArray($this->getResponseFormat(), $thread);
$hasBotOrBridge = !empty($this->botService->getBotsForToken($this->room->getToken(), Bot::FEATURE_WEBHOOK));
if (!$hasBotOrBridge) {
@ -1299,7 +1315,7 @@ class ChatController extends AEnvironmentAwareOCSController {
continue;
}
$data = $message->toArray($this->getResponseFormat());
$data = $message->toArray($this->getResponseFormat(), null);
if ($participant->getAttendee()->isSensitive()) {
$data['message'] = '';
@ -1384,7 +1400,7 @@ class ChatController extends AEnvironmentAwareOCSController {
$this->messageParser->parseMessage($systemMessage);
$data = $systemMessage->toArray($this->getResponseFormat());
$data = $systemMessage->toArray($this->getResponseFormat(), null);
$bridge = $this->matterbridgeManager->getBridgeOfRoom($this->room);
@ -1610,6 +1626,8 @@ class ChatController extends AEnvironmentAwareOCSController {
protected function getMessagesForRoom(array $messageIds): array {
$comments = $this->chatManager->getMessagesForRoomById($this->room, $messageIds);
$this->sharePreloader->preloadShares($comments);
$potentialThreadIds = array_map(static fn (IComment $comment) => (int)$comment->getTopmostParentId(), $comments);
$threads = $this->threadService->findByThreadIds($this->room->getId(), $potentialThreadIds);
$messages = [];
$comments = $this->chatManager->filterCommentsWithNonExistingFiles($comments);
@ -1628,7 +1646,8 @@ class ChatController extends AEnvironmentAwareOCSController {
continue;
}
$messages[$comment->getId()] = $message->toArray($this->getResponseFormat());
$threadId = (int)$comment->getTopmostParentId();
$messages[$comment->getId()] = $message->toArray($this->getResponseFormat(), $threads[$threadId] ?? null);
}
return $messages;

View file

@ -166,15 +166,11 @@ class ThreadController extends AEnvironmentAwareOCSController {
}
try {
$thread = $this->threadService->findByThreadId($threadId);
$thread = $this->threadService->findByThreadId($this->room->getId(), $threadId);
} catch (DoesNotExistException) {
return new DataResponse(['error' => 'thread'], Http::STATUS_NOT_FOUND);
}
if ($thread->getRoomId() !== $this->room->getId()) {
return new DataResponse(['error' => 'thread'], Http::STATUS_NOT_FOUND);
}
$list = $this->prepareListOfThreads([$thread]);
/** @var TalkThreadInfo $threadInfo */
$threadInfo = array_shift($list);
@ -214,15 +210,11 @@ class ThreadController extends AEnvironmentAwareOCSController {
}
try {
$thread = $this->threadService->findByThreadId($threadId);
$thread = $this->threadService->findByThreadId($this->room->getId(), $threadId);
} catch (DoesNotExistException) {
return new DataResponse(['error' => 'thread'], Http::STATUS_NOT_FOUND);
}
if ($thread->getRoomId() !== $this->room->getId()) {
return new DataResponse(['error' => 'thread'], Http::STATUS_NOT_FOUND);
}
# FIXME Only allow for moderator and original author
try {
@ -314,8 +306,8 @@ class ThreadController extends AEnvironmentAwareOCSController {
$list[] = [
'thread' => $thread->toArray($room),
'attendee' => $attendee->jsonSerialize(),
'first' => $firstMessage?->toArray($this->getResponseFormat(), true),
'last' => $lastMessage?->toArray($this->getResponseFormat(), true),
'first' => $firstMessage?->toArray($this->getResponseFormat(), $thread),
'last' => $lastMessage?->toArray($this->getResponseFormat(), $thread),
];
}
@ -366,7 +358,7 @@ class ThreadController extends AEnvironmentAwareOCSController {
}
try {
$thread = $this->threadService->findByThreadId($messageId);
$thread = $this->threadService->findByThreadId($this->room->getId(), $messageId);
} catch (DoesNotExistException) {
return new DataResponse(['error' => 'message'], Http::STATUS_NOT_FOUND);
}

View file

@ -184,7 +184,7 @@ class Message {
* @psalm-param 'json'|'xml' $format
* @return TalkChatMessage
*/
public function toArray(string $format, ?bool $isThread = null): array {
public function toArray(string $format, ?Thread $thread): array {
$expireDate = $this->getComment()->getExpireDate();
$reactions = $this->getComment()->getReactions();
@ -215,8 +215,10 @@ class Message {
'markdown' => $this->getMessageType() === ChatManager::VERB_SYSTEM ? false : true,
'threadId' => $threadId,
];
if ($isThread === true) {
if ($thread !== null) {
$data['isThread'] = true;
$data['threadTitle'] = $thread->getName();
$data['threadReplies'] = $thread->getNumReplies();
}
if ($this->lastEditActorType && $this->lastEditActorId && $this->lastEditTimestamp) {

View file

@ -25,10 +25,11 @@ class ThreadMapper extends QBMapper {
}
/**
* @param non-negative-int $roomId
* @param non-negative-int $threadId
* @throws DoesNotExistException
*/
public function findById(int $threadId): Thread {
public function findById(int $roomId, int $threadId): Thread {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from($this->getTableName())
@ -36,11 +37,39 @@ class ThreadMapper extends QBMapper {
'id',
$query->createNamedParameter($threadId, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
))
->andWhere($query->expr()->eq(
'room_id',
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
));
return $this->findEntity($query);
}
/**
* @param non-negative-int $roomId
* @param list<non-negative-int> $threadIds
* @return list<Thread>
*/
public function findByIds(int $roomId, array $threadIds): array {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from($this->getTableName())
->where($query->expr()->in(
'id',
$query->createNamedParameter($threadIds, IQueryBuilder::PARAM_INT_ARRAY),
IQueryBuilder::PARAM_INT,
))
->andWhere($query->expr()->eq(
'room_id',
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
IQueryBuilder::PARAM_INT,
));
return $this->findEntities($query);
}
/**
* @param int<1, 50> $limit
* @return list<Thread>

View file

@ -121,6 +121,8 @@ namespace OCA\Talk;
* silent?: bool,
* threadId?: int,
* isThread?: bool,
* threadTitle?: string,
* threadReplies?: int,
* }
*
* @psalm-type TalkChatProxyMessage = TalkBaseMessage

View file

@ -462,6 +462,6 @@ class RoomFormatter {
return null;
}
return $message->toArray($responseFormat);
return $message->toArray($responseFormat, null);
}
}

View file

@ -46,11 +46,26 @@ class ThreadService {
}
/**
* @param non-negative-int $roomId
* @param non-negative-int $threadId
* @throws DoesNotExistException
*/
public function findByThreadId(int $threadId): Thread {
return $this->threadMapper->findById($threadId);
public function findByThreadId(int $roomId, int $threadId): Thread {
return $this->threadMapper->findById($roomId, $threadId);
}
/**
* @param non-negative-int $roomId
* @param list<non-negative-int> $threadId
* @return array<int, Thread> Map with thread id as key
*/
public function findByThreadIds(int $roomId, array $threadIds): array {
$threads = $this->threadMapper->findByIds($roomId, $threadIds);
$result = [];
foreach ($threads as $thread) {
$result[$thread->getId()] = $thread;
}
return $result;
}
/**
@ -213,27 +228,6 @@ class ThreadService {
$this->threadAttendeeMapper->deleteByRoomId($room->getId());
}
public function validateThreadIds(int $roomId, 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),
))
->andWhere($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
$ids = [];
$result = $query->executeQuery();
while ($row = $result->fetch()) {
$ids[] = (int)$row['id'];
}
$result->closeCursor();
return $ids;
}
public function validateThread(int $roomId, int $potentialThreadId): bool {
$query = $this->connection->getQueryBuilder();
$query->select('id')