Newer
Older
<div>
<table class="default" id="messages">
<caption>{{ messageListName }}</caption>
<colgroup>
<col class="hidden-small-down">
<col>
<col class="hidden-small-down">
<col style="width: 20ex">
<col class="hidden-small-down">
</colgroup>
<thead>
<th class="hidden-small-down">
<input type="checkbox" @click="setCheckboxesByProxy"
:disabled="allMessages.length === 0"
:checked="selected.length === allMessages.length"
:indeterminate.prop="selected.length > 0 && selected.length < allMessages.length">
<th class="hidden-small-down">
<template v-if="type === 'inbox'">{{ $gettext('Absender') }}</template>
<template v-else>{{ $gettext('Empfänger') }}</template>
<th class="hidden-small-down">
</thead>
<transition-group name="fade" tag="tbody" aria-relevant="additions" aria-live="polite">
<tr v-for="message in allMessages" :key="`message-${message.id}`"
:id="`message_${message.id}`"
:class="isMessageRead(message) ? 'read' : 'unread'"
draggable="true"
@dragstart="dragStart(message, $event)" @dragend="dragStop()"
>
<td class="hidden-small-down">
<input type="checkbox" :checked="messageIsSelected(message)" :value="message.id" @click="selectedMessage(message)">
</td>
<td class="title">
<a :href="getMessageURL(message)" data-dialog>
{{ message.attributes.subject }}
<span v-if="message.relationships.attachments.data.length > 0">
<studip-icon shape="staple" role="info" :size="20" :title="$gettext('Mit Anhang')"/>
</span>
<studip-icon shape="outbox" role="info" :size="20" :title="$gettext('Beantwortet')"/>
</span>
</div>
</a>
</td>
<td v-if="type === 'inbox'" class="hidden-small-down">
<a v-if="message.relationships.sender" :href="getProfileURL(userById(message.relationships.sender.data.id))">
{{ userById(message.relationships.sender.data.id).attributes['formatted-name'] }}
</a>
<template v-else>{{ $gettext('Systemnachricht') }}</template>
</td>
<td v-else class="hidden-small-down">
<a v-for="recipient in message.relationships.recipients.data" :href="getProfileURL(userById(recipient.id))">
{{ userById(recipient.id).attributes['formatted-name'] }}
</a>
</td>
<td>
{{ showDate(message.attributes.mkdate) }}
</td>
<td class="tag-container hidden-small-down">
<a v-for="tag in message.attributes.tags" :key="`message-${message.attributes.id}-${tag}`"
class="message-tag"
:href="`?tag=${tag}`"
:title="$gettext('Alle Nachrichten zu diesem Schlagwort')">
{{ tag }}
</a>
</td>
</tr>
</transition-group>
</table>
<mounting-portal mount-to="#sidebar" append>
<sidebar-widget :title="$gettext('Schlagwörter')">
<template #content>
<ul class="widget-list widget-links sidebar-views">
<li :class="{active: !selectedTag}">
<a href="#" @click.prevent="selectedTag = null">
<li v-for="tag in tags" :key="`tag-${tag}`"
:class="{active: selectedTag === tag, dropzone: isDragging, draggedover: draggedOverTag === tag}"
@drop="drop(tag, $event)"
@dragover.prevent="draggedOver(tag)" @dragenter.prevent
@dragleave="draggedOver(null)"
>
<a href="#" @click.prevent="selectedTag = tag">
{{ tag }}
</a>
</li>
</ul>
</template>
</sidebar-widget>
</mounting-portal>
</div>
import SidebarWidget from "./SidebarWidget.vue";
import {LOAD_TAGS} from "../store/MessagesStore";
import {mapActions, mapGetters} from "vuex";
props: {
type: {
type: String,
required: true,
validator (value) {
return ['inbox', 'outbox'].includes(value);
}
}
},
data () {
return {
pageOptions: {
offset: 0,
limit: 30,
...mapActions({
loadTags: LOAD_TAGS,
}),
// Unfortunately, this cannot be used since we cannot use the dyanmic prop
//
// ...mapActions({
// isLoading: `${this.type}/isLoading`,
// loadPage: `${this.type}/loadPage`,
// userById: `users/byId`,
// }),
isLoading() {
return this.$store.getters[`${this.type}/isLoading`];
},
loadPage({options}) {
return this.$store.dispatch(`${this.type}/loadPage`, {options});
},
userById(id) {
return this.$store.getters[`users/byId`]({ id });
},
showDate (date) {
const jsDate = new Date(date);
day: '2-digit',
month: '2-digit',
year: 'numeric',
};
const timeOptions = {
};
return jsDate.toLocaleDateString(String.locale, dateOptions) + ' ' + jsDate.toLocaleTimeString(String.locale, timeOptions);
},
setCheckboxesByProxy () {
this.selected = [];
} else {
}
},
messageIsSelected(message) {
return this.selected.includes(message.id);
},
selectedMessage(message) {
if (this.selected.includes(message.id)) {
this.selected = this.selected.filter(m => m !== message.id);
} else {
this.selected.push(message.id);
}
},
getMessageURL(message) {
return STUDIP.URLHelper.getURL(`dispatch.php/messages/read/${message.id}`);
},
getProfileURL(user) {
return STUDIP.URLHelper.getURL('dispatch.php/profile', {username: user.attributes.username});
},
isMessageRead(message) {
return message.attributes['is-read'] || message.relationships.sender?.data.id === STUDIP.USER_ID;
},
loadMessages() {
return;
}
let options = {
'page[offset]': this.pageOptions.offset,
'page[limit]': this.pageOptions.limit,
include: this.type === 'inbox' ? 'sender' : 'recipients',
}
const lastMeta = this.$store.getters[`${this.type}/lastMeta`];
if (lastMeta !== null) {
if (lastMeta.page.offset + lastMeta.page.limit >= lastMeta.page.total) {
return;
}
options['page[offset]'] = lastMeta.page.offset + lastMeta.page.limit;
options['page[limit]'] = lastMeta.page.limit;
}
},
observe(element) {
if (this.observer === null) {
this.observer = new IntersectionObserver(entries => {
if (entries[0].intersectionRatio < 1) {
return;
}
this.loadMessages();
}, {
rootMargin: `0px 0px ${screen.availHeight / 3}px 0px`,
threshold: 1.0
})
}
if (this.observed) {
this.observer.unobserve(this.observed);
}
this.observed = element;
this.observer.observe(this.observed);
event.dataTransfer.dropEffect = 'move';
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('messageId', message.id);
},
dragStop(message) {
this.isDragging = false;
},
drop(tag, event) {
const messageId = event.dataTransfer.getData('messageId');
console.log('dropped message on tag', messageId, tag);
draggedOver(tag) {
this.draggedOverTag = tag;
}
...mapGetters({
tags: 'tags',
}),
// Unfortunately, this cannot be used since we cannot use the dyanmic prop
//
// ...mapGetters({
// allMessages: `${this.type}/all`
// }),
allMessages() {
return this.$store.getters[`${this.type}/all`];
},
return this.type === 'inbox' ? this.$gettext('Eingang') : this.$gettext('Gesendet');
},
updated () {
this.observe(this.$el.querySelector('tbody tr:last-of-type'));
background-color: var(--red-20);
}
.dropzone.draggedover {
background-color: var(--red-80);