Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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 lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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 @@
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 @@
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);

Check failure on line 484 in lib/Chat/ChatManager.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

NoInterfaceProperties

lib/Chat/ChatManager.php:484:56: NoInterfaceProperties: Interfaces cannot have properties (see https://psalm.dev/028)
}

$alreadyNotifiedUsers = [];
Expand Down
4 changes: 3 additions & 1 deletion lib/Chat/SystemMessage/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@
}

protected function attendeesAddedEvent(AttendeesAddedEvent $event): void {
$event->shouldSkipLastMessageUpdate = true;

Check failure on line 479 in lib/Chat/SystemMessage/Listener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedPropertyAssignment

lib/Chat/SystemMessage/Listener.php:479:3: UndefinedPropertyAssignment: Instance property OCA\Talk\Events\AttendeesAddedEvent::$shouldSkipLastMessageUpdate is not defined (see https://psalm.dev/038)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A parallel PR made this read-only and private, so I guess we need to drop read-only and add a setter

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I committed another change to use getter / setters in this place.
I removed readonly also but kept it private, so this should now be compatible to the other pull request?

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 @@ -493,6 +494,7 @@
}

protected function attendeesRemovedEvent(AttendeesRemovedEvent $event): void {
$event->shouldSkipLastMessageUpdate = true;

Check failure on line 497 in lib/Chat/SystemMessage/Listener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedPropertyAssignment

lib/Chat/SystemMessage/Listener.php:497:3: UndefinedPropertyAssignment: Instance property OCA\Talk\Events\AttendeesRemovedEvent::$shouldSkipLastMessageUpdate is not defined (see https://psalm.dev/038)
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 @@ -512,7 +514,7 @@
string $message,
array $parameters = [],
?Participant $participant = null,
bool $shouldSkipLastMessageUpdate = false,
bool $shouldSkipLastMessageUpdate = true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we keep this we need to check all the calls in this file. E.g. currently posting a file does not update the last message anymore.
Other cases where I would still bump would be: lobby_none, lobby_timer_reached, breakout_rooms_started, breakout_rooms_stopped, call_left, call_joined, call_started, setCallRecording

bool $silent = false,
bool $forceSystemAsActor = false,
?int $replyTo = null,
Expand Down
4 changes: 4 additions & 0 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,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 @@ -121,7 +121,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';
}
2 changes: 1 addition & 1 deletion lib/Events/ASystemMessageSentEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function __construct(
?Participant $participant = null,
bool $silent = false,
?IComment $parent = null,
protected bool $skipLastActivityUpdate = false,
protected bool $skipLastActivityUpdate = true,
) {
parent::__construct(
$room,
Expand Down
2 changes: 1 addition & 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 readonly bool $skipLastMessageUpdate = true,
) {
parent::__construct($room, $attendees);
}
Expand Down
7 changes: 7 additions & 0 deletions lib/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,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 @@ -135,6 +136,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 @@ -171,6 +176,7 @@ public function createRoomObject(array $row): Room {
(int)$row['call_flag'],
$activeSince,
$lastActivity,
$lastMetadataActivity,
(int)$row['last_message'],
$lastMessage,
$lobbyTimer,
Expand Down Expand Up @@ -321,6 +327,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
109 changes: 109 additions & 0 deletions lib/Migration/Version24000Date20260510193300.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?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\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
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,
]);
<<<<<<< HEAD

Check failure on line 45 in lib/Migration/Version24000Date20260510193300.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

ParseError

lib/Migration/Version24000Date20260510193300.php:45:1: ParseError: Syntax error, unexpected T_SL on line 45 (see https://psalm.dev/173)
<<<<<<< HEAD
Comment thread
sudormant marked this conversation as resolved.
Outdated
$table->addIndex(['last_metadata_activity'], 'talkroom_lastmetadataactive');

}

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

if (!$table->hasColumn('last_metadata_activity')) {
$table->addColumn('last_metadata_activity', Types::DATETIME, [
'notnull' => false,
]);
$table->addIndex(['last_metadata_activity'], 'talkthread_lastmetadataactive');
=======
$table->addIndex(['last_metadata_activity'], 'talkthread_lastmetadataactive');
=======
$table->addIndex(['last_metadata_activity'], 'talkroom_lastmetadataactive');
>>>>>>> a798ce9366 (change(conversations): change behavior in all necessary places for Rooms and Threads to use / set lastMetadataActivity instaed of lastActivity where appropriate, i.e. where system messages are signalled and not real chat messages.)

>>>>>>> 2ed52550f0 (feature(api): Add a new field and corresponding functionality for lastMetadaActivity for Rooms and Threads, similar to lastActivity. This also adds a database migration for the oc_talk_rooms and oc_talk_threads tables as well. Functions are introduced and prepared for later use. The use of the field lastActivity to signal when the last real message in a conversation appeared remains unchanged to keep the API stable. The intended use of this feature is to better distinguish between real messages (lastActivity) to notify and bump conversations in the thread list to the top, and other status / metadata related messages (lastMetadataActivity) like room state and participant list changes that shall get synced and be updated in the clients, but not trigger an activity bump of its conversations in the thread list.)
}

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

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

return $schema;
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
#[Override]
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) : void {
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> a798ce9366 (change(conversations): change behavior in all necessary places for Rooms and Threads to use / set lastMetadataActivity instaed of lastActivity where appropriate, i.e. where system messages are signalled and not real chat messages.)
$update = $this->connection->getQueryBuilder();
$update->update('talk_rooms')
->set('last_metadata_activity', 'last_activity');
$update->executeStatement();
$update = $this->connection->getQueryBuilder();
$update->update('talk_threads')
->set('last_metadata_activity', 'last_activity');
$update->executeStatement();
<<<<<<< HEAD
=======
$update = $this->connection->getQueryBuilder();
$update->update('talk_rooms')
->set('last_metadata_activity', 'last_activity');
$update->executeStatement();
>>>>>>> 2ed52550f0 (feature(api): Add a new field and corresponding functionality for lastMetadaActivity for Rooms and Threads, similar to lastActivity. This also adds a database migration for the oc_talk_rooms and oc_talk_threads tables as well. Functions are introduced and prepared for later use. The use of the field lastActivity to signal when the last real message in a conversation appeared remains unchanged to keep the API stable. The intended use of this feature is to better distinguish between real messages (lastActivity) to notify and bump conversations in the thread list to the top, and other status / metadata related messages (lastMetadataActivity) like room state and participant list changes that shall get synced and be updated in the clients, but not trigger an activity bump of its conversations in the thread list.)
=======
>>>>>>> a798ce9366 (change(conversations): change behavior in all necessary places for Rooms and Threads to use / set lastMetadataActivity instaed of lastActivity where appropriate, i.e. where system messages are signalled and not real chat messages.)
}

}
3 changes: 3 additions & 0 deletions lib/Model/SelectHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function selectRoomsTable(IQueryBuilder $query, string $alias = 'r'): voi
$alias . 'default_permissions',
$alias . 'call_flag',
$alias . 'last_activity',
$alias . 'last_metadata_activity',
$alias . 'last_message',
$alias . 'lobby_timer',
$alias . 'object_type',
Expand Down Expand Up @@ -61,6 +62,7 @@ public function selectThreadsTable(IQueryBuilder $query, string $alias = 'th', b
->selectAlias($alias . 'last_message_id', 'th_last_message_id')
->selectAlias($alias . 'num_replies', 'th_num_replies')
->selectAlias($alias . 'last_activity', 'th_last_activity')
->selectAlias($alias . 'last_metadata_activity', 'th_last_metadata_activity')
->selectAlias($alias . 'name', 'th_name')
->selectAlias($alias . 'id', 'th_id');
return;
Expand All @@ -71,6 +73,7 @@ public function selectThreadsTable(IQueryBuilder $query, string $alias = 'th', b
$alias . 'last_message_id',
$alias . 'num_replies',
$alias . 'last_activity',
$alias . 'last_metadata_activity',
$alias . 'name',
])->selectAlias($alias . 'id', 'th_id');

Expand Down
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 @@
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 @@
$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 @@
$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 @@
'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 @@ -97,16 +104,17 @@
}

/**
* @return TalkThread

Check failure on line 107 in lib/Model/Thread.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

InvalidReturnType

lib/Model/Thread.php:107:13: InvalidReturnType: The declared return type 'array{id: int<1, max>, lastActivity: int<0, max>, lastMessageId: int<0, max>, numReplies: int<0, max>, roomToken: string, title: string}' for OCA\Talk\Model\Thread::toArray is incorrect, got 'array{id: int<1, max>, lastActivity: int<0, max>, lastMessageId: int<0, max>, lastMetadataActivity: int<0, max>, numReplies: int<0, max>, roomToken: string, title: string}' which is different due to additional array shape fields (lastMetadataActivity) (see https://psalm.dev/011)
*/
public function toArray(Room $room): array {
return [

Check failure on line 110 in lib/Model/Thread.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

InvalidReturnStatement

lib/Model/Thread.php:110:10: InvalidReturnStatement: The inferred type 'array{id: int<1, max>, lastActivity: int<0, max>, lastMessageId: int<0, max>, lastMetadataActivity: int<0, max>, numReplies: int<0, max>, roomToken: string, title: string}' does not match the declared return type 'array{id: int<1, max>, lastActivity: int<0, max>, lastMessageId: int<0, max>, numReplies: int<0, max>, roomToken: string, title: string}' for OCA\Talk\Model\Thread::toArray due to additional array shape fields (lastMetadataActivity) (see https://psalm.dev/128)
'id' => max(1, $this->getId()),
// 'roomId' => max(1, $this->getRoomId()),
'roomToken' => $room->getToken(),
'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
7 changes: 5 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,9 @@
* 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
Comment thread
sudormant marked this conversation as resolved.
* // 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 @@ -118,6 +118,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 @@ -314,6 +315,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 @@ -388,7 +388,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