Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
],
],
['name' => 'settings#generateIframeToken', 'url' => 'settings/generateToken/{type}', 'verb' => 'GET'],
['name' => 'settings#setOverviewGridView', 'url' => 'settings/overview/grid_view', 'verb' => 'PUT'],

// Direct Editing: Webview
['name' => 'directView#show', 'url' => '/direct/{token}', 'verb' => 'GET'],
Expand Down
7 changes: 7 additions & 0 deletions lib/Controller/OverviewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IPreview;
use OCP\IRequest;
use OCP\Util;
Expand All @@ -27,6 +28,8 @@ public function __construct(
private IEventDispatcher $eventDispatcher,
private IInitialState $initialState,
private IPreview $preview,
private IConfig $config,
private ?string $userId,
) {
parent::__construct($appName, $request);
}
Expand All @@ -40,6 +43,10 @@ public function index(): TemplateResponse {
Util::addScript('richdocuments', 'richdocuments-overview');

$this->initialState->provideInitialState('previewEnabled', $this->preview->isMimeSupported('application/vnd.oasis.opendocument.text'));
$this->initialState->provideInitialState('overview_config', [
'overview_grid_view' => $this->userId !== null
&& $this->config->getUserValue($this->userId, 'richdocuments', 'overview_grid_view', '0') === '1',
]);

// Viewer is pre-installed in production but may not be available in other environments
if (class_exists(LoadViewer::class)) {
Expand Down
9 changes: 9 additions & 0 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,15 @@ public function setPersonalSettings($templateFolder,
return new JSONResponse($response);
}

#[NoAdminRequired]
public function setOverviewGridView(bool $value): JSONResponse {
if ($this->userId === null) {
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
}
$this->config->setUserValue($this->userId, 'richdocuments', 'overview_grid_view', $value ? '1' : '0');
return new JSONResponse(['message' => 'ok']);
}

/**
* @NoAdminRequired
* @PublicPage
Expand Down
19 changes: 9 additions & 10 deletions src/components/FileCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-->

<template>
<div class="file-card" @click="$emit('click', $event)">
<button type="button" class="file-card" @click="$emit('click', $event)">
<div class="file-card__preview">
<slot name="preview" />
</div>
Expand All @@ -16,7 +16,7 @@
<slot name="subname" />
</div>
</div>
</div>
</button>
</template>

<script>
Expand All @@ -26,14 +26,6 @@ export default {
</script>

<style scoped>
/* Nextcloud's global reset sets cursor:default on div, img and span explicitly,
which prevents inheritance. Override for all slotted content. */
.file-card ::v-deep div,
.file-card ::v-deep img,
.file-card ::v-deep span {
cursor: pointer;
}

.file-card {
display: flex;
flex-direction: column;
Expand All @@ -44,7 +36,14 @@ export default {
padding: calc(var(--default-grid-baseline) * 3);
cursor: pointer;
box-sizing: border-box;
background: none;
text-align: left;
transition: box-shadow 0.2s ease, transform 0.2s ease;

&:focus-visible {
outline: 2px solid var(--color-primary-element);
outline-offset: 2px;
}
}

.file-card:hover {
Expand Down
49 changes: 28 additions & 21 deletions src/components/TemplateSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<section class="template-section">
<h3 class="template-section__heading">
<section class="template-section" aria-labelledby="template-section-heading">
<h2 id="template-section-heading" class="template-section__heading">
{{ t('richdocuments', 'Create new') }}
</h3>
</h2>

<ul class="template-section__list">
<!-- Blank file card -->
<li class="template-section__item">
<button class="template-card" @click="$emit('select', creator, null)">
<span class="template-card__preview template-card__preview--blank"
:style="previewStyle">
<span class="template-card__preview template-card__preview--blank">
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="template-card__icon" v-html="creator.iconSvgInline" />
</span>
Expand All @@ -26,12 +25,14 @@
:key="template.fileid"
class="template-section__item">
<button class="template-card" @click="$emit('select', creator, template)">
<span class="template-card__preview" :style="previewStyle">
<img v-if="template.hasPreview"
:src="template.previewUrl"
<span class="template-card__preview">
<img v-if="template.hasPreview && !failedPreviews[template.fileid]"
:src="templatePreviewUrl(template)"
:alt="nameWithoutExt(template.basename)"
loading="lazy"
class="template-card__image">
class="template-card__image"
@error="failedPreviews = { ...failedPreviews, [template.fileid]: true }">
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-else class="template-card__icon" v-html="creator.iconSvgInline" />
</span>
<span class="template-card__name">{{ nameWithoutExt(template.basename) }}</span>
Expand All @@ -42,6 +43,8 @@
</template>

<script>
import { generateUrl } from '@nextcloud/router'

export default {
name: 'TemplateSection',

Expand All @@ -54,34 +57,41 @@ export default {

emits: ['select'],

computed: {
previewStyle() {
if (!this.creator.ratio) {
return {}
}
// ratio is width/height; convert to padding-bottom trick
return { paddingBottom: `${(1 / this.creator.ratio) * 100}%` }
},
data() {
return {
failedPreviews: {},
}
},

methods: {
nameWithoutExt(basename) {
const dot = basename.lastIndexOf('.')
return dot > 0 ? basename.slice(0, dot) : basename
},

templatePreviewUrl(template) {
if (template.previewUrl) {
return template.previewUrl
}
return generateUrl('/core/preview?fileId={fileid}&x=256&y=256&a=1', { fileid: template.fileid })
},
},
}
</script>

<style scoped>
.template-section {
padding: calc(var(--default-grid-baseline) * 4);
margin: calc(var(--default-grid-baseline) * 4) calc(var(--default-grid-baseline) * 4) 0;
background-color: var(--color-background-hover);
border-radius: var(--border-radius-large);
}

.template-section__heading {
margin: 0 0 calc(var(--default-grid-baseline) * 2);
font-size: var(--default-font-size);
font-weight: 600;
color: var(--color-text-maxcontrast);
}

.template-section__list {
Expand Down Expand Up @@ -118,18 +128,15 @@ export default {
.template-card__preview {
position: relative;
width: 100%;
aspect-ratio: 2 / 3;
overflow: hidden;
border: 2px solid var(--color-border);
border-radius: var(--border-radius-large);
background-color: var(--color-main-background);
box-sizing: border-box;
}

.template-card__preview--blank {
display: flex;
align-items: center;
justify-content: center;
min-height: 120px;
}

.template-card__image {
Expand Down
11 changes: 11 additions & 0 deletions src/services/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'

export async function setOverviewGridView(value) {

Check warning on line 9 in src/services/config.js

View workflow job for this annotation

GitHub Actions / NPM lint

Missing JSDoc comment
await axios.put(generateUrl('/apps/richdocuments/settings/overview/grid_view'), { value })
}
Loading
Loading