Dashboard widget for NC20
This commit is contained in:
parent
00d2fcfef1
commit
937c08754b
|
@ -13,6 +13,12 @@ return ['routes' => [
|
||||||
'verb' => 'GET',
|
'verb' => 'GET',
|
||||||
'postfix' => 'welcome',
|
'postfix' => 'welcome',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'name' => 'page#create',
|
||||||
|
'url' => '/new',
|
||||||
|
'verb' => 'GET',
|
||||||
|
'postfix' => 'new',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'name' => 'page#index',
|
'name' => 'page#index',
|
||||||
'url' => '/note/{id}',
|
'url' => '/note/{id}',
|
||||||
|
@ -28,6 +34,11 @@ return ['routes' => [
|
||||||
'url' => '/notes',
|
'url' => '/notes',
|
||||||
'verb' => 'GET',
|
'verb' => 'GET',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'name' => 'notes#dashboard',
|
||||||
|
'url' => '/notes/dashboard',
|
||||||
|
'verb' => 'GET',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'name' => 'notes#get',
|
'name' => 'notes#get',
|
||||||
'url' => '/notes/{id}',
|
'url' => '/notes/{id}',
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
@include icon-black-white('notes', 'notes', 1);
|
|
@ -8,6 +8,7 @@ use OCP\AppFramework\App;
|
||||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||||
|
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
||||||
|
|
||||||
class Application extends App implements IBootstrap {
|
class Application extends App implements IBootstrap {
|
||||||
public const APP_ID = 'notes';
|
public const APP_ID = 'notes';
|
||||||
|
@ -19,6 +20,11 @@ class Application extends App implements IBootstrap {
|
||||||
|
|
||||||
public function register(IRegistrationContext $context): void {
|
public function register(IRegistrationContext $context): void {
|
||||||
$context->registerCapability(Capabilities::class);
|
$context->registerCapability(Capabilities::class);
|
||||||
|
$context->registerDashboardWidget(DashboardWidget::class);
|
||||||
|
$context->registerEventListener(
|
||||||
|
BeforeTemplateRenderedEvent::class,
|
||||||
|
BeforeTemplateRenderedListener::class
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(IBootContext $context): void {
|
public function boot(IBootContext $context): void {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\Notes\AppInfo;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\EventDispatcher\IEventListener;
|
||||||
|
|
||||||
|
class BeforeTemplateRenderedListener implements IEventListener {
|
||||||
|
public function handle(Event $event): void {
|
||||||
|
if (!($event instanceof BeforeTemplateRenderedEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$event->isLoggedIn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
\OCP\Util::addStyle('notes', 'global');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\Notes\AppInfo;
|
||||||
|
|
||||||
|
use OCP\Dashboard\IWidget;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
|
||||||
|
class DashboardWidget implements IWidget {
|
||||||
|
|
||||||
|
/** @var IURLGenerator */
|
||||||
|
private $url;
|
||||||
|
/** @var IL10N */
|
||||||
|
private $l10n;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IURLGenerator $url,
|
||||||
|
IL10N $l10n
|
||||||
|
) {
|
||||||
|
$this->url = $url;
|
||||||
|
$this->l10n = $l10n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getId(): string {
|
||||||
|
return 'notes';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getTitle(): string {
|
||||||
|
return $this->l10n->t('Notes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getOrder(): int {
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getIconClass(): string {
|
||||||
|
return 'icon-notes';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getUrl(): ?string {
|
||||||
|
return $this->url->linkToRouteAbsolute('notes.page.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function load(): void {
|
||||||
|
\OCP\Util::addScript('notes', 'notes-dashboard');
|
||||||
|
}
|
||||||
|
}
|
|
@ -114,6 +114,38 @@ class NotesController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*/
|
||||||
|
public function dashboard() : JSONResponse {
|
||||||
|
return $this->helper->handleErrorResponse(function () {
|
||||||
|
$maxItems = 7;
|
||||||
|
$userId = $this->helper->getUID();
|
||||||
|
$notes = $this->notesService->getTopNotes($userId, $maxItems + 1);
|
||||||
|
$hasMoreNotes = count($notes) > $maxItems;
|
||||||
|
$notes = array_slice($notes, 0, $maxItems);
|
||||||
|
$items = array_map(function ($note) {
|
||||||
|
$excerpt = '';
|
||||||
|
try {
|
||||||
|
$excerpt = $note->getExcerpt();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'id' => $note->getId(),
|
||||||
|
'title' => $note->getTitle(),
|
||||||
|
'category' => $note->getCategory(),
|
||||||
|
'favorite' => $note->getFavorite(),
|
||||||
|
'excerpt' => $excerpt,
|
||||||
|
];
|
||||||
|
}, $notes);
|
||||||
|
return [
|
||||||
|
'items' => $items,
|
||||||
|
'hasMoreItems' => $hasMoreNotes,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,22 +4,41 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\Notes\Controller;
|
namespace OCA\Notes\Controller;
|
||||||
|
|
||||||
|
use OCA\Notes\Service\NotesService;
|
||||||
|
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||||
|
use OCP\AppFramework\Http\RedirectResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
use OCP\IUserSession;
|
||||||
|
|
||||||
class PageController extends Controller {
|
class PageController extends Controller {
|
||||||
public function __construct(string $AppName, IRequest $request) {
|
/** @NotesService */
|
||||||
|
private $notesService;
|
||||||
|
/** @var IUserSession */
|
||||||
|
private $userSession;
|
||||||
|
/** @IURLGenerator */
|
||||||
|
private $urlGenerator;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $AppName,
|
||||||
|
IRequest $request,
|
||||||
|
NotesService $notesService,
|
||||||
|
IUserSession $userSession,
|
||||||
|
IURLGenerator $urlGenerator
|
||||||
|
) {
|
||||||
parent::__construct($AppName, $request);
|
parent::__construct($AppName, $request);
|
||||||
|
$this->notesService = $notesService;
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->urlGenerator = $urlGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
*
|
|
||||||
* @return TemplateResponse
|
|
||||||
*/
|
*/
|
||||||
public function index() : TemplateResponse {
|
public function index() : TemplateResponse {
|
||||||
$devMode = !is_file(dirname(__FILE__).'/../../js/notes-main.js');
|
$devMode = !is_file(dirname(__FILE__).'/../../js/notes-main.js');
|
||||||
|
@ -35,4 +54,15 @@ class PageController extends Controller {
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*/
|
||||||
|
public function create() : RedirectResponse {
|
||||||
|
$note = $this->notesService->create($this->userSession->getUser()->getUID(), '', '');
|
||||||
|
$note->setContent('');
|
||||||
|
$url = $this->urlGenerator->linkToRoute('notes.page.index', [ 'id' => $note->getId() ]);
|
||||||
|
return new RedirectResponse($url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,22 @@ class Note {
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getExcerpt(int $maxlen = 100) : string {
|
||||||
|
$excerpt = trim($this->noteUtil->stripMarkdown($this->getContent()));
|
||||||
|
$title = $this->getTitle();
|
||||||
|
if (!empty($title)) {
|
||||||
|
$length = strlen($title);
|
||||||
|
if (strncasecmp($excerpt, $title, $length) === 0) {
|
||||||
|
$excerpt = substr($excerpt, $length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$excerpt = trim($excerpt);
|
||||||
|
if (strlen($excerpt) > $maxlen) {
|
||||||
|
$excerpt = substr($excerpt, 0, $maxlen) . '…';
|
||||||
|
}
|
||||||
|
return str_replace("\n", "\u{2003}", $excerpt);
|
||||||
|
}
|
||||||
|
|
||||||
public function getModified() : int {
|
public function getModified() : int {
|
||||||
return $this->file->getMTime();
|
return $this->file->getMTime();
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,15 @@ class NoteUtil {
|
||||||
return trim($str);
|
return trim($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function stripMarkdown(string $str) : string {
|
||||||
|
// prepare content: remove markdown characters and empty spaces
|
||||||
|
$str = preg_replace("/^\s*[*+-]\s+/mu", "", $str); // list item
|
||||||
|
$str = preg_replace("/^#+\s+(.*?)\s*#*$/mu", "$1", $str); // headline
|
||||||
|
$str = preg_replace("/^(=+|-+)$/mu", "", $str); // separate line for headline
|
||||||
|
$str = preg_replace("/(\*+|_+)(.*?)\\1/mu", "$2", $str); // emphasis
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a folder and creates it if non-existent
|
* Finds a folder and creates it if non-existent
|
||||||
* @param string $path path to the folder
|
* @param string $path path to the folder
|
||||||
|
|
|
@ -37,6 +37,20 @@ class NotesService {
|
||||||
return [ 'notes' => $notes, 'categories' => $data['categories'] ];
|
return [ 'notes' => $notes, 'categories' => $data['categories'] ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTopNotes(string $userId, int $count) : array {
|
||||||
|
$notes = $this->getAll($userId)['notes'];
|
||||||
|
usort($notes, function (Note $a, Note $b) {
|
||||||
|
$favA = $a->getFavorite();
|
||||||
|
$favB = $b->getFavorite();
|
||||||
|
if ($favA === $favB) {
|
||||||
|
return $b->getModified() - $a->getModified();
|
||||||
|
} else {
|
||||||
|
return $favA > $favB ? -1 : 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return array_slice($notes, 0, $count);
|
||||||
|
}
|
||||||
|
|
||||||
public function get(string $userId, int $id) : Note {
|
public function get(string $userId, int $id) : Note {
|
||||||
$notesFolder = $this->getNotesFolder($userId);
|
$notesFolder = $this->getNotesFolder($userId);
|
||||||
$note = new Note($this->getFileById($notesFolder, $id), $notesFolder, $this->noteUtil);
|
$note = new Note($this->getFileById($notesFolder, $id), $notesFolder, $this->noteUtil);
|
||||||
|
@ -89,11 +103,7 @@ class NotesService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitleFromContent(string $content) : string {
|
public function getTitleFromContent(string $content) : string {
|
||||||
// prepare content: remove markdown characters and empty spaces
|
$content = $this->noteUtil->stripMarkdown($content);
|
||||||
$content = preg_replace("/^\s*[*+-]\s+/mu", "", $content); // list item
|
|
||||||
$content = preg_replace("/^#+\s+(.*?)\s*#*$/mu", "$1", $content); // headline
|
|
||||||
$content = preg_replace("/^(=+|-+)$/mu", "", $content); // separate line for headline
|
|
||||||
$content = preg_replace("/(\*+|_+)(.*?)\\1/mu", "$2", $content); // emphasis
|
|
||||||
return $this->noteUtil->getSafeTitle($content);
|
return $this->noteUtil->getSafeTitle($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2731,6 +2731,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nextcloud/vue-dashboard": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nextcloud/vue-dashboard/-/vue-dashboard-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-QKOf0qm4UYobuC3djLYwICSuj29H6xnm24IHetc0AMsrfhwPNN1/CC5IWEl1uKCCvwI0NA02HXCVFLtUErlgyg==",
|
||||||
|
"requires": {
|
||||||
|
"@nextcloud/vue": "^2.3.0",
|
||||||
|
"core-js": "^3.6.4",
|
||||||
|
"vue": "^2.6.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nextcloud/webpack-vue-config": {
|
"@nextcloud/webpack-vue-config": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@nextcloud/webpack-vue-config/-/webpack-vue-config-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nextcloud/webpack-vue-config/-/webpack-vue-config-1.4.1.tgz",
|
||||||
|
@ -11328,6 +11338,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"webpack-merge": {
|
||||||
|
"version": "5.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.1.4.tgz",
|
||||||
|
"integrity": "sha512-LSmRD59mxREGkCBm9PCW3AaV4doDqxykGlx1NvioEE0FgkT2GQI54Wyvg39ptkiq2T11eRVoV39udNPsQvK+QQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"clone-deep": "^4.0.1",
|
||||||
|
"wildcard": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"webpack-sources": {
|
"webpack-sources": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
|
||||||
|
@ -11370,6 +11390,12 @@
|
||||||
"string-width": "^1.0.2 || 2"
|
"string-width": "^1.0.2 || 2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wildcard": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"word-wrap": {
|
"word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@nextcloud/moment": "^1.1.1",
|
"@nextcloud/moment": "^1.1.1",
|
||||||
"@nextcloud/router": "^1.2.0",
|
"@nextcloud/router": "^1.2.0",
|
||||||
"@nextcloud/vue": "^2.6.6",
|
"@nextcloud/vue": "^2.6.6",
|
||||||
|
"@nextcloud/vue-dashboard": "^1.0.1",
|
||||||
"@nextcloud/webpack-vue-config": "^1.4.1",
|
"@nextcloud/webpack-vue-config": "^1.4.1",
|
||||||
"easymde": "^2.12.0",
|
"easymde": "^2.12.0",
|
||||||
"markdown-it": "^11.0.1",
|
"markdown-it": "^11.0.1",
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
"vue-loader": "^15.9.3",
|
"vue-loader": "^15.9.3",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.12",
|
||||||
"webpack": "^4.44.2",
|
"webpack": "^4.44.2",
|
||||||
"webpack-cli": "^3.3.12"
|
"webpack-cli": "^3.3.12",
|
||||||
|
"webpack-merge": "^5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,19 @@ export const setSettings = settings => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getDashboardData = () => {
|
||||||
|
return axios
|
||||||
|
.get(url('/notes/dashboard'))
|
||||||
|
.then(response => {
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
handleSyncError(t('notes', 'Fetching notes for dashboard has failed.'), err)
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const fetchNotes = () => {
|
export const fetchNotes = () => {
|
||||||
const lastETag = store.state.sync.etag
|
const lastETag = store.state.sync.etag
|
||||||
const lastModified = store.state.sync.lastModified
|
const lastModified = store.state.sync.lastModified
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
<template>
|
||||||
|
<DashboardWidget :items="items"
|
||||||
|
:loading="loading"
|
||||||
|
:show-more-text="t('notes', 'notes')"
|
||||||
|
:show-more-url="showMoreUrl"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ item }">
|
||||||
|
<DashboardWidgetItem
|
||||||
|
:target-url="getItemTargetUrl(item)"
|
||||||
|
:main-text="item.title"
|
||||||
|
:sub-text="subtext(item)"
|
||||||
|
>
|
||||||
|
<template v-slot:avatar>
|
||||||
|
<div
|
||||||
|
class="note-item"
|
||||||
|
:class="{ 'note-item-favorite': item.favorite, 'note-item-no-favorites': !hasFavorites }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</DashboardWidgetItem>
|
||||||
|
</template>
|
||||||
|
<template v-slot:empty-content>
|
||||||
|
<EmptyContent icon="icon-notes">
|
||||||
|
<template #desc>
|
||||||
|
<p class="notes-empty-content-label">
|
||||||
|
{{ t('notes', 'No notes yet') }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a :href="createNoteUrl" class="button">{{ t('note', 'New note') }}</a>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</EmptyContent>
|
||||||
|
</template>
|
||||||
|
</DashboardWidget>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { DashboardWidget, DashboardWidgetItem } from '@nextcloud/vue-dashboard'
|
||||||
|
import { EmptyContent } from '@nextcloud/vue'
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
import { getDashboardData, categoryLabel } from '../NotesService'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Dashboard',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
DashboardWidget,
|
||||||
|
DashboardWidgetItem,
|
||||||
|
EmptyContent,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
items: [],
|
||||||
|
hasMoreItems: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
showMoreUrl() {
|
||||||
|
return this.hasMoreItems ? generateUrl('/apps/notes') : null
|
||||||
|
},
|
||||||
|
|
||||||
|
hasFavorites() {
|
||||||
|
return this.items.length > 0 && this.items[0].favorite
|
||||||
|
},
|
||||||
|
|
||||||
|
createNoteUrl() {
|
||||||
|
return generateUrl('/apps/notes/new')
|
||||||
|
},
|
||||||
|
|
||||||
|
getItemTargetUrl() {
|
||||||
|
return (note) => {
|
||||||
|
return generateUrl('/apps/notes/note/' + note.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.loadDashboardData()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
loadDashboardData() {
|
||||||
|
getDashboardData().then(data => {
|
||||||
|
this.items = data.items
|
||||||
|
this.hasMoreItems = data.hasMoreItems
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
subtext(item) {
|
||||||
|
return item.excerpt ? item.excerpt : categoryLabel(item.category)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.note-item-favorite {
|
||||||
|
background: var(--icon-star-dark-fc0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-size: 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item-no-favorites {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-empty-content-label {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Dashboard from './components/Dashboard'
|
||||||
|
|
||||||
|
Vue.mixin({ methods: { t, n } })
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
OCA.Dashboard.register('notes', (el) => {
|
||||||
|
const View = Vue.extend(Dashboard)
|
||||||
|
new View().$mount(el)
|
||||||
|
})
|
||||||
|
})
|
12
webpack.js
12
webpack.js
|
@ -1 +1,11 @@
|
||||||
module.exports = require('@nextcloud/webpack-vue-config')
|
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
|
const path = require('path')
|
||||||
|
const { merge } = require('webpack-merge')
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
entry: {
|
||||||
|
dashboard: path.join(__dirname, 'src', 'dashboard.js'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = merge(webpackConfig, config)
|
||||||
|
|
Loading…
Reference in New Issue