Add option to auto refresh collections (#4777)

* add auto refresh

* add refresh_interval to DB

* remove refresh_interval template

* set refresh interval default to null

* Fix typo in filename

* Rename sidebar-auto-refresh to refresh-sidebar-detail

* Rename import

* Add badge on active refresh, change options

* Fix refresh not working on refresh

Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
Nitwel
2021-04-09 23:24:40 +02:00
committed by GitHub
parent 52ee17dc33
commit 44091e6f41
10 changed files with 156 additions and 8 deletions

View File

@@ -0,0 +1,13 @@
import { Knex } from 'knex';
export async function up(knex: Knex) {
await knex.schema.alterTable('directus_presets', (table) => {
table.integer('refresh_interval');
});
}
export async function down(knex: Knex) {
await knex.schema.alterTable('directus_presets', (table) => {
table.dropColumn('refresh_interval');
});
}

View File

@@ -15,7 +15,7 @@ import { defineComponent } from '@vue/composition-api';
export default defineComponent({
props: {
value: {
type: [String, Number],
type: [Boolean, String, Number],
default: null,
},
dot: {
@@ -102,12 +102,21 @@ body {
bottom: calc(var(--v-badge-size) / -2 + var(--v-badge-offset-y));
}
&.dot * {
display: none;
&.bordered {
filter: drop-shadow(1.5px 1.5px 0 var(--v-badge-border-color))
drop-shadow(1.5px -1.5px 0 var(--v-badge-border-color)) drop-shadow(-1.5px 1.5px 0 var(--v-badge-border-color))
drop-shadow(-1.5px -1.5px 0 var(--v-badge-border-color));
}
&.bordered {
border: 2px solid var(--v-badge-border-color);
&.dot {
width: var(--v-badge-size);
min-width: 0;
height: var(--v-badge-size);
border: 0;
* {
display: none;
}
}
}
}

View File

@@ -131,6 +131,20 @@ export function usePreset(collection: Ref<string>, bookmark: Ref<number | null>
},
});
const refreshInterval = computed<number | null>({
get() {
return localPreset.value.refresh_interval || null;
},
set(val) {
localPreset.value = {
...localPreset.value,
refresh_interval: val,
};
handleChanges();
},
});
const searchQuery = computed<string | null>({
get() {
return localPreset.value.search || null;
@@ -167,6 +181,7 @@ export function usePreset(collection: Ref<string>, bookmark: Ref<number | null>
layoutQuery,
filters,
searchQuery,
refreshInterval,
savePreset,
saveCurrentAsBookmark,
bookmarkTitle,
@@ -195,6 +210,7 @@ export function usePreset(collection: Ref<string>, bookmark: Ref<number | null>
layout: 'tabular',
filters: null,
search: null,
refresh_interval: null,
};
await savePreset();

View File

@@ -149,6 +149,11 @@ uuid: UUID
hash: Hash
not_available_for_type: Not Available for this Type
create_translations: Create Translations
auto_refresh: Auto Refresh
refresh_interval: Refresh Interval
no_refresh: Do not refresh
refresh_interval_seconds: Refresh Instantly | Every Second | Every {seconds} Seconds
refresh_interval_minutes: Every Minute | Every {minutes} Minutes
auto_generate: Auto-Generate
this_will_auto_setup_fields_relations: This will automatically setup all required fields and relations.
click_here: Click here

View File

@@ -222,6 +222,7 @@
<layout-sidebar-detail @input="layout = $event" :value="layout" />
<portal-target name="sidebar" />
<export-sidebar-detail :layout-query="layoutQuery" :search-query="searchQuery" :collection="currentCollection" />
<refresh-sidebar-detail @refresh="refresh" v-model="refreshInterval" />
</template>
<v-dialog v-if="deleteError" active>
@@ -249,6 +250,7 @@ import useCollection from '@/composables/use-collection';
import usePreset from '@/composables/use-preset';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail';
import ExportSidebarDetail from '@/views/private/components/export-sidebar-detail';
import RefreshSidebarDetail from '@/views/private/components/refresh-sidebar-detail';
import SearchInput from '@/views/private/components/search-input';
import BookmarkAdd from '@/views/private/components/bookmark-add';
import BookmarkEdit from '@/views/private/components/bookmark-edit';
@@ -274,6 +276,7 @@ export default defineComponent({
BookmarkAdd,
BookmarkEdit,
DrawerBatch,
RefreshSidebarDetail,
},
props: {
collection: {
@@ -311,6 +314,7 @@ export default defineComponent({
resetPreset,
bookmarkSaved,
bookmarkIsMine,
refreshInterval,
busy: bookmarkSaving,
clearLocalSave,
} = usePreset(collection, bookmarkID);
@@ -344,7 +348,6 @@ export default defineComponent({
addNewLink,
batchDelete,
batchEditActive,
confirmDelete,
currentCollection,
deleting,
@@ -381,6 +384,7 @@ export default defineComponent({
bookmarkSaving,
clearLocalSave,
refresh,
refreshInterval,
};
function refresh() {

View File

@@ -14,6 +14,7 @@ const defaultPreset: Omit<Preset, 'collection'> = {
layout: null,
layout_query: null,
layout_options: null,
refresh_interval: null,
};
const systemDefaults: Record<string, Partial<Preset>> = {

View File

@@ -35,4 +35,5 @@ export type Preset = {
layout: string | null;
layout_query: { [layout: string]: any } | null;
layout_options: { [layout: string]: any } | null;
refresh_interval: number | null;
};

View File

@@ -0,0 +1,4 @@
import RefreshSidebarDetail from './refresh-sidebar-detail.vue';
export { RefreshSidebarDetail };
export default RefreshSidebarDetail;

View File

@@ -0,0 +1,95 @@
<template>
<sidebar-detail :icon="active ? 'sync' : 'sync_disabled'" :title="$t('auto_refresh')" :badge="active">
<div class="fields">
<div class="field full">
<p class="type-label">{{ $t('refresh_interval') }}</p>
<v-select :items="items" v-model="interval" />
</div>
</div>
</sidebar-detail>
</template>
<script lang="ts">
import i18n from '@/lang';
import { computed, defineComponent, ref, watch } from '@vue/composition-api';
export default defineComponent({
props: {
value: {
type: Number,
default: null,
},
},
setup(props, { emit }) {
const interval = computed<number | null>({
get() {
return props.value;
},
set(newVal) {
emit('input', newVal);
},
});
const activeInterval = ref<NodeJS.Timeout | null>(null);
watch(
interval,
(newInterval) => {
if (activeInterval.value !== null) {
clearInterval(activeInterval.value);
}
if (newInterval !== null && newInterval > 0) {
activeInterval.value = setInterval(() => {
emit('refresh');
}, newInterval * 1000);
}
},
{ immediate: true }
);
const items = computed(() => {
const intervals = [null, 10, 30, 60, 300];
return intervals.map((seconds) => {
if (seconds === null)
return {
text: i18n.t('no_refresh'),
value: null,
};
return seconds >= 60 && seconds % 60 === 0
? {
text: i18n.tc('refresh_interval_minutes', seconds / 60, { minutes: seconds / 60 }),
value: seconds,
}
: {
text: i18n.tc('refresh_interval_seconds', seconds, { seconds }),
value: seconds,
};
});
});
const active = computed(() => interval.value !== null);
return { active, interval, items };
},
});
</script>
<style lang="scss" scoped>
@import '@/styles/mixins/form-grid';
.fields {
--form-vertical-gap: 24px;
@include form-grid;
.type-label {
font-size: 1rem;
}
}
.v-checkbox {
margin-top: 8px;
}
</style>

View File

@@ -2,7 +2,7 @@
<div class="sidebar-detail" :class="{ open: sidebarOpen }">
<button class="toggle" @click="toggle" :class="{ open: active }">
<div class="icon">
<v-badge bordered :value="badge" :disabled="!badge">
<v-badge :dot="badge === true" bordered :value="badge" :disabled="!badge">
<v-icon :name="icon" outline />
</v-badge>
</div>
@@ -42,7 +42,7 @@ export default defineComponent({
required: true,
},
badge: {
type: [String, Number],
type: [Boolean, String, Number],
default: null,
},
close: {