SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 4662b981 authored by Jean-Sébastien Caux's avatar Jean-Sébastien Caux
Browse files

Add thread (un)focusing to messages listing

parent f73b8a1b
No related branches found
No related tags found
No related merge requests found
...@@ -152,6 +152,44 @@ class StoredMessageFilterBackend(filters.BaseFilterBackend): ...@@ -152,6 +152,44 @@ class StoredMessageFilterBackend(filters.BaseFilterBackend):
queryset = StoredMessage.objects.all() queryset = StoredMessage.objects.all()
queryfilter = Q() queryfilter = Q()
view_option = request.query_params.get('view', None)
if view_option == 'by_thread':
queryset = queryset.filter(data__References__isnull=True)
thread_of_uuid = request.query_params.get('thread_of_uuid', None)
if thread_of_uuid:
# Identify email thread using data['References'] or data['Message-Id'].
# Since Django ORM does not support hyphenated lookups, use raw SQL.
# First find the message at the root of the thread. which is the message
# with Message-Id first in the list of the message with uuid `thread_of_uuid`
reference_message = get_object_or_404(StoredMessage, uuid=thread_of_uuid)
# First try the RFC 2822 References MIME header:
head_id = None
try:
head_id = reference_message.data['References'].split()[0]
except KeyError:
# Then try the RFC 2822 In-Reply-To
try:
head_id = reference_message.data['In-Reply-To'].split()[0]
except KeyError:
# This message is head of the thread as far as can be guessed
head_id = reference_message.data['Message-Id']
if head_id:
thread_query_raw = (
"SELECT apimail_storedmessage.id FROM apimail_storedmessage "
"WHERE UPPER((apimail_storedmessage.data ->> %s)::text) LIKE UPPER(%s) "
"OR UPPER((apimail_storedmessage.data ->> %s)::text) LIKE UPPER(%s) "
"ORDER BY apimail_storedmessage.datetimestamp DESC;")
sm_ids = [sm.id for sm in StoredMessage.objects.raw(
thread_query_raw,
['Message-Id', '%%%s%%' % head_id, 'References', '%%%s%%' % head_id])]
queryset = queryset.filter(pk__in=sm_ids)
else:
queryset = queryset.filter(uuid=thread_of_uuid)
flow = request.query_params.get('flow', None) flow = request.query_params.get('flow', None)
if flow == 'in': if flow == 'in':
# Restrict to incoming emails # Restrict to incoming emails
......
...@@ -132,7 +132,25 @@ ...@@ -132,7 +132,25 @@
<h2 class="text-center mb-2">Messages&nbsp;for&emsp;<strong>{{ accountSelected.email }}</strong></h2> <h2 class="text-center mb-2">Messages&nbsp;for&emsp;<strong>{{ accountSelected.email }}</strong></h2>
<hr class="my-2"> <hr class="my-2">
<b-row class="mb-0"> <b-row class="mb-0">
<b-col class="col-lg-6"> <b-col class="col-lg-4">
<b-form-group
label="View by "
label-cols-sm="6"
label-align-sm="right"
label-size="sm"
>
<b-form-radio-group
v-model="viewFormat"
buttons
button-variant="outline-primary"
size="sm"
:options="viewFormatOptions"
class="float-center"
>
</b-form-radio-group>
</b-form-group>
</b-col>
<b-col md="auto">
<small class="p-2">Last loaded: {{ lastLoaded }}</small> <small class="p-2">Last loaded: {{ lastLoaded }}</small>
<b-badge <b-badge
class="p-2" class="p-2"
...@@ -143,9 +161,9 @@ ...@@ -143,9 +161,9 @@
Refresh now Refresh now
</b-badge> </b-badge>
</b-col> </b-col>
<b-col class="col-lg-6"> <b-col>
<b-form-group <b-form-group
label="Auto refresh every: " label="Refresh interval: "
label-cols-sm="6" label-cols-sm="6"
label-align-sm="right" label-align-sm="right"
label-size="sm" label-size="sm"
...@@ -158,7 +176,7 @@ ...@@ -158,7 +176,7 @@
:options="refreshMinutesOptions" :options="refreshMinutesOptions"
class="float-center" class="float-center"
> >
&nbsp;minutes &nbsp;mins
</b-form-radio-group> </b-form-radio-group>
</b-form-group> </b-form-group>
</b-col> </b-col>
...@@ -290,6 +308,12 @@ ...@@ -290,6 +308,12 @@
</b-col> </b-col>
</b-row> </b-row>
</b-card> </b-card>
<div v-if="threadOf" class="m-2">
<b-button size="sm" variant="info"><strong>Focusing on thread {{ threadOf }}</strong></b-button>
<b-button size="sm" variant="warning" @click="threadOf = null">
Unfocus this thread
</b-button>
</div>
<b-table <b-table
id="my-table" id="my-table"
class="mb-0" class="mb-0"
...@@ -379,7 +403,18 @@ ...@@ -379,7 +403,18 @@
<path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/> <path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg> </svg>
</b-button> </b-button>
<br>
</template> </template>
<div v-if="threadOf != message.uuid">
<b-button class="m-2" variant="primary" @click="threadOf = message.uuid">
Focus on this thread
</b-button>
</div>
<div v-else>
<b-button class="m-2" variant="warning" @click="threadOf = null">
Unfocus this thread
</b-button>
</div>
<message-content <message-content
:message="message" :message="message"
:tags="tags" :tags="tags"
...@@ -438,6 +473,12 @@ export default { ...@@ -438,6 +473,12 @@ export default {
], ],
filter: null, filter: null,
filterOn: [], filterOn: [],
viewFormat: 'by_message',
viewFormatOptions: [
{ text: 'message', value: 'by_message' },
{ text: 'thread', value: 'by_thread' },
],
threadOf: null,
timePeriod: 'any', timePeriod: 'any',
timePeriodOptions: [ timePeriodOptions: [
{ text: 'week', value: 'week' }, { text: 'week', value: 'week' },
...@@ -524,6 +565,13 @@ export default { ...@@ -524,6 +565,13 @@ export default {
var params = '?account=' + this.accountSelected.email var params = '?account=' + this.accountSelected.email
// Our API uses limit/offset pagination // Our API uses limit/offset pagination
params += '&limit=' + ctx.perPage + '&offset=' + ctx.perPage * (ctx.currentPage - 1) params += '&limit=' + ctx.perPage + '&offset=' + ctx.perPage * (ctx.currentPage - 1)
// By message or thread view:
if (this.viewFormat == 'by_thread') {
params += '&view=by_thread'
}
if (this.threadOf) {
params += '&thread_of_uuid=' + this.threadOf
}
// Add flow direction // Add flow direction
if (this.flowDirection) { if (this.flowDirection) {
params += '&flow=' + this.flowDirection params += '&flow=' + this.flowDirection
...@@ -605,6 +653,12 @@ export default { ...@@ -605,6 +653,12 @@ export default {
accountSelected: function () { accountSelected: function () {
this.$root.$emit('bv::refresh::table', 'my-table') this.$root.$emit('bv::refresh::table', 'my-table')
}, },
viewFormat: function () {
this.$root.$emit('bv::refresh::table', 'my-table')
},
threadOf: function () {
this.$root.$emit('bv::refresh::table', 'my-table')
},
timePeriod: function () { timePeriod: function () {
this.$root.$emit('bv::refresh::table', 'my-table') this.$root.$emit('bv::refresh::table', 'my-table')
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment