From f77b180208d77643efce6714f05d29acc69958ff Mon Sep 17 00:00:00 2001 From: Richard Steinmetz Date: Thu, 17 Aug 2023 15:31:27 +0200 Subject: [PATCH 1/2] perf(dashboard): Implement widget item api v2 Signed-off-by: Richard Steinmetz --- lib/Dashboard/TalkWidget.php | 63 ++++- src/dashboard.js | 56 ---- src/views/Dashboard.vue | 245 ------------------ .../features/integration/dashboard.feature | 2 +- webpack.config.js | 1 - 5 files changed, 59 insertions(+), 308 deletions(-) delete mode 100644 src/dashboard.js delete mode 100644 src/views/Dashboard.vue diff --git a/lib/Dashboard/TalkWidget.php b/lib/Dashboard/TalkWidget.php index ccf366e1ffc..ca86e6703d5 100644 --- a/lib/Dashboard/TalkWidget.php +++ b/lib/Dashboard/TalkWidget.php @@ -5,6 +5,7 @@ * @copyright Copyright (c) 2020 Julius Härtl * * @author Julius Härtl + * @author Richard Steinmetz * * @license GNU AGPL version 3 or any later version * @@ -40,16 +41,17 @@ use OCP\Dashboard\IConditionalWidget; use OCP\Dashboard\IIconWidget; use OCP\Dashboard\IOptionWidget; +use OCP\Dashboard\IReloadableWidget; use OCP\Dashboard\Model\WidgetButton; use OCP\Dashboard\Model\WidgetItem; +use OCP\Dashboard\Model\WidgetItems; use OCP\Dashboard\Model\WidgetOptions; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserSession; -use OCP\Util; -class TalkWidget implements IAPIWidget, IIconWidget, IButtonWidget, IOptionWidget, IConditionalWidget { +class TalkWidget implements IAPIWidget, IIconWidget, IButtonWidget, IOptionWidget, IConditionalWidget, IReloadableWidget { public function __construct( protected IUserSession $userSession, @@ -112,7 +114,7 @@ public function getWidgetButtons(string $userId): array { $buttons[] = new WidgetButton( WidgetButton::TYPE_MORE, $this->url->linkToRouteAbsolute('spreed.Page.index'), - $this->l10n->t('More unread mentions') + $this->l10n->t('More conversations') ); return $buttons; } @@ -135,8 +137,6 @@ public function getUrl(): ?string { * @inheritDoc */ public function load(): void { - Util::addStyle('spreed', 'icons'); - Util::addScript('spreed', 'talk-dashboard'); } public function getItems(string $userId, ?string $since = null, int $limit = 7): array { @@ -170,6 +170,52 @@ public function getItems(string $userId, ?string $since = null, int $limit = 7): return $result; } + /** + * @inheritDoc + */ + public function getItemsV2(string $userId, ?string $since = null, int $limit = 7): WidgetItems { + $allRooms = $this->manager->getRoomsForUser($userId, [], true); + + $rooms = []; + $mentions = []; + foreach ($allRooms as $room) { + if ($room->getObjectType() !== BreakoutRoom::PARENT_OBJECT_TYPE) { + $rooms[] = $room; + } + + $participant = $this->participantService->getParticipant($room, $userId); + $attendee = $participant->getAttendee(); + if ($room->getCallFlag() !== Participant::FLAG_DISCONNECTED + || $attendee->getLastMentionMessage() > $attendee->getLastReadMessage() + || ( + ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) + && $room->getLastMessage() + && $room->getLastMessage()->getId() > $attendee->getLastReadMessage() + )) { + $mentions[] = $room; + } + } + + $roomsToReturn = $rooms; + if (!empty($mentions)) { + $roomsToReturn = $mentions; + } + + uasort($roomsToReturn, [$this, 'sortRooms']); + $roomsToReturn = array_slice($roomsToReturn, 0, $limit); + + $result = []; + foreach ($roomsToReturn as $room) { + $result[] = $this->prepareRoom($room, $userId); + } + + return new WidgetItems( + $result, + empty($result) ? $this->l10n->t('Say hi to your friends and colleagues!') : '', + empty($mentions) ? $this->l10n->t('No unread mentions') : '', + ); + } + protected function prepareRoom(Room $room, string $userId): WidgetItem { $participant = $this->participantService->getParticipant($room, $userId); $subtitle = ''; @@ -219,4 +265,11 @@ protected function sortRooms(Room $roomA, Room $roomB): int { return $roomA->getLastActivity() >= $roomB->getLastActivity() ? -1 : 1; } + + /** + * @inheritDoc + */ + public function getReloadInterval(): int { + return 30; + } } diff --git a/src/dashboard.js b/src/dashboard.js deleted file mode 100644 index 4407997bf30..00000000000 --- a/src/dashboard.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * @copyright Copyright (c) 2020 Julius Härtl - * - * @author Julius Härtl - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -import Vue from 'vue' - -import { getRequestToken } from '@nextcloud/auth' -import { translate, translatePlural } from '@nextcloud/l10n' -import { generateFilePath } from '@nextcloud/router' - -import Dashboard from './views/Dashboard.vue' - -// CSP config for webpack dynamic chunk loading -// eslint-disable-next-line -__webpack_nonce__ = btoa(getRequestToken()) - -// Correct the root of the app for chunk loading -// OC.linkTo matches the apps folders -// OC.generateUrl ensure the index.php (or not) -// We do not want the index.php since we're loading files -// eslint-disable-next-line -__webpack_public_path__ = generateFilePath('spreed', '', 'js/') - -Vue.prototype.t = translate -Vue.prototype.n = translatePlural -Vue.prototype.OC = OC -Vue.prototype.OCA = OCA - -document.addEventListener('DOMContentLoaded', function() { - - OCA.Dashboard.register('spreed', (el) => { - const View = Vue.extend(Dashboard) - new View({ - propsData: {}, - }).$mount(el) - }) - -}) diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue deleted file mode 100644 index 05a7a8c2698..00000000000 --- a/src/views/Dashboard.vue +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - diff --git a/tests/integration/features/integration/dashboard.feature b/tests/integration/features/integration/dashboard.feature index c5ebf83f3d0..c8f521d59b7 100644 --- a/tests/integration/features/integration/dashboard.feature +++ b/tests/integration/features/integration/dashboard.feature @@ -6,7 +6,7 @@ Feature: integration/dashboard Scenario: User gets the available dashboard widgets When user "participant1" sees the following entry when loading the list of dashboard widgets (v1) | id | title | icon_class | icon_url | widget_url | item_icons_round | order | buttons | item_api_versions | reload_interval | - | spreed | Talk mentions | dashboard-talk-icon | img/app-dark.svg | {$BASE_URL}index.php/apps/spreed/ | true | 10 | [{"type":"more","text":"More unread mentions","link":"{$BASE_URL}index.php/apps/spreed/"}] | [1] | 0 | + | spreed | Talk mentions | dashboard-talk-icon | img/app-dark.svg | {$BASE_URL}index.php/apps/spreed/ | true | 10 | [{"type":"more","text":"More conversations","link":"{$BASE_URL}index.php/apps/spreed/"}] | [1,2] | 30 | Scenario: User gets the dashboard widget content When user "participant1" sees the following entries for dashboard widgets "spreed" (v1) diff --git a/webpack.config.js b/webpack.config.js index 4f5bf9bdd97..504d4467275 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,7 +29,6 @@ module.exports = mergeWithRules({ 'public-share-auth-sidebar': path.join(__dirname, 'src', 'mainPublicShareAuthSidebar.js'), 'public-share-sidebar': path.join(__dirname, 'src', 'mainPublicShareSidebar.js'), flow: path.join(__dirname, 'src', 'flow.js'), - dashboard: path.join(__dirname, 'src', 'dashboard.js'), deck: path.join(__dirname, 'src', 'deck.js'), maps: path.join(__dirname, 'src', 'maps.js'), search: path.join(__dirname, 'src', 'search.js'), From 26b6a1950e2fe10b9bfc888d906f62256cca3fb5 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 10 Apr 2024 09:40:29 +0200 Subject: [PATCH 2/2] fix(breakoutrooms): Hide breakout rooms from dashboard items Signed-off-by: Joas Schilling --- lib/Dashboard/TalkWidget.php | 5 +++-- .../features/bootstrap/FeatureContext.php | 18 ++++++++++++------ .../features/integration/dashboard.feature | 7 +++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/Dashboard/TalkWidget.php b/lib/Dashboard/TalkWidget.php index ca86e6703d5..34f820d213b 100644 --- a/lib/Dashboard/TalkWidget.php +++ b/lib/Dashboard/TalkWidget.php @@ -179,9 +179,10 @@ public function getItemsV2(string $userId, ?string $since = null, int $limit = 7 $rooms = []; $mentions = []; foreach ($allRooms as $room) { - if ($room->getObjectType() !== BreakoutRoom::PARENT_OBJECT_TYPE) { - $rooms[] = $room; + if ($room->getObjectType() === BreakoutRoom::PARENT_OBJECT_TYPE) { + continue; } + $rooms[] = $room; $participant = $this->participantService->getParticipant($room, $userId); $attendee = $participant->getAttendee(); diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 466022b3cb3..8bc716d4cf5 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -2322,7 +2322,7 @@ public function userGetsDashboardWidgets($user, $apiVersion = 'v1', TableNode $f } /** - * @Then /^user "([^"]*)" sees the following entries for dashboard widgets "([^"]*)"(?: \((v1)\))$/ + * @Then /^user "([^"]*)" sees the following entries for dashboard widgets "([^"]*)"(?: \((v1|v2)\))$/ * * @param string $user * @param string $widgetId @@ -2339,12 +2339,18 @@ public function userGetsDashboardWidgetItems($user, $widgetId, $apiVersion = 'v1 Assert::assertArrayHasKey($widgetId, $data); $expectedItems = $formData->getColumnsHash(); + if ($apiVersion === 'v1') { + $actualItems = $data[$widgetId]; + } else { + $actualItems = $data[$widgetId]['items']; + } + if (empty($expectedItems)) { - Assert::assertEmpty($data[$widgetId]); + Assert::assertEmpty($actualItems); return; } - Assert::assertCount(count($expectedItems), $data[$widgetId]); + Assert::assertCount(count($expectedItems), $actualItems); foreach ($expectedItems as $key => $item) { $token = self::$identifierToToken[$item['link']]; @@ -2352,11 +2358,11 @@ public function userGetsDashboardWidgetItems($user, $widgetId, $apiVersion = 'v1 $item['iconUrl'] = str_replace('{$BASE_URL}', $this->baseUrl, $item['iconUrl']); $item['iconUrl'] = str_replace('{token}', $token, $item['iconUrl']); - Assert::assertMatchesRegularExpression('/\?v=\w{8}$/', $data[$widgetId][$key]['iconUrl']); - preg_match('/(?\?v=\w{8})$/', $data[$widgetId][$key]['iconUrl'], $matches); + Assert::assertMatchesRegularExpression('/\?v=\w{8}$/', $actualItems[$key]['iconUrl']); + preg_match('/(?\?v=\w{8})$/', $actualItems[$key]['iconUrl'], $matches); $item['iconUrl'] = str_replace('{version}', $matches['version'], $item['iconUrl']); - Assert::assertEquals($item, $data[$widgetId][$key], 'Wrong details for item #' . $key); + Assert::assertEquals($item, $actualItems[$key], 'Wrong details for item #' . $key); } } diff --git a/tests/integration/features/integration/dashboard.feature b/tests/integration/features/integration/dashboard.feature index c8f521d59b7..233ba534acd 100644 --- a/tests/integration/features/integration/dashboard.feature +++ b/tests/integration/features/integration/dashboard.feature @@ -11,6 +11,8 @@ Feature: integration/dashboard Scenario: User gets the dashboard widget content When user "participant1" sees the following entries for dashboard widgets "spreed" (v1) | title | subtitle | link | iconUrl | + When user "participant1" sees the following entries for dashboard widgets "spreed" (v2) + | title | subtitle | link | iconUrl | Given user "participant2" creates room "one-to-one room" (v4) | roomType | 1 | | invite | participant1 | @@ -40,3 +42,8 @@ Feature: integration/dashboard | call room | Call in progress | call room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | | group room | You were mentioned | group room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | | participant2-displayname | Hello | one-to-one room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | + Then user "participant1" sees the following entries for dashboard widgets "spreed" (v2) + | title | subtitle | link | iconUrl | sinceId | overlayIconUrl | + | call room | Call in progress | call room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | + | group room | You were mentioned | group room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | | + | participant2-displayname | Hello | one-to-one room | {$BASE_URL}ocs/v2.php/apps/spreed/api/v1/room/{token}/avatar{version} | | |