Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,4 @@
* `preserve-conversation` - Whether the owner can preserve a conversation
* `recording-chunked-upload` (local) - Whether the recording backend can request a temporary upload share to upload large recordings via chunked public WebDAV before finishing with the store endpoint
* `config => call => external-call-service` (local) - The target URL for an external call service if one is configured
* `last-metadata-activity` - Keeps track when the room was last changed as opposed to last-room-activity set by new messages (which is used for thread sorting)
1 change: 1 addition & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Capabilities implements IPublicCapability {
'multi-room-users',
'favorites',
'last-room-activity',
'last-metadata-activity',
'no-ping',
'system-messages',
'delete-messages',
Expand Down
8 changes: 4 additions & 4 deletions lib/Chat/ChatManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public function addSystemMessage(
bool $sendNotifications,
?string $referenceId = null,
?IComment $replyTo = null,
bool $shouldSkipLastMessageUpdate = false,
bool $shouldSkipLastMessageUpdate = true,
bool $silent = false,
int $threadId = 0,
): IComment {
Expand Down Expand Up @@ -473,15 +473,15 @@ public function sendMessage(
if (!$fromScheduledMessage && $participant instanceof Participant) {
$this->participantService->updateLastReadMessage($participant, $messageId);
}

// Update last_message
if ($comment->getActorType() !== Attendee::ACTOR_BOTS
|| $comment->getActorId() === Attendee::ACTOR_ID_CHANGELOG
|| str_starts_with((string)$comment->getActorId(), Attendee::ACTOR_BOT_PREFIX)) {
$this->roomService->setLastMessage($chat, $comment);
$this->roomService->setLastMessageInfo($chat, (int)$comment->getId(), $comment->getCreationDateTime());
$this->roomService->setLastMetadataActivity($chat, $comment->getCreationDateTime());
$this->unreadCountCache->clear($chat->getId() . '-');
} else {
$this->roomService->setLastActivity($chat, $comment->getCreationDateTime());
$this->roomService->setLastMetadataActivity($chat, $comment->getCreationDateTime());
}

$alreadyNotifiedUsers = [];
Expand Down
6 changes: 4 additions & 2 deletions lib/Chat/SystemMessage/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ protected function fixMimeTypeOfVoiceMessage(ShareCreatedEvent|BeforeDuplicateSh
}

protected function attendeesAddedEvent(AttendeesAddedEvent $event): void {
$event->setShouldSkipLastMessageUpdate(true);
foreach ($event->getAttendees() as $attendee) {
$this->logger->debug($attendee->getActorType() . ' "' . $attendee->getActorId() . '" added to room "' . $event->getRoom()->getToken() . '"', ['app' => 'spreed-bfp']);
if ($attendee->getActorType() === Attendee::ACTOR_GROUPS) {
Expand All @@ -506,6 +507,7 @@ protected function attendeesAddedEvent(AttendeesAddedEvent $event): void {
}

protected function attendeesRemovedEvent(AttendeesRemovedEvent $event): void {
$event->setShouldSkipLastActivityUpdate(true);
foreach ($event->getAttendees() as $attendee) {
$this->logger->debug($attendee->getActorType() . ' "' . $attendee->getActorId() . '" removed from room "' . $event->getRoom()->getToken() . '"', ['app' => 'spreed-bfp']);
if ($attendee->getActorType() === Attendee::ACTOR_GROUPS) {
Expand All @@ -525,7 +527,7 @@ protected function sendSystemMessage(
string $message,
array $parameters = [],
?Participant $participant = null,
bool $shouldSkipLastMessageUpdate = false,
bool $shouldSkipLastMessageUpdate = true,
bool $silent = false,
bool $forceSystemAsActor = false,
?int $replyTo = null,
Expand Down Expand Up @@ -649,7 +651,7 @@ protected function setCallRecording(RoomModifiedEvent $event): void {
$suffix = $this->getCallRecordingSuffix($event);
$systemMessage = $prefix . 'recording_' . $suffix;

$this->sendSystemMessage($event->getRoom(), $systemMessage, [], $actor);
$this->sendSystemMessage($event->getRoom(), $systemMessage, [], $actor, false);
}

protected function getCallRecordingSuffix(RoomModifiedEvent $event): string {
Expand Down
4 changes: 4 additions & 0 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ public function getRooms(int $noStatusUpdate = 0, bool $includeStatus = false, i
// Include rooms which had activity
return true;
}
if ($room->getLastMetadataActivity() && $room->getLastMetadataActivity()->getTimestamp() >= $modifiedSince) {
// Include rooms which had metadata activity
return true;
}

// Include rooms where only attendee level things changed,
// e.g. favorite, read-marker update, notification setting
Expand Down
2 changes: 1 addition & 1 deletion lib/Controller/ThreadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public function getSubscribedThreads(int $limit = 100, int $offset = 0): DataRes
}
}

// Sort by last activity again
// Sort by last message activity again
usort($threads, static function (Thread $a, Thread $b): int {
if ($b->getLastActivity() === $a->getLastActivity()) {
return $b->getId() <=> $a->getId();
Expand Down
1 change: 1 addition & 0 deletions lib/Events/ARoomSyncedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@

abstract class ARoomSyncedEvent extends ARoomEvent {
public const PROPERTY_LAST_ACTIVITY = 'lastActivity';
public const PROPERTY_LAST_METADATA_ACTIVITY = 'lastMetadataActivity';
}
19 changes: 18 additions & 1 deletion lib/Events/ASystemMessageSentEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
?Participant $participant = null,
bool $silent = false,
?IComment $parent = null,
protected bool $skipLastActivityUpdate = false,
private bool $skipLastActivityUpdate = false,
private bool $skipLastMetadataActivityUpdate = true,
) {
parent::__construct(
$room,
Expand All @@ -44,4 +45,20 @@
public function shouldSkipLastActivityUpdate(): bool {
return $this->skipLastActivityUpdate;
}

/**
* public setter for shouldSkipLastAcitvityUpdate
* @param bool $shouldSkipLastActivity
*/
public function setShouldSkipLastActivityUpdate(bool $shouldSkipLastActivity) {
$this->skipLastActivityUpdate = $shouldSkipLastActivity;
}

public function shouldSkipLastMetadataActivityUpdate(): bool {
return $this->skipLastMetadataActivityUpdate;
}

public function setShouldSkipLastMetadataActivityUpdate(bool $shouldSkipLastMetadaActivity) {
$this->skipLastMetadataActivityUpdate = $shouldSkipLastMetadataActivity;

Check failure on line 62 in lib/Events/ASystemMessageSentEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedVariable

lib/Events/ASystemMessageSentEvent.php:62:43: UndefinedVariable: Cannot find referenced variable $shouldSkipLastMetadataActivity (see https://psalm.dev/024)
}
}
6 changes: 5 additions & 1 deletion lib/Events/AttendeesAddedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AttendeesAddedEvent extends AttendeesEvent {
public function __construct(
Room $room,
array $attendees,
private readonly bool $skipLastMessageUpdate = false,
private bool $skipLastMessageUpdate = true,
) {
parent::__construct($room, $attendees);
}
Expand All @@ -30,6 +30,10 @@ public function shouldSkipLastMessageUpdate(): bool {
return $this->skipLastMessageUpdate;
}

public function setShouldSkipLastMessageUpdate(bool $shouldSkipLastMessageUpdate) {
$this->skipLastMessageUpdate = $shouldSkipLastMessageUpdate;
}

public function setLastMessage(IComment $lastMessage): void {
$this->lastMessage = $lastMessage;
}
Expand Down
10 changes: 10 additions & 0 deletions lib/Events/AttendeesRemovedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@
namespace OCA\Talk\Events;

class AttendeesRemovedEvent extends AttendeesEvent {

private bool $shouldSkipLastMessageUpdate = true;

public function shouldSkipLastMessageUpdate() : bool {
return $this->shouldSkipLastMessageUpdate;
}

public function setShouldSkipLastActivityUpdate(bool $pShouldSkipLastActivityUpdate) {
$this->shouldSkipLastMessageUpdate = $pShouldSkipLastActivityUpdate;
}
}
7 changes: 7 additions & 0 deletions lib/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public function createRoomObjectFromData(array $data): Room {
'call_flag' => 0,
'active_since' => null,
'last_activity' => null,
'last_metadata_activity' => null,
'last_message' => 0,
'comment_id' => null,
'lobby_timer' => null,
Expand Down Expand Up @@ -134,6 +135,10 @@ public function createRoomObject(array $row): Room {
if (!empty($row['last_activity'])) {
$lastActivity = $this->timeFactory->getDateTime($row['last_activity']);
}
$lastMetadataActivity = null;
if (!empty($row['last_metadata_activity'])) {
$lastMetadataActivity = $this->timeFactory->getDateTime($row['last_metadata_activity']);
}

$lobbyTimer = null;
if (!empty($row['lobby_timer'])) {
Expand Down Expand Up @@ -170,6 +175,7 @@ public function createRoomObject(array $row): Room {
(int)$row['call_flag'],
$activeSince,
$lastActivity,
$lastMetadataActivity,
(int)$row['last_message'],
$lastMessage,
$lobbyTimer,
Expand Down Expand Up @@ -320,6 +326,7 @@ public function getInactiveRooms(\DateTime $inactiveSince): array {
$helper->selectRoomsTable($query);
$query->from('talk_rooms', 'r')
->andWhere($query->expr()->lte('r.last_activity', $query->createNamedParameter($inactiveSince, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->andWhere($query->expr()->lte('r.last_metadata_activity', $query->createNamedParameter($inactiveSince, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
->andWhere($query->expr()->neq('r.read_only', $query->createNamedParameter(Room::READ_ONLY, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->in('r.type', $query->createNamedParameter([Room::TYPE_PUBLIC, Room::TYPE_GROUP], IQueryBuilder::PARAM_INT_ARRAY)))
->andWhere($query->expr()->emptyString('remoteServer'))
Expand Down
64 changes: 64 additions & 0 deletions lib/Migration/Version24000Date20260510193300.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;

class Version24000Date20260510193300 extends SimpleMigrationStep {

public function __construct(
private readonly IDBConnection $connection,
) {
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

$table = $schema->getTable('talk_rooms');

if (!$table->hasColumn('last_metadata_activity')) {
$table->addColumn('last_metadata_activity', Types::DATETIME, [
'notnull' => false,
]);
$table->addIndex(['last_metadata_activity'], 'talkroom_lastmetadataactive');

}

return $schema;
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
#[Override]
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) : void {
$update = $this->connection->getQueryBuilder();
$update->update('talk_rooms')
->set('last_metadata_activity', 'last_activity');
$update->executeStatement();
}

}
8 changes: 8 additions & 0 deletions lib/Model/Thread.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
* @method void setNumReplies(int $numReplies)
* @method int getNumReplies()
* @method void setLastActivity(\DateTime $lastActivity)
* @method void setLastMetadataActivity(\DateTime $lastMetadataActivity)
* @method \DateTime|null getLastActivity()
* @method \DateTime|null getLastMetadataActivity()
* @method void setName(string $name)
*
* @psalm-import-type TalkThread from ResponseDefinitions
Expand All @@ -34,13 +36,15 @@ class Thread extends Entity {
protected int $lastMessageId = 0;
protected int $numReplies = 0;
protected ?\DateTime $lastActivity = null;
protected ?\DateTime $lastMetadataActivity = null;
protected string $name = '';

public function __construct() {
$this->addType('roomId', Types::BIGINT);
$this->addType('lastMessageId', Types::BIGINT);
$this->addType('numReplies', Types::BIGINT);
$this->addType('lastActivity', Types::DATETIME);
$this->addType('lastMetadataActivity', Types::DATETIME);
$this->addType('name', Types::STRING);
}

Expand All @@ -51,6 +55,7 @@ public static function createFromRow(array $row): Thread {
$thread->setLastMessageId((int)$row['last_message_id']);
$thread->setNumReplies((int)$row['num_replies']);
$thread->setLastActivity(new \DateTime($row['last_activity']));
$thread->setLastMetadataActivity(new \DateTime($row['last_metadata_activity']));
$thread->setName($row['name']);
return $thread;
}
Expand All @@ -68,6 +73,7 @@ public static function fromJson(string $json): Thread {
$thread->setLastMessageId((int)$row['last_message_id']);
$thread->setNumReplies((int)$row['num_replies']);
$thread->setLastActivity(new \DateTime('@' . $row['last_activity']));
$thread->setLastMetadataActivity(new \DateTime('@' . $row['last_metadata_activity']));
$thread->setName($row['name']);
return $thread;
}
Expand All @@ -83,6 +89,7 @@ public function toJson(): string {
'last_message_id' => $this->getLastMessageId(),
'num_replies' => $this->getNumReplies(),
'last_activity' => $this->getLastActivity()?->getTimestamp() ?? 0,
'last_metadata_activity' => $this->getLastMetadataActivity()?->getTimestamp() ?? 0,
'name' => $this->getName(),
], flags: JSON_THROW_ON_ERROR);
}
Expand All @@ -107,6 +114,7 @@ public function toArray(Room $room): array {
'lastMessageId' => max(0, $this->getLastMessageId()),
'numReplies' => max(0, $this->getNumReplies()),
'lastActivity' => max(0, $this->getLastActivity()?->getTimestamp() ?? 0),
'lastMetadataActivity' => max(0, $this->getLastMetadataActivity()?->getTimestamp() ?? 0),
'title' => $this->getName(),
];
}
Expand Down
8 changes: 6 additions & 2 deletions lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,10 @@
* isCustomAvatar: bool,
* // Flag if the conversation is favorited by the user
* isFavorite: bool,
* // Timestamp of the last activity in the conversation, in seconds and UTC time zone
* // Timestamp of the last message activity in the conversation, in seconds and UTC time zone
* lastActivity: int,
* // Timestamp of the last activity (metadata, not messages) in the conversation, in seconds and UTC time zone
* lastMetadataActivity: int,
* // ID of the last message read by every user that has read privacy set to public in a room. When the user themself has it set to private the value is `0` (only available with `chat-read-status` capability)
* lastCommonReadMessage: int,
* // Last message in a conversation if available, otherwise empty. **Note:** Even when given the message will not contain the `parent` or `reactionsSelf` attribute due to performance reasons
Expand Down Expand Up @@ -725,8 +727,10 @@
* title: string,
* // ID of the last message in the thread
* lastMessageId: non-negative-int,
* // UNIX timestamp of the last activity in the thread
* // UNIX timestamp of the last message activity in the thread
* lastActivity: non-negative-int,
* // UNIX timestamp of the last metadata, not message, activity in the thread
* lastMetadataActivity: non-negative-int,
* // Number of replies in the thread
* numReplies: non-negative-int,
* }
Expand Down
8 changes: 8 additions & 0 deletions lib/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public function __construct(
private int $callFlag,
private ?\DateTime $activeSince,
private ?\DateTime $lastActivity,
private ?\DateTime $lastMetadataActivity,
private int $lastMessageId,
private ?IComment $lastMessage,
private ?\DateTime $lobbyTimer,
Expand Down Expand Up @@ -293,6 +294,13 @@ public function getLastActivity(): ?\DateTime {
public function setLastActivity(\DateTime $now): void {
$this->lastActivity = $now;
}
public function getLastMetadataActivity(): ?\DateTime {
return $this->lastMetadataActivity;
}

public function setLastMetadataActivity(\DateTime $now): void {
$this->lastMetadataActivity = $now;
}

public function getLastMessageId(): int {
return $this->lastMessageId;
Expand Down
2 changes: 1 addition & 1 deletion lib/Service/BreakoutRoomService.php
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ protected function setAssistanceRequest(Room $breakoutRoom, int $status): void {
}

$this->roomService->setBreakoutRoomStatus($breakoutRoom, $status);
$this->roomService->setLastActivity($breakoutRoom, $this->timeFactory->getDateTime());
$this->roomService->setLastMetadataActivity($breakoutRoom, $this->timeFactory->getDateTime());
}

/**
Expand Down
Loading
Loading