Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
29 changes: 29 additions & 0 deletions css/fullcalendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
*
*/

.fc-event-nc-task-completed,
.fc-event-nc-tentative,
.fc-event-nc-cancelled {
opacity: .5;
}

.fc-event-nc-task-completed,
.fc-event-nc-cancelled {
text-decoration: line-through !important;
}
Expand Down Expand Up @@ -185,6 +187,33 @@
}
}

// Task icon on events which are VTODO
.fc-event-nc-task {
.icon-event-task {
display: inline-block;
vertical-align: bottom;
border: 1px solid black;
border-radius: 1px;
margin: 2px 3px 2px 1px;
min-width: 9px;
min-height: 9px;
&--light {
border-color: #ffffff;
}
&--checked--light {
background-size: 9px;
@include icon-color('checkmark', 'actions', '#ffffff', 1, true);
}
&--dark {
border-color: #000000;
}
&--checked--dark {
background-size: 9px;
@include icon-color('checkmark', 'actions', '#000000', 1, true);
}
}
}

.fc-content {
background-color: inherit;
text-overflow: ellipsis;
Expand Down
3 changes: 3 additions & 0 deletions lib/Controller/PublicViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ private function publicIndex(string $token,
$defaultSkipPopover = $this->config->getAppValue($this->appName, 'skipPopover', 'yes');
$defaultTimezone = $this->config->getAppValue($this->appName, 'timezone', 'automatic');
$defaultSlotDuration = $this->config->getAppValue($this->appName, 'slotDuration', '00:30:00');
$defaultShowTasks = $this->config->getAppValue($this->appName, 'showTasks', 'yes');

$appVersion = $this->config->getAppValue($this->appName, 'installed_version');

Expand All @@ -133,6 +134,8 @@ private function publicIndex(string $token,
$this->initialStateService->provideInitialState($this->appName, 'talk_enabled', false);
$this->initialStateService->provideInitialState($this->appName, 'timezone', $defaultTimezone);
$this->initialStateService->provideInitialState($this->appName, 'slot_duration', $defaultSlotDuration);
$this->initialStateService->provideInitialState($this->appName, 'show_tasks', $defaultShowTasks === 'yes');
$this->initialStateService->provideInitialState($this->appName, 'tasks_enabled', false);

return new TemplateResponse($this->appName, 'main', [
'share_url' => $this->getShareURL(),
Expand Down
27 changes: 27 additions & 0 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public function setConfig(string $key,
return $this->setEventLimit($value);
case 'slotDuration':
return $this->setSlotDuration($value);
case 'showTasks':
return $this->setShowTasks($value);
default:
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
}
Expand Down Expand Up @@ -143,6 +145,31 @@ private function setSkipPopover(string $value):JSONResponse {
return new JSONResponse();
}

/**
* set config value for showing tasks
*
* @param $value User-selected option whether or not to show tasks
* @return JSONResponse
*/
private function setShowTasks(string $value):JSONResponse {
if (!\in_array($value, ['yes', 'no'])) {
return new JSONResponse([], Http::STATUS_UNPROCESSABLE_ENTITY);
}

try {
$this->config->setUserValue(
$this->userId,
$this->appName,
'showTasks',
$value
);
} catch (\Exception $e) {
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}

return new JSONResponse();
}

/**
* set config value for showing week numbers
*
Expand Down
8 changes: 7 additions & 1 deletion lib/Controller/ViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public function index():TemplateResponse {
$defaultSkipPopover = $this->config->getAppValue($this->appName, 'skipPopover', 'no');
$defaultTimezone = $this->config->getAppValue($this->appName, 'timezone', 'automatic');
$defaultSlotDuration = $this->config->getAppValue($this->appName, 'slotDuration', '00:30:00');
$defaultShowTasks = $this->config->getAppValue($this->appName, 'showTasks', 'yes');

$appVersion = $this->config->getAppValue($this->appName, 'installed_version');
$eventLimit = $this->config->getUserValue($this->userId, $this->appName, 'eventLimit', $defaultEventLimit) === 'yes';
Expand All @@ -102,9 +103,12 @@ public function index():TemplateResponse {
$showWeekends = $this->config->getUserValue($this->userId, $this->appName, 'showWeekends', $defaultShowWeekends) === 'yes';
$showWeekNumbers = $this->config->getUserValue($this->userId, $this->appName, 'showWeekNr', $defaultWeekNumbers) === 'yes';
$skipPopover = $this->config->getUserValue($this->userId, $this->appName, 'skipPopover', $defaultSkipPopover) === 'yes';
$talkEnabled = $this->appManager->isEnabledForUser('spreed');
$timezone = $this->config->getUserValue($this->userId, $this->appName, 'timezone', $defaultTimezone);
$slotDuration = $this->config->getUserValue($this->userId, $this->appName, 'slotDuration', $defaultSlotDuration);
$showTasks = $this->config->getUserValue($this->userId, $this->appName, 'showTasks', $defaultShowTasks) === 'yes';

$talkEnabled = $this->appManager->isEnabledForUser('spreed');
$tasksEnabled = $this->appManager->isEnabledForUser('tasks');

$this->initialStateService->provideInitialState($this->appName, 'app_version', $appVersion);
$this->initialStateService->provideInitialState($this->appName, 'event_limit', $eventLimit);
Expand All @@ -116,6 +120,8 @@ public function index():TemplateResponse {
$this->initialStateService->provideInitialState($this->appName, 'talk_enabled', $talkEnabled);
$this->initialStateService->provideInitialState($this->appName, 'timezone', $timezone);
$this->initialStateService->provideInitialState($this->appName, 'slot_duration', $slotDuration);
$this->initialStateService->provideInitialState($this->appName, 'show_tasks', $showTasks);
$this->initialStateService->provideInitialState($this->appName, 'tasks_enabled', $tasksEnabled);

return new TemplateResponse($this->appName, 'main');
}
Expand Down
25 changes: 23 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@fullcalendar/vue": "4.3.1",
"@nextcloud/auth": "^1.2.3",
"@nextcloud/axios": "^1.3.2",
"@nextcloud/dialogs": "^1.2.2",
"@nextcloud/initial-state": "^1.1.2",
"@nextcloud/l10n": "^1.2.3",
"@nextcloud/logger": "^1.1.2",
Expand Down
21 changes: 21 additions & 0 deletions src/components/AppNavigation/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
@update:checked="toggleBirthdayEnabled">
{{ $t('calendar', 'Enable birthday calendar') }}
</ActionCheckbox>
<ActionCheckbox
class="settings-fieldset-interior-item"
:checked="showTasks"
:disabled="savingTasks"
@update:checked="toggleTasksEnabled">
{{ $t('calendar', 'Show tasks in calendar') }}
</ActionCheckbox>
<ActionCheckbox
class="settings-fieldset-interior-item"
:checked="showPopover"
Expand Down Expand Up @@ -118,6 +125,7 @@ export default {
return {
savingBirthdayCalendar: false,
savingEventLimit: false,
savingTasks: false,
savingPopover: false,
savingSlotDuration: false,
savingWeekend: false,
Expand All @@ -131,6 +139,7 @@ export default {
...mapState({
eventLimit: state => state.settings.eventLimit,
showPopover: state => !state.settings.skipPopover,
showTasks: state => state.settings.showTasks,
showWeekends: state => state.settings.showWeekends,
showWeekNumbers: state => state.settings.showWeekNumbers,
slotDuration: state => state.settings.slotDuration,
Expand Down Expand Up @@ -205,6 +214,18 @@ export default {
this.savingEventLimit = false
}
},
async toggleTasksEnabled() {
// change to loading status
this.savingTasks = true
try {
await this.$store.dispatch('toggleTasksEnabled')
this.savingTasks = false
} catch (error) {
console.error(error)
this.$toast.error(this.$t('calendar', 'New setting was not saved successfully.'))
this.savingTasks = false
}
},
async togglePopoverEnabled() {
// change to loading status
this.savingPopover = true
Expand Down
81 changes: 63 additions & 18 deletions src/fullcalendar/eventClick.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
*
*/
import { getPrefixedRoute } from '../utils/router'
import { generateUrl } from '@nextcloud/router'
import { translate as t } from '@nextcloud/l10n'
import { showInfo } from '@nextcloud/dialogs'

/**
* Returns a function for click action on event. This will open the editor.
Expand All @@ -33,27 +36,69 @@ import { getPrefixedRoute } from '../utils/router'
*/
export default function(store, router, route, window) {
return function({ event }) {
let desiredRoute = store.state.settings.skipPopover
? 'EditSidebarView'
: 'EditPopoverView'
switch (event.extendedProps.objectType) {
case 'VEVENT':
handleEventClick(event, store, router, route, window)
break

if (window.innerWidth <= 768 && desiredRoute === 'EditPopoverView') {
desiredRoute = 'EditSidebarView'
case 'VTODO':
handleToDoClick(event, store, window)
break
}
}
}

const name = getPrefixedRoute(route.name, desiredRoute)
const params = Object.assign({}, route.params, {
object: event.extendedProps.objectId,
recurrenceId: String(event.extendedProps.recurrenceId),
})

// Don't push new route when day didn't change
if ((getPrefixedRoute(route.name, 'EditPopoverView') === route.name || getPrefixedRoute(route.name, 'EditSidebarView') === route.name)
&& params.object === route.params.object
&& params.recurrenceId === route.params.recurrenceId) {
return
}
/**
* Handle eventClick for VEVENT
*
* @param {EventDef} event FullCalendar event
* @param {Object} store The Vuex store
* @param {Object} router The Vue router
* @param {Object} route The current Vue route
* @param {Window} window The window object
*/
function handleEventClick(event, store, router, route, window) {
let desiredRoute = store.state.settings.skipPopover
? 'EditSidebarView'
: 'EditPopoverView'

if (window.innerWidth <= 768 && desiredRoute === 'EditPopoverView') {
desiredRoute = 'EditSidebarView'
}

router.push({ name, params })
const name = getPrefixedRoute(route.name, desiredRoute)
const params = Object.assign({}, route.params, {
object: event.extendedProps.objectId,
recurrenceId: String(event.extendedProps.recurrenceId),
})

// Don't push new route when day didn't change
if ((getPrefixedRoute(route.name, 'EditPopoverView') === route.name || getPrefixedRoute(route.name, 'EditSidebarView') === route.name)
&& params.object === route.params.object
&& params.recurrenceId === route.params.recurrenceId) {
return
}

router.push({ name, params })
}

/**
* Handle eventClick for VTODO
*
* @param {EventDef} event FullCalendar event
* @param {Object} store The Vuex store
* @param {Window} window The window object
*/
function handleToDoClick(event, store, window) {
if (!store.state.settings.tasksEnabled) {
showInfo(t('calendar', 'Please ask your administrator to enable the Tasks App.'))
return
}

const davUrlParts = event.extendedProps.davUrl.split('/')
const taskId = davUrlParts.pop()
const calendarId = davUrlParts.pop()
const url = `apps/tasks/#/calendars/${calendarId}/tasks/${taskId}`

window.location = window.location.protocol + '//' + window.location.host + generateUrl(url)
}
19 changes: 19 additions & 0 deletions src/fullcalendar/eventRender.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ export default function({ event, el }) {
el.firstChild.appendChild(notificationIcon)
}

if (el.classList.contains('fc-event-nc-task')) {
const taskIcon = document.createElement('span')
taskIcon.type = 'checkbox'
taskIcon.classList.add('icon-event-task')
if (event.extendedProps.darkText) {
taskIcon.classList.add('icon-event-task--dark')
} else {
taskIcon.classList.add('icon-event-task--light')
}
if (event.extendedProps.percent && event.extendedProps.percent === 100) {
if (event.extendedProps.darkText) {
taskIcon.classList.add('icon-event-task--checked--dark')
} else {
taskIcon.classList.add('icon-event-task--checked--light')
}
}
el.firstChild.insertBefore(taskIcon, el.firstChild.firstChild)
}

if (event.source === null) {
el.dataset.isNew = 'yes'
} else {
Expand Down
Loading