mirror of
https://github.com/axllent/mailpit.git
synced 2025-06-15 00:05:15 +02:00
Feature: Add pagination & limits to URL parameters (#303)
* Set search conditions to query parameters * Fixed by review * Update query parameters when new message notified
This commit is contained in:
committed by
Ralph Slooten
parent
31390e4b82
commit
e87b98b73b
@ -2,6 +2,7 @@
|
|||||||
import { mailbox } from '../stores/mailbox'
|
import { mailbox } from '../stores/mailbox'
|
||||||
import CommonMixins from '../mixins/CommonMixins'
|
import CommonMixins from '../mixins/CommonMixins'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import {pagination} from "../stores/pagination";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [
|
mixins: [
|
||||||
@ -99,6 +100,14 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toTagUrl: function (t) {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: String(0),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
return '/search?q=' + this.tagEncodeURI(t) + '&' + params.toString()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -143,7 +152,7 @@ export default {
|
|||||||
{{ message.Snippet }}
|
{{ message.Snippet }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="message.Tags.length">
|
<div v-if="message.Tags.length">
|
||||||
<RouterLink class="badge me-1" v-for="t in message.Tags" :to="'/search?q=' + tagEncodeURI(t)"
|
<RouterLink class="badge me-1" v-for="t in message.Tags" :to="toTagUrl(t)"
|
||||||
:style="mailbox.showTagColors ? { backgroundColor: colorHash(t) } : { backgroundColor: '#6c757d' }"
|
:style="mailbox.showTagColors ? { backgroundColor: colorHash(t) } : { backgroundColor: '#6c757d' }"
|
||||||
:title="'Filter messages tagged with ' + t">
|
:title="'Filter messages tagged with ' + t">
|
||||||
{{ t }}
|
{{ t }}
|
||||||
|
@ -32,6 +32,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
reloadInbox: function () {
|
reloadInbox: function () {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
|
this.$router.push('/')
|
||||||
this.loadMessages()
|
this.loadMessages()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -67,9 +67,22 @@ export default {
|
|||||||
if (query == '') {
|
if (query == '') {
|
||||||
this.$router.push('/')
|
this.$router.push('/')
|
||||||
} else {
|
} else {
|
||||||
this.$router.push('/search?q=' + encodeURIComponent(query))
|
const params = new URLSearchParams({
|
||||||
|
q: query,
|
||||||
|
start: pagination.start.toString(),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
this.$router.push('/search?' + params.toString())
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
toTagUrl(tag) {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start: String(0),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
return '/search?q=' + this.tagEncodeURI(tag) + '&' + params.toString()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -91,7 +104,7 @@ export default {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group mt-1 mb-5 pb-3">
|
<div class="list-group mt-1 mb-5 pb-3">
|
||||||
<RouterLink v-for="tag in mailbox.tags" :to="'/search?q=' + tagEncodeURI(tag)" @click="hideNav"
|
<RouterLink v-for="tag in mailbox.tags" :to="toTagUrl(tag)" @click="hideNav"
|
||||||
v-on:click="reloadFilter(tag)" v-on:click.ctrl="toggleTag($event, tag)"
|
v-on:click="reloadFilter(tag)" v-on:click.ctrl="toggleTag($event, tag)"
|
||||||
:style="mailbox.showTagColors ? { borderLeftColor: colorHash(tag), borderLeftWidth: '4px' } : ''"
|
:style="mailbox.showTagColors ? { borderLeftColor: colorHash(tag), borderLeftWidth: '4px' } : ''"
|
||||||
class="list-group-item list-group-item-action small px-2" :class="inSearch(tag) ? 'active' : ''">
|
class="list-group-item list-group-item-action small px-2" :class="inSearch(tag) ? 'active' : ''">
|
||||||
|
@ -60,6 +60,13 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
// update pagination offset
|
// update pagination offset
|
||||||
pagination.start++
|
pagination.start++
|
||||||
|
const path = self.$route.path
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
...self.$route.query,
|
||||||
|
start: pagination.start.toString(),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
self.$router.push(path + '?' + params.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import CommonMixins from '../mixins/CommonMixins'
|
import CommonMixins from '../mixins/CommonMixins'
|
||||||
import { mailbox } from '../stores/mailbox'
|
import { mailbox } from '../stores/mailbox'
|
||||||
import { pagination } from '../stores/pagination'
|
import {limitOptions, pagination} from '../stores/pagination'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
pagination,
|
pagination,
|
||||||
mailbox,
|
mailbox,
|
||||||
|
limitOptions,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -44,11 +45,13 @@ export default {
|
|||||||
changeLimit: function () {
|
changeLimit: function () {
|
||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
|
this.updateQueryParams()
|
||||||
},
|
},
|
||||||
|
|
||||||
viewNext: function () {
|
viewNext: function () {
|
||||||
pagination.start = parseInt(pagination.start, 10) + parseInt(pagination.limit, 10)
|
pagination.start = parseInt(pagination.start, 10) + parseInt(pagination.limit, 10)
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
|
this.updateQueryParams()
|
||||||
},
|
},
|
||||||
|
|
||||||
viewPrev: function () {
|
viewPrev: function () {
|
||||||
@ -58,6 +61,17 @@ export default {
|
|||||||
}
|
}
|
||||||
pagination.start = s
|
pagination.start = s
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
|
this.updateQueryParams()
|
||||||
|
},
|
||||||
|
|
||||||
|
updateQueryParams: function () {
|
||||||
|
const path = this.$route.path
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
...this.$route.query,
|
||||||
|
start: pagination.start.toString(),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
this.$router.push(path + '?' + params.toString())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,10 +80,7 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<select v-model="pagination.limit" @change="changeLimit" class="form-select form-select-sm d-inline w-auto me-2"
|
<select v-model="pagination.limit" @change="changeLimit" class="form-select form-select-sm d-inline w-auto me-2"
|
||||||
:disabled="total == 0">
|
:disabled="total == 0">
|
||||||
<option value="25">25</option>
|
<option v-for="option in limitOptions" :key="option" :value="option">{{option}}</option>
|
||||||
<option value="50">50</option>
|
|
||||||
<option value="100">100</option>
|
|
||||||
<option value="200">200</option>
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
|
@ -40,7 +40,12 @@ export default {
|
|||||||
pagination.start = 0
|
pagination.start = 0
|
||||||
this.$emit('loadMessages')
|
this.$emit('loadMessages')
|
||||||
}
|
}
|
||||||
this.$router.push('/search?q=' + encodeURIComponent(this.search))
|
const params = new URLSearchParams({
|
||||||
|
q: this.search,
|
||||||
|
start: pagination.start.toString(),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
this.$router.push('/search?' + params.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -2,6 +2,7 @@ import axios from 'axios'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import ColorHash from 'color-hash'
|
import ColorHash from 'color-hash'
|
||||||
import { Modal, Offcanvas } from 'bootstrap'
|
import { Modal, Offcanvas } from 'bootstrap'
|
||||||
|
import {limitOptions} from "../stores/pagination";
|
||||||
|
|
||||||
// BootstrapElement is used to return a fake Bootstrap element
|
// BootstrapElement is used to return a fake Bootstrap element
|
||||||
// if the ID returns nothing to prevent errors.
|
// if the ID returns nothing to prevent errors.
|
||||||
@ -66,14 +67,28 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
const q = urlParams.get('q').trim()
|
const q = urlParams.get('q')?.trim()
|
||||||
if (q == '') {
|
if (!q) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return q
|
return q
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPaginationParams: function () {
|
||||||
|
if (!window.location.search) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
const start = parseInt(urlParams.get('start')?.trim(), 10)
|
||||||
|
const limit = parseInt(urlParams.get('limit')?.trim(), 10)
|
||||||
|
return {
|
||||||
|
start: Number.isInteger(start) && start >= 0 ? start : null,
|
||||||
|
limit: limitOptions.includes(limit) ? limit : null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// generic modal get/set function
|
// generic modal get/set function
|
||||||
modal: function (id) {
|
modal: function (id) {
|
||||||
let e = document.getElementById(id)
|
let e = document.getElementById(id)
|
||||||
|
@ -6,3 +6,5 @@ export const pagination = reactive({
|
|||||||
total: 0, // total results of current view / filter
|
total: 0, // total results of current view / filter
|
||||||
count: 0, // number of messages currently displayed
|
count: 0, // number of messages currently displayed
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const limitOptions = [25, 50, 100, 200]
|
||||||
|
@ -9,6 +9,7 @@ import NavTags from '../components/NavTags.vue'
|
|||||||
import Pagination from '../components/Pagination.vue'
|
import Pagination from '../components/Pagination.vue'
|
||||||
import SearchForm from '../components/SearchForm.vue'
|
import SearchForm from '../components/SearchForm.vue'
|
||||||
import { mailbox } from '../stores/mailbox'
|
import { mailbox } from '../stores/mailbox'
|
||||||
|
import {pagination} from "../stores/pagination";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [CommonMixins, MessagesMixins],
|
mixins: [CommonMixins, MessagesMixins],
|
||||||
@ -30,6 +31,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
const paginationParams = this.getPaginationParams()
|
||||||
|
if (paginationParams?.start) {
|
||||||
|
pagination.start = paginationParams.start
|
||||||
|
}
|
||||||
|
if (paginationParams?.limit) {
|
||||||
|
pagination.limit = paginationParams.limit
|
||||||
|
}
|
||||||
|
|
||||||
mailbox.searching = false
|
mailbox.searching = false
|
||||||
this.apiURI = this.resolve(`/api/v1/messages`)
|
this.apiURI = this.resolve(`/api/v1/messages`)
|
||||||
this.loadMessages()
|
this.loadMessages()
|
||||||
|
@ -184,9 +184,18 @@ export default {
|
|||||||
mailbox.lastMessage = this.$route.params.id
|
mailbox.lastMessage = this.$route.params.id
|
||||||
|
|
||||||
if (mailbox.searching) {
|
if (mailbox.searching) {
|
||||||
this.$router.push('/search?q=' + encodeURIComponent(mailbox.searching))
|
const params = new URLSearchParams({
|
||||||
|
q: mailbox.searching,
|
||||||
|
start: pagination.start.toString(),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
this.$router.push('/search?' + params.toString())
|
||||||
} else {
|
} else {
|
||||||
this.$router.push('/')
|
const params = new URLSearchParams({
|
||||||
|
start: pagination.start.toString(),
|
||||||
|
limit: pagination.limit.toString(),
|
||||||
|
})
|
||||||
|
this.$router.push('/?' + params.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -33,17 +33,25 @@ export default {
|
|||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
$route(to, from) {
|
$route(to, from) {
|
||||||
this.doSearch(true)
|
this.doSearch()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
const paginationParams = this.getPaginationParams()
|
||||||
|
if (paginationParams?.start) {
|
||||||
|
pagination.start = paginationParams.start
|
||||||
|
}
|
||||||
|
if (paginationParams?.limit) {
|
||||||
|
pagination.limit = paginationParams.limit
|
||||||
|
}
|
||||||
|
|
||||||
mailbox.searching = this.getSearch()
|
mailbox.searching = this.getSearch()
|
||||||
this.doSearch(false)
|
this.doSearch()
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
doSearch: function (resetPagination) {
|
doSearch: function () {
|
||||||
let s = this.getSearch()
|
let s = this.getSearch()
|
||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
@ -54,10 +62,6 @@ export default {
|
|||||||
|
|
||||||
mailbox.searching = s
|
mailbox.searching = s
|
||||||
|
|
||||||
if (resetPagination) {
|
|
||||||
pagination.start = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
this.apiURI = this.resolve(`/api/v1/search`) + '?query=' + encodeURIComponent(s)
|
this.apiURI = this.resolve(`/api/v1/search`) + '?query=' + encodeURIComponent(s)
|
||||||
if (mailbox.timeZone != '' && (s.indexOf('after:') != -1 || s.indexOf('before:') != -1)) {
|
if (mailbox.timeZone != '' && (s.indexOf('after:') != -1 || s.indexOf('before:') != -1)) {
|
||||||
this.apiURI += '&tz=' + encodeURIComponent(mailbox.timeZone)
|
this.apiURI += '&tz=' + encodeURIComponent(mailbox.timeZone)
|
||||||
|
Reference in New Issue
Block a user