Skip to content

feat: add notifications#2681

Open
luka-nextcloud wants to merge 1 commit into
mainfrom
add-notifications
Open

feat: add notifications#2681
luka-nextcloud wants to merge 1 commit into
mainfrom
add-notifications

Conversation

@luka-nextcloud

@luka-nextcloud luka-nextcloud commented May 22, 2026

Copy link
Copy Markdown
Contributor

Resolves: #1460

Demo:
demo.webm

@luka-nextcloud luka-nextcloud force-pushed the add-notifications branch 2 times, most recently from 57335c5 to bd02b34 Compare June 4, 2026 14:28
@luka-nextcloud luka-nextcloud marked this pull request as ready for review June 4, 2026 14:28
Signed-off-by: Luka Trovic <luka@nextcloud.com>

@blizzz blizzz left a comment

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.

Thanks @luka-nextcloud! I did only little smoke testing, but that was already working. I left some comments around here in the code, the ones marked with 💡 are not blocking, but for consideration.

What I also did not check yet, question back for you, do you think this scales with a bigger number of users, or users in a group?

My impression is that in some places there is code that is quite similar, an almost-duplication. There might be potential to reduce this, but do not consider this critical.

return [
{
key: 'notify-row',
label: t('tables', 'Row changes'),

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.

💄 less technical could be: Content changes?

},
{
key: 'notify-column',
label: t('tables', 'Column changes'),

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.

💄 Less technical wording could be Structure changes?

{
key: 'notify-row',
label: t('tables', 'Row changes'),
description: t('tables', 'Notify me when rows are created, updated or deleted.'),

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.

Here maybe row values, or just values, or values in a row.

}
}

private function createEvent($objectType, $object, $subject, $additionalParams = [], $author = null) {

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.

💡 This method collected more than one hundred lines now. Maybe it can be split up?

Comment on lines +276 to +279
'id' => 'unknown',
'name' => 'unknown',
'type' => 'unknown',
];

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.

💡 move 'unknown' into a constant, it is used in more places.

'columnId' => $columnId,
'exception' => $e->getMessage()
]);
continue; // Skip if column not found

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.

should not be necessary as last instruction in the for loop?


try {
$column = $this->columnMapper->find($columnId);
$this->columnCache[$columnId] = $column;

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.

the columnMapper already has a cache

if ($receiverId === '' || $receiverId === $authorId || !isset($recipientSet[$receiverId])) {
continue;
}
if (!$this->configService->isNotifyEnabledForScope($receiverId, 'table', $tableId, 'notify-assigned')) {

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.

please use constants when values like notify-assigned are used more than once and especially around multiple places. This will save use from errors through type and simplifies refactoring and understanding of the code.

return false;
}

public function set($key, $value) {

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.

type hints

},

computed: {
// ...mapState(useTablesStore, ['activeElement', 'isView']),

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.

leftover?

@blizzz

blizzz commented Jun 12, 2026

Copy link
Copy Markdown
Member

P.S.: what I forgot to missed to add: 💄 better to not include the row ID in the text of the notification, it does not really help people and looks technical. Just link row, there is a direct link already anyway (at least for the creates and updates).

@enjeck enjeck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not sure if should be done in another PR, but could we have notifications for asynchronous import? Right now, users never know exactly when import is done and it would be great

@enjeck enjeck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

some comments

Comment on lines +333 to +335
'id' => $column->getId(),
'name' => $column->getTitle(),
'type' => $column->getType(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

$column could be null, so trying to access these properties errors out. I don't think this is caught by following exception

if (this.objectType === 'table') {
activities = activities.filter((activity) => {
return (activity.object_type === 'tables_row' && activity.object_id === this.objectId)
return (activity.subject_rich[1].table?.id === this.objectId.toString() && !activity.subject_rich[1].view)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is subject_rich[1] guaranteed to exist. Do we need to guard it with subject_rich?.[1]?.table?.id too?

* @param array<string, mixed> $additionalParams
* @param mixed $author
*/
private function sendRowNotification(Row2 $row, string $subject, array $additionalParams, $author): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this function iterates viewMapper->findAll($tableId) twice (regular notifications, then assigned), calling findSharedWithUserIds() per view.
For each shared recipient it calls row2Mapper->isRowInViewPresent(), which executes getWantedRowIds(). So a single row edit on a table with V views and U shares can fire on the order of V×U filter evaluation queries. On busy/large tables this will have slow writes?

Can we fetch views once and reuse?

Comment on lines +22 to +23
private IURLGenerator $urlGenerator,
protected readonly IURLGenerator $url,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why are we injecting IURLGenerator twice? Can we use once?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there is a lot of logic duplication between ActivityManager (view-context resolution, changed-column filtering, shared-user fan-out) and NotificationHelper. If i understand correctly, in both, we want to figure out which views/users are affected by a specific row/column? Can we reuse?

Comment thread lib/Activity/Filter.php
*/
public function filterTypes(array $types): array {
return $types;
return array_merge($types, ['tables_sharing', 'tables_row_table', 'tables_row_view', 'tables_column_table', 'tables_column_view']);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

these constants are reused in various places, would be nice to have shared constants/variables

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add notifications

3 participants