Skip to content

Nextcloud Calendar widget - #2287

Open
nxdjib wants to merge 1 commit into
lissy93:masterfrom
nxdjib:nextcloud-widget
Open

Nextcloud Calendar widget #2287
nxdjib wants to merge 1 commit into
lissy93:masterfrom
nxdjib:nextcloud-widget

Conversation

@nxdjib

@nxdjib nxdjib commented Aug 5, 2026

Copy link
Copy Markdown

Category

Widget

Overview

This PR adds a new Nextcloud Calendar widget to Dashy, allowing users to display their Nextcloud/ICS calendars directly within the dashboard.

The widget currently supports calendar events, with support for VTODO tasks planned for a future update.

Issue Number

#1201

Additional Info

This implementation was developed with the help of GitHub Copilot. I did my best to follow the existing code style and project conventions, but I'm still learning the codebase. If there are better ways to implement any part of this feature, I'd really appreciate your feedback and suggestions.

Thank you for taking the time to review this PR. I hope this widget can be a useful addition for other users, and I'm happy to make any changes that are needed.

Capture d'écran 2026-08-05 031625

@nxdjib
nxdjib requested a review from lissy93 as a code owner August 5, 2026 01:21
@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for dashy-dev ready!

Name Link
🔨 Latest commit 919be77
🔍 Latest deploy log https://app.netlify.com/projects/dashy-dev/deploys/6a729034950c5700088f1a2e
😎 Deploy Preview https://deploy-preview-2287--dashy-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@lissy93 lissy93 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Hey @nxdjib

Thanks for the PR :)

Although I do have some concerns around it's maintainability, as it's a quite a bit longer and more complex than it needs to be. Ideally, it should be short, neat and make use of the shared utils.

I wonder if maybe GitHub Co-pilot finds our codebase to big to fully understand. Because I don't think co-pilot's output is very good :(

Here's the summary:

  • The styles need to use the CSS vars, so theming works
  • Do not included widget-specific code in EditWidget.vue
  • Missing docs in docs/widgets.md
  • Don't edit the main conf.yml here
  • Hard-coded strings can move to en.json
  • Some of the code is longer, more verbose than needed

Comment on lines +364 to +376
color: #ffffff !important;
background: rgba(255, 255, 255, 0.08) !important;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 0.8rem;
padding: 0.8rem;
margin-bottom: 0.45rem;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
transition: transform 120ms ease, background 120ms ease;
}
.event-card:hover,
.tasks-list li:hover {
transform: translateY(-1px);
background: rgba(255, 255, 255, 0.14) !important;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

These should probably use the CSS variables, so that the widget gets styled with the user's style

Comment thread user-data/conf.yml
Comment on lines +59 to +71

- name: Nextcloud
icon: fas fa-calendar-alt
widgets:
- type: nextcloud-calendar
label: Nextcloud Calendar (public)
options:
icsUrl: https://example.com/your-calendar.ics
useProxy: true
pastDays: 0
showTasks: true
limit: 5
taskLimit: 20

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

You can delete this from the main conf.yml
Please don't modify this, it's used for our dev instance and shipped as the sample config ;)

{{ $t('interactive-editor.edit-widget.options-heading') }}
</h4>

<div v-if="currentOptionDefinitions.length">

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

What's happening here?
A widget code should never be in the generic form like this, it needs to be fully standalone. Otherwise everyone not using calendar widget is going to be loading unused code

return raw;
};

const widgetOptionDefinitions = {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This needs to go in the schema, not here. The it will be loaded automatically, no need to manage it here

<select id="event-count-select" v-model="selectedDisplayLimit">
<option v-for="item in displayOptions" :key="item.value" :value="item.value">{{ item.label }}</option>
</select>
<span class="display-label">events</span>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Probably fine, but this string isn't translatable if it's not moved into en.json

Comment on lines +72 to +79
const defaultOptions = [3, 5, 8, 10, 15];
const limit = parseInt(this.options.limit, 10) || 5;
const options = defaultOptions.includes(limit)
? [...defaultOptions]
: [limit, ...defaultOptions].sort((a, b) => a - b);
return [...options.map((value) => ({ value, label: String(value) })), { value: 'all', label: 'All' }];
},
displayLimit() {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think this can be simplified and neatened

icsUrl() {
if (this.options.icsUrl) return this.parseAsEnvVar(this.options.icsUrl);
if (this.options.calendarId) {
return `${this.hostname}/remote.php/dav/calendars/${this.username}/${this.options.calendarId}`;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Does this work? I tried to test the PR, and it wasn't wworking with a test nextcloud instance (I might not have enabeled an option somewhere) but it looks like this endpoint was always returning a 405 without ?export appended

return this.options.showTasks !== false && this.options.showTasks !== 'false';
},
showEvents() {
return true;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

probably not really needed

if (sameDay) {
return `${this.formatEventDate(start, false)} ${this.formatTime(start)} – ${this.formatTime(end)}`;
}
return `${this.formatEventDate(start, false)} ${this.formatTime(start)} – ${this.formatEventDate(end, false)} ${this.formatTime(end)}`;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

We've already got some date parsing utils you can use, instead of re-inventing. In MiscHelpers there's: timestampToDate, timestampToTime, timestampToDateTime, getTimeAgo, etc which the other widgets use.

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.

2 participants