Add dashboard widget: On this day

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
Louis Chemineau 2024-03-20 14:21:18 +01:00
parent c297a2d3de
commit 8de134984c
No known key found for this signature in database
7 changed files with 210 additions and 4 deletions

View File

@ -28,6 +28,7 @@ namespace OCA\Photos\AppInfo;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\Files\Event\LoadSidebar;
use OCA\Photos\Dashboard\OnThisDay;
use OCA\Photos\Listener\AlbumsManagementEventListener;
use OCA\Photos\Listener\CSPListener;
use OCA\Photos\Listener\ExifMetadataProvider;
@ -80,6 +81,7 @@ class Application extends App implements IBootstrap {
}
public function register(IRegistrationContext $context): void {
$context->registerDashboardWidget(OnThisDay::class);
/** Register $principalBackend for the DAV collection */
$context->registerServiceAlias('principalBackend', Principal::class);

View File

@ -0,0 +1,63 @@
<?php
namespace OCA\Photos\Dashboard;
use OCA\Photos\AppInfo\Application;
use OCP\AppFramework\Services\IInitialState;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Util;
class OnThisDay implements IWidget {
public function __construct(
private IL10N $l,
private IURLGenerator $url,
private IInitialState $initialState
) {
}
/**
* @inheritDoc
*/
public function getId(): string {
return 'photos.onthisday';
}
/**
* @inheritDoc
*/
public function getTitle(): string {
return $this->l->t('On This Day');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 20;
}
/**
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-calendar-dark';
}
/**
* @inheritDoc
*/
public function getUrl(): ?string {
return $this->url->linkToRoute('photos.page.indexthisday');
}
/**
* @inheritDoc
*/
public function load(): void {
Util::addScript('photos', 'photos-dashboard');
$this->initialState->provideInitialState('image-mimes', Application::IMAGE_MIMES);
$this->initialState->provideInitialState('video-mimes', Application::VIDEO_MIMES);
}
}

View File

@ -0,0 +1,88 @@
<!--
- Copyright (c) 2020. The Nextcloud Bookmarks contributors.
-
- This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
-->
<template>
<div class="on-this-day-dashboard">
<NcLoadingIcon v-if="loading" :size="48" />
<NcEmptyContent v-else-if="items.length === 0"
:name="t('photos', 'No picture for this day')"
:description="t('photos', 'Picture taken on this day will show up here.')">
<template #icon>
<ImageIcon />
</template>
</NcEmptyContent>
<template v-else>
<File class="on-this-day-dashboard__file"
:file="items[0]"
:allow-selection="false" />
<NcButton :href="moreUrl">
{{ t('photos', 'More photos from this day') }}
</NcButton>
</template>
</div>
</template>
<script>
import Image from 'vue-material-design-icons/Image.vue'
import { generateUrl } from '@nextcloud/router'
import { NcButton, NcLoadingIcon, NcEmptyContent } from '@nextcloud/vue'
import getPhotos from '../../services/PhotoSearch.js'
import { allMimes } from '../../services/AllowedMimes.js'
import File from '../File.vue'
import logger from '../../services/logger.js'
export default {
name: 'DashboardOnThisDay',
components: {
File,
NcButton,
NcLoadingIcon,
NcEmptyContent,
ImageIcon: Image,
},
data() {
return {
loading: true,
items: [],
}
},
computed: {
moreUrl() {
return generateUrl('/apps/photos/thisday')
},
},
async created() {
try {
this.items = await getPhotos({
firstResult: 0,
nbResults: 1,
mimesType: allMimes,
onThisDay: true,
})
} catch (error) {
logger.error('Failed to load on this day pictures', { error })
} finally {
this.loading = false
}
},
}
</script>
<style lang="scss" scoped>
.on-this-day-dashboard {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
gap: 16px;
.file-container {
flex-grow: 1;
border: none;
}
}
</style>

View File

@ -107,7 +107,7 @@ export default {
},
selected: {
type: Boolean,
required: true,
default: false,
},
allowSelection: {
type: Boolean,
@ -115,7 +115,7 @@ export default {
},
distance: {
type: Number,
required: true,
default: 0,
},
},
@ -221,7 +221,7 @@ export default {
},
getItemURL(size) {
const token = this.$route.params.token
const token = this.$route?.params.token
if (token) {
return generateUrl(`/apps/photos/api/v1/publicPreview/${this.file.fileid}?etag=${this.decodedEtag}&x=${size}&y=${size}&token=${token}`)
} else {

53
src/dashboard.js Normal file
View File

@ -0,0 +1,53 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
import { generateFilePath } from '@nextcloud/router'
import { getRequestToken } from '@nextcloud/auth'
import { translate, translatePlural } from '@nextcloud/l10n'
import Vue from 'vue'
import store from './store/index.js'
import DashboardOnThisDay from './components/Dashboard/DashboardOnThisDay.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('photos', '', 'js/')
Vue.prototype.t = translate
Vue.prototype.n = translatePlural
window.addEventListener('DOMContentLoaded', () => {
OCA.Dashboard.register('photos.onthisday', (el) => {
global.PhotosOnThisDay = new Vue({
el,
store,
render: h => h(DashboardOnThisDay),
})
})
})

View File

@ -31,7 +31,6 @@ import store from '../store/index.js'
/**
* List files from a folder and filter out unwanted mimes
*
* @param {object} path the lookup path
* @param {object} [options] used for the cancellable requests
* @param {number} [options.firstResult=0] Index of the first result that we want (starts at 0)
* @param {number} [options.nbResults=200] The number of file to fetch

View File

@ -13,6 +13,7 @@ webpackConfig.entry = {
main: path.join(__dirname, 'src', 'main.js'),
public: path.join(__dirname, 'src', 'public.js'),
sidebar: path.join(__dirname, 'src', 'sidebar.js'),
dashboard: path.join(__dirname, 'src', 'dashboard.js'),
}
webpackRules.RULE_JS.exclude = BabelLoaderExcludeNodeModulesExcept([