Skip to content

Commit

Permalink
Convert mail to task
Browse files Browse the repository at this point in the history
Signed-off-by: hamza221 <hamzamahjoubi221@gmail.com>
  • Loading branch information
hamza221 committed Apr 13, 2023
1 parent e56dab3 commit f7d0489
Show file tree
Hide file tree
Showing 7 changed files with 1,090 additions and 7 deletions.
16 changes: 16 additions & 0 deletions src/components/Envelope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,14 @@
</template>
{{ t('mail', 'Create event') }}
</ActionButton>
<ActionButton :close-after-click="true"
@click.prevent="showTaskModal = true">
<template #icon>
<TaskIcon
:size="20" />
</template>
{{ t('mail', 'Create task') }}
</ActionButton>
<ActionLink
:close-after-click="true"
:href="exportMessageLink">
Expand Down Expand Up @@ -243,6 +251,9 @@
<EventModal v-if="showEventModal"
:envelope="data"
@close="showEventModal = false" />
<TaskModal v-if="showTaskModal"
:envelope="data"
@close="showTaskModal = false" />
<TagModal
v-if="showTagModal"
:account="account"
Expand All @@ -260,6 +271,7 @@ import CheckIcon from 'vue-material-design-icons/Check'
import ChevronLeft from 'vue-material-design-icons/ChevronLeft'
import DeleteIcon from 'vue-material-design-icons/Delete'
import ArchiveIcon from 'vue-material-design-icons/PackageDown'
import TaskIcon from 'vue-material-design-icons/CheckboxMarkedCirclePlusOutline'
import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal'
import importantSvg from '../../img/important.svg'
import { DraggableEnvelopeDirective } from '../directives/drag-and-drop/draggable-envelope'
Expand All @@ -285,6 +297,7 @@ import PlusIcon from 'vue-material-design-icons/Plus'
import TagIcon from 'vue-material-design-icons/Tag'
import TagModal from './TagModal'
import EventModal from './EventModal'
import TaskModal from './TaskModal'
import EnvelopePrimaryActions from './EnvelopePrimaryActions'
import { hiddenTags } from './tags.js'
import { generateUrl } from '@nextcloud/router'
Expand All @@ -302,9 +315,11 @@ export default {
ChevronLeft,
DeleteIcon,
ArchiveIcon,
TaskIcon,
DotsHorizontalIcon,
EnvelopePrimaryActions,
EventModal,
TaskModal,
ListItem,
ImportantIcon,
JunkIcon,
Expand Down Expand Up @@ -365,6 +380,7 @@ export default {
importantSvg,
showMoveModal: false,
showEventModal: false,
showTaskModal: false,
showTagModal: false,
moreActionsOpen: false,
}
Expand Down
12 changes: 6 additions & 6 deletions src/components/NewMessageModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,6 @@ export default {
warning: undefined,
}
},
created() {
const draftId = this.composerData?.draftId
if (draftId) {
this.draftsPromise = Promise.resolve(draftId)
}
},
computed: {
modalTitle() {
if (this.composerMessage.type === 'outbox') {
Expand All @@ -145,6 +139,12 @@ export default {
return this.composerMessage?.options?.forwardedMessages ?? []
},
},
created() {
const draftId = this.composerData?.draftId
if (draftId) {
this.draftsPromise = Promise.resolve(draftId)
}
},
methods: {
handleShow(element) {
this.toolbarElements = [element]
Expand Down
207 changes: 207 additions & 0 deletions src/components/TaskModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<template>
<Modal @close="onClose">
<div class="modal-content">
<h2>{{ t('mail', 'Create event') }}</h2>
<div class="taskTitle">
<input v-model="taskTitle" type="text">
</div>
<div class="all-day">
<DatetimePicker v-model="startDate"
:format="dateFormat"
:clearable="false"
:minute-step="5"
:show-second="false"
:type="datePickerType"
:show-timezone-select="true"
:timezone-id="startTimezoneId" />
<DatetimePicker v-model="endDate"
:format="dateFormat"
:clearable="false"
:minute-step="5"
:show-second="false"
:type="datePickerType"
:show-timezone-select="true"
:timezone-id="endTimezoneId" />
</div>
<label for="note">Tell us your story:</label>
<textarea id="note" v-model="note" />
<div class="all-day">
<input
id="allDay"
v-model="isAllDay"
type="checkbox"
class="checkbox">
<label
for="allDay">
{{ t('mail', 'All day') }}
</label>
</div>
<Multiselect
v-model="selectedCalendar"
label="displayname"
track-by="url"
:allow-empty="false"
:options="calendars">
<template #option="{option}">
<CalendarPickerOption
v-bind="option" />
</template>
<template #singleLabel="{option}">
<CalendarPickerOption
:display-icon="option.displayIcon"
v-bind="option" />
</template>
</Multiselect>

<br>
<button class="primary" @click="onSave">
{{ t('mail', 'Create') }}
</button>
</div>
</Modal>
</template>

<script>
import { NcDatetimePicker as DatetimePicker, NcModal as Modal, NcMultiselect as Multiselect } from '@nextcloud/vue'
import jstz from 'jstz'
import logger from '../logger'
import CalendarPickerOption from './CalendarPickerOption'
import { showError, showSuccess } from '@nextcloud/dialogs'
import moment from 'moment'
export default {
name: 'TaskModal',
components: {
CalendarPickerOption,
DatetimePicker,
Modal,
Multiselect,
},
props: {
envelope: {
type: Object,
required: true,
},
},
data() {
// Try to determine the current timezone, and fall back to UTC otherwise
const defaultTimezone = jstz.determine()
const defaultTimezoneId = defaultTimezone ? defaultTimezone.name() : 'UTC'
return {
taskTitle: this.envelope.subject,
startDate: new Date(),
endDate: new Date(Date.now() + 60 * 60 * 1000),
isAllDay: true,
startTimezoneId: defaultTimezoneId,
endTimezoneId: defaultTimezoneId,
saving: false,
selectedCalendar: undefined,
note: this.envelope.previewText,
}
},
computed: {
dateFormat() {
return this.isAllDay ? moment.localeData().longDateFormat('L') : moment.localeData().longDateFormat('LT')
},
datePickerType() {
return this.isAllDay ? 'date' : 'datetime'
},
tags() {
return this.$store.getters.getAllTags
},
calendars() {
return this.$store.getters.getTaskCalendarsForCurrentUser
},
},
created() {
logger.debug('creating task from envelope', {
envelope: this.envelope,
})
},
async mounted() {
if (this.calendars.length) {
this.selectedCalendar = this.calendars[0]
}
},
methods: {
onClose() {
this.$emit('close')
},
async onSave() {
this.saving = true
const taskData = {
summary: this.taskTitle,
calendar: this.selectedCalendar,
start: moment(this.startDate).format().toString(),
due: moment(this.endDate).set().format().toString(),
allDay: this.isAllDay,
note: this.note,
}
try {
logger.debug('create task', taskData)
this.$store.commit('createTask', taskData)
showSuccess(t('mail', 'Task created'))
this.onClose()
} catch (error) {
showError(t('mail', 'Could not create task'))
logger.error('Creating event from message failed', { error })
} finally {
this.saving = false
}
},
},
}
</script>

<style lang="scss" scoped>
:deep(.modal-wrapper .modal-container) {
width: calc(100vw - 120px) !important;
height: calc(100vh - 120px) !important;
max-width: 490px !important;
max-height: 500px !important;
}
:deep(.calendar-picker-option__color-indicator){
margin-left: 10px !important;
}
.modal-content {
padding: 30px 30px 20px !important;
}
input , textarea {
width: 100%;
}
:deep(input[type='text'].multiselect__input) {
padding: 0 !important;
}
:deep(.multiselect__single) {
margin-left: -18px;
width: 100px;
}
:deep(.multiselect__tags) {
border: none !important;
}
.all-day {
margin-left: -1px;
margin-top: 5px;
margin-bottom: 5px;
}
.taskTitle {
margin-bottom: 5px;
}
.primary {
height: 44px !important;
float: right;
}
:deep(.mx-datepicker) {
width: 213px;
}
</style>
94 changes: 94 additions & 0 deletions src/store/calendar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Maps a dav collection to our calendar object model
*
* @param {object} calendar The calendar object from the cdav library
* @param {object} currentUserPrincipal The principal model of the current user principal
* @return {object}
*/
export default function toCalendar(calendar, currentUserPrincipal) {
const owner = calendar.owner
let isSharedWithMe = false
if (!currentUserPrincipal) {
// If the user is not authenticated, the calendar
// will always be marked as shared with them
isSharedWithMe = true
} else {
isSharedWithMe = (owner !== currentUserPrincipal.url)
}
const displayname = calendar.displayname || getCalendarUriFromUrl(calendar.url)

const color = calendar.color

const shares = []
if (!!currentUserPrincipal && Array.isArray(calendar.shares)) {
for (const share of calendar.shares) {
if (share.href === currentUserPrincipal.principalScheme) {
continue
}

shares.push(mapDavShareeToSharee(share))
}
}

const order = +calendar.order || 0

return {
// get last part of url
id: calendar.url.split('/').slice(-2, -1)[0],
displayname,
color,
order,
enabled: calendar.enabled !== false,
owner,
readOnly: !calendar.isWriteable(),
tasks: {},
url: calendar.url,
dav: calendar,
shares,
supportsEvents: calendar.components.includes('VEVENT'),
supportsTasks: calendar.components.includes('VTODO'),
loadedCompleted: false,
isSharedWithMe,
canBeShared: calendar.isShareable(),
}
}

/**
* Gets the calendar uri from the url
*
* @param {string} url The url to get calendar uri from
* @return {string}
*/
function getCalendarUriFromUrl(url) {
if (url.endsWith('/')) {
url = url.substring(0, url.length - 1)
}

return url.substring(url.lastIndexOf('/') + 1)
}

/*
* Maps a dav collection to the sharee array
*
* @param {object} sharee The sharee object from the cdav library shares
* @return {object}
*/
export function mapDavShareeToSharee(sharee) {
const id = sharee.href.split('/').slice(-1)[0]
let name = sharee['common-name']
? sharee['common-name']
: sharee.href

if (sharee.href.startsWith('principal:principals/groups/') && name === sharee.href) {
name = sharee.href.slice(28)
}

return {
displayName: name,
id,
writeable: sharee.access[0].endsWith('read-write'),
isGroup: sharee.href.startsWith('principal:principals/groups/'),
isCircle: sharee.href.startsWith('principal:principals/circles/'),
uri: sharee.href,
}
}
Loading

0 comments on commit f7d0489

Please sign in to comment.