Skip to content

Commit

Permalink
Merge pull request #1649 from nextcloud/enh/createTaskFromTalkMsg
Browse files Browse the repository at this point in the history
Create task from Talk message
  • Loading branch information
raimund-schluessler committed Jul 13, 2021
2 parents 31c7e1b + 033d851 commit b2584f7
Show file tree
Hide file tree
Showing 11 changed files with 450 additions and 42 deletions.
1 change: 1 addition & 0 deletions css/tasks-talk.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include icon-black-white('tasks', 'tasks', 1);
4 changes: 4 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
namespace OCA\Tasks\AppInfo;

use OCA\Tasks\Dashboard\TasksWidget;
use OCA\Tasks\Listeners\BeforeTemplateRenderedListener;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;

class Application extends App implements IBootstrap {

Expand All @@ -42,6 +44,8 @@ public function __construct(array $params=[]) {

public function register(IRegistrationContext $context): void {
$context->registerDashboardWidget(TasksWidget::class);

$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
}

public function boot(IBootContext $context): void {
Expand Down
58 changes: 58 additions & 0 deletions lib/Listeners/BeforeTemplateRenderedListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
/**
* @copyright Copyright (c) 2021 Jakob Röhrl <jakob.roehrl@web.de>
*
* @author Julius Härtl <jus@bitgrid.net>
* @author Jakob Röhrl <jakob.roehrl@web.de>
*
* @license GNU AGPL version 3 or any later version
*
* 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/>.
*
*/

declare(strict_types=1);


namespace OCA\Tasks\Listeners;

use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\IRequest;
use OCP\Util;

class BeforeTemplateRenderedListener implements IEventListener {
private $request;

public function __construct(IRequest $request) {
$this->request = $request;
}

public function handle(Event $event): void {
if (!($event instanceof BeforeTemplateRenderedEvent)) {
return;
}

if (!$event->isLoggedIn()) {
return;
}

$pathInfo = $this->request->getPathInfo();
if (strpos($pathInfo, '/call/') === 0 || strpos($pathInfo, '/apps/spreed') === 0) {
Util::addScript('tasks', 'tasks-talk');
Util::addStyle('tasks', 'tasks-talk');
}
}
}
8 changes: 5 additions & 3 deletions src/components/AppSidebar/CalendarPickerItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
:disabled="isDisabled"
:options="calendars"
:value="calendar"
:placeholder="$t('tasks', 'Select a calendar')"
:placeholder="translate('tasks', 'Select a calendar')"
@select="change">
<template slot="singleLabel" slot-scope="scope">
<CalendarPickerOption v-bind="scope.option" />
Expand All @@ -43,7 +43,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
color=""
owner=""
:is-shared-with-me="false"
:display-name="$t('tasks', 'No calendar matches the search.')" />
:display-name="translate('tasks', 'No calendar matches the search.')" />
</template>
</Multiselect>
</div>
Expand All @@ -52,6 +52,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<script>
import CalendarPickerOption from './CalendarPickerOption.vue'
import { translate } from '@nextcloud/l10n'
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
export default {
Expand All @@ -62,7 +63,7 @@ export default {
props: {
calendar: {
type: Object,
required: true,
default: null,
},
calendars: {
type: Array,
Expand All @@ -79,6 +80,7 @@ export default {
},
},
methods: {
translate,
/**
* TODO: this should emit the calendar id instead
* @param {Object} newCalendar The selected calendar
Expand Down
206 changes: 206 additions & 0 deletions src/components/TaskCreateDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<!--
Nextcloud - Tasks
@author Julius Härtl
@copyright 2021 Julius Härtl <jus@bitgrid.net>
@author Jakob Röhrl
@copyright 2021 Jakob Röhrl <jakob.roehrl@web.de>
@author Raimund Schlüßler
@copyright 2021 Raimund Schlüßler <raimund.schluessler@mailbox.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
License as published by the Free Software Foundation; either
version 3 of the License, or any later version.
This library 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 library. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
<Modal class="task-selector" @close="close">
<div class="modal-scroller">
<div v-if="!creating && !created" id="modal-inner" :class="{ 'icon-loading': loading }">
<h3>{{ t('tasks', 'Create a new task') }}</h3>

<CalendarPickerItem
:disabled="loading"
:calendar="pendingCalendar"
:calendars="writableCalendars"
@changeCalendar="changeCalendar" />

<input v-model="pendingTitle"
type="text"
:placeholder="t('tasks', 'Create a new task')"
:disabled="loading">

<textarea v-model="pendingDescription"
:disabled="loading" />
<div class="modal-buttons">
<button @click="close">
{{ t('tasks', 'Cancel') }}
</button>
<button :disabled="loading"
class="primary"
@click="addTask">
{{ t('tasks', 'Create task') }}
</button>
</div>
</div>
<div v-else id="modal-inner">
<EmptyContent v-if="creating" icon="icon-loading">
{{ t('tasks', 'Creating the new task…') }}
</EmptyContent>
<EmptyContent v-else-if="created" icon="icon-checkmark">
{{ t('tasks', '"{task}" was added to "{calendar}"', { task: pendingTitle, calendar: pendingCalendar.displayName }, undefined, { sanitize: false, escape: false }) }}
<template #desc>
<button class="primary" @click="openNewTask">
{{ t('tasks', 'Open task') }}
</button>
<button @click="close">
{{ t('tasks', 'Close') }}
</button>
</template>
</EmptyContent>
</div>
</div>
</Modal>
</template>

<script>
import CalendarPickerItem from './AppSidebar/CalendarPickerItem.vue'
import client from '../services/cdav.js'
import { translate as t } from '@nextcloud/l10n'
import { generateUrl } from '@nextcloud/router'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
import Modal from '@nextcloud/vue/dist/Components/Modal'
import { mapGetters, mapActions } from 'vuex'
export default {
name: 'TaskCreateDialog',
components: {
CalendarPickerItem,
EmptyContent,
Modal,
},
props: {
title: {
type: String,
default: '',
},
description: {
type: String,
default: '',
},
},
data() {
return {
pendingTitle: '',
pendingDescription: '',
pendingCalendar: null,
loading: true,
creating: false,
created: false,
newTask: null,
}
},
computed: {
...mapGetters({
writableCalendars: 'getSortedWritableCalendars',
defaultCalendar: 'getDefaultCalendar',
}),
},
beforeMount() {
this.fetchCalendars()
},
mounted() {
this.pendingTitle = this.title
this.pendingDescription = this.description
},
methods: {
...mapActions([
'createTask',
]),
t,
changeCalendar(calendar) {
this.pendingCalendar = calendar
},
close() {
this.$root.$emit('close')
},
async fetchCalendars() {
this.loading = true
await client.connect({ enableCalDAV: true })
await this.$store.dispatch('fetchCurrentUserPrincipal')
await this.$store.dispatch('getCalendars')
// TODO: Would be good to select the default calendar instead of the first one
this.pendingCalendar = this.writableCalendars[0]
this.loading = false
},
async addTask() {
this.creating = true
const task = {
summary: this.pendingTitle,
note: this.pendingDescription,
calendar: this.pendingCalendar,
}
this.newTask = await this.createTask(task)
this.creating = false
this.created = true
},
openNewTask() {
window.location = generateUrl('apps/tasks') + `/#/calendars/${this.pendingCalendar.id}/tasks/${this.newTask.uri}`
},
},
}
</script>

<style lang="scss" scoped>
.modal-scroller {
overflow: scroll;
max-height: calc(80vh - 40px);
margin: 10px;
}
#modal-inner {
width: 90vw;
max-width: 400px;
padding: 10px;
min-height: 200px;
}
input, textarea {
width: 100%;
margin-bottom: 10px !important;
}
.modal-buttons {
display: flex;
justify-content: flex-end;
}
.task-selector::v-deep .modal-container {
overflow: visible !important;
}
</style>
50 changes: 50 additions & 0 deletions src/helpers/selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* @copyright Copyright (c) 2021 Jakob Röhrl <jakob.roehrl@web.de>
*
* @author Julius Härtl <jus@bitgrid.net>
* @author Jakob Röhrl <jakob.roehrl@web.de>
*
* @license GNU AGPL version 3 or any later version
*
* 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 Vue from 'vue'
import store from '../store/store.js'

const buildSelector = (selector, propsData = {}) => {
return new Promise((resolve, reject) => {
const container = document.createElement('div')
document.getElementById('body-user').append(container)
const View = Vue.extend(selector)
const ComponentVM = new View({
propsData,
store,
}).$mount(container)
ComponentVM.$root.$on('close', () => {
ComponentVM.$el.remove()
ComponentVM.$destroy()
reject(new Error('Selection canceled'))
})
ComponentVM.$root.$on('select', (id) => {
ComponentVM.$el.remove()
ComponentVM.$destroy()
resolve(id)
})
})
}

export {
buildSelector,
}
9 changes: 8 additions & 1 deletion src/store/calendars.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,14 @@ const mutations = {
*/
addCalendar(state, calendar) {
// extend the calendar to the default model
state.calendars.push(Object.assign({}, calendarModel, calendar))
calendar = Object.assign({}, calendarModel, calendar)
// Only add the calendar if it is not already present
if (state.calendars.some(cal => {
return cal.id === calendar.id
})) {
return
}
state.calendars.push(calendar)
},

/**
Expand Down

0 comments on commit b2584f7

Please sign in to comment.