migrating some state to pinia stores.

This commit is contained in:
ArabCoders 2024-12-13 20:15:05 +03:00
parent 62d5d29bfc
commit f7b610434f
5 changed files with 175 additions and 95 deletions

View file

@ -223,20 +223,13 @@ import { defineProps, computed, ref, watch, defineEmits } from 'vue';
import moment from "moment"; import moment from "moment";
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
import LazyLoader from './LazyLoader' import LazyLoader from './LazyLoader'
import { makeDownload, formatBytes, ucFirst } from '../utils/index' import { makeDownload, formatBytes, ucFirst } from '~/utils/index'
import { useConfigStore } from '~/store/ConfigStore';
import { useStateStore } from '~/store/StateStore';
const emits = defineEmits(['deleteItem', 'addItem', 'playItem', 'archiveItem']); const emits = defineEmits(['deleteItem', 'addItem', 'playItem', 'archiveItem'])
const config = useConfigStore()
const props = defineProps({ const stateStore = useStateStore()
completed: {
type: Object,
required: true
},
config: {
type: Object,
required: true
},
})
const selectedElms = ref([]); const selectedElms = ref([]);
const masterSelectAll = ref(false); const masterSelectAll = ref(false);
@ -244,8 +237,8 @@ const showCompleted = useStorage('showCompleted', true)
const direction = useStorage('sortCompleted', 'desc') const direction = useStorage('sortCompleted', 'desc')
watch(masterSelectAll, (value) => { watch(masterSelectAll, (value) => {
for (const key in props.completed) { for (const key in stateStore.history) {
const element = props.completed[key]; const element = stateStore.history[key];
if (value) { if (value) {
selectedElms.value.push(element._id); selectedElms.value.push(element._id);
} else { } else {
@ -256,16 +249,17 @@ watch(masterSelectAll, (value) => {
const sortCompleted = computed(() => { const sortCompleted = computed(() => {
const thisDirection = direction.value; const thisDirection = direction.value;
return Object.values(props.completed).sort((a, b) => { return Object.values(stateStore.history).sort((a, b) => {
if (thisDirection === 'asc') { if ('asc' === thisDirection) {
return new Date(a.datetime) - new Date(b.datetime); return new Date(a.datetime) - new Date(b.datetime);
} }
return new Date(b.datetime) - new Date(a.datetime); return new Date(b.datetime) - new Date(a.datetime);
}) })
}) })
const hasSelected = computed(() => selectedElms.value.length > 0) const hasSelected = computed(() => selectedElms.value.length > 0)
const hasItems = computed(() => Object.keys(props.completed)?.length > 0) const hasItems = computed(() => stateStore.count('history') > 0)
const getTotal = computed(() => Object.keys(props.completed)?.length); const getTotal = computed(() => stateStore.count('history'));
const showMessage = (item) => { const showMessage = (item) => {
if (!item?.msg || item.msg === item?.error) { if (!item?.msg || item.msg === item?.error) {
@ -276,12 +270,12 @@ const showMessage = (item) => {
} }
const hasIncomplete = computed(() => { const hasIncomplete = computed(() => {
if (Object.keys(props.completed)?.length < 0) { if (Object.keys(stateStore.history)?.length < 0) {
return false; return false;
} }
for (const key in props.completed) { for (const key in stateStore.history) {
const element = props.completed[key]; const element = stateStore.history[key];
if (element.status !== 'finished') { if (element.status !== 'finished') {
return true; return true;
} }
@ -290,12 +284,12 @@ const hasIncomplete = computed(() => {
}) })
const hasCompleted = computed(() => { const hasCompleted = computed(() => {
if (Object.keys(props.completed)?.length < 0) { if (Object.keys(stateStore.history)?.length < 0) {
return false; return false;
} }
for (const key in props.completed) { for (const key in stateStore.history) {
const element = props.completed[key]; const element = stateStore.history[key];
if (element.status === 'finished') { if (element.status === 'finished') {
return true; return true;
} }
@ -311,33 +305,27 @@ const clearCompleted = () => {
const keys = {}; const keys = {};
for (const key in props.completed) { for (const key in stateStore.history) {
if (props.completed[key].status === 'finished') { if (stateStore.history[key].status === 'finished') {
keys[key] = props.completed[key]._id; keys[key] = stateStore.history[key]._id;
stateStore.remove('history', key);
} }
} }
emits('deleteItem', 'completed', keys);
} }
const clearIncomplete = () => { const clearIncomplete = () => {
const state = confirm('Are you sure you want to clear all incomplete downloads?'); if (false === confirm('Are you sure you want to clear all incomplete downloads?')) {
if (false === state) {
return; return;
} }
const keys = {}; for (const key in stateStore.history) {
if (stateStore.history[key].status !== 'finished') {
for (const key in props.completed) { stateStore.remove('history', key);
if (props.completed[key].status !== 'finished') {
keys[key] = props.completed[key]._id;
} }
} }
emits('deleteItem', 'completed', keys);
} }
const setIcon = (item) => { const setIcon = item => {
if (item.status === 'finished' && item.is_live) { if (item.status === 'finished' && item.is_live) {
return 'fa-solid fa-globe'; return 'fa-solid fa-globe';
} }
@ -362,8 +350,8 @@ const requeueIncomplete = () => {
return false; return false;
} }
for (const key in props.completed) { for (const key in stateStore.history) {
const item = props.completed[key]; const item = stateStore.history[key];
if (item.status !== 'finished') { if (item.status !== 'finished') {
emits('deleteItem', 'completed', key); emits('deleteItem', 'completed', key);
emits('addItem', { emits('addItem', {

View file

@ -35,7 +35,8 @@
</div> </div>
<div class="columns is-multiline"> <div class="columns is-multiline">
<LazyLoader :unrender="true" :min-height="265" class="column is-6" v-for="item in queue" :key="item._id"> <LazyLoader :unrender="true" :min-height="265" class="column is-6" v-for="item in stateStore.queue"
:key="item._id">
<div class="card"> <div class="card">
<header class="card-header has-tooltip" v-tooltip="item.title"> <header class="card-header has-tooltip" v-tooltip="item.title">
<div class="card-header-title has-text-centered is-text-overflow is-block"> <div class="card-header-title has-text-centered is-text-overflow is-block">
@ -126,27 +127,21 @@ import { defineProps, defineEmits, ref, watch, computed } from 'vue';
import moment from "moment"; import moment from "moment";
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
import LazyLoader from './LazyLoader' import LazyLoader from './LazyLoader'
import { useConfigStore } from '~/store/ConfigStore';
import { useStateStore } from '~/store/StateStore';
const emit = defineEmits(['deleteItem']); const emit = defineEmits(['deleteItem']);
const props = defineProps({ const config = useConfigStore();
queue: { const stateStore = useStateStore();
type: Object,
required: true
},
config: {
type: Object,
required: true
},
})
const selectedElms = ref([]); const selectedElms = ref([]);
const masterSelectAll = ref(false); const masterSelectAll = ref(false);
const showQueue = useStorage('showQueue', true) const showQueue = useStorage('showQueue', true)
watch(masterSelectAll, (value) => { watch(masterSelectAll, (value) => {
for (const key in props.queue) { for (const key in stateStore.queue) {
const element = props.queue[key]; const element = stateStore.queue[key];
if (value) { if (value) {
selectedElms.value.push(element._id); selectedElms.value.push(element._id);
} else { } else {
@ -156,10 +151,10 @@ watch(masterSelectAll, (value) => {
}) })
const hasSelected = computed(() => selectedElms.value.length > 0) const hasSelected = computed(() => selectedElms.value.length > 0)
const hasQueuedItems = computed(() => Object.keys(props.queue)?.length > 0) const hasQueuedItems = computed(() => stateStore.count('queue') > 0)
const getTotal = computed(() => Object.keys(props.queue)?.length); const getTotal = computed(() => stateStore.count('queue'));
const setIcon = (item) => { const setIcon = item => {
if (item.status === 'downloading' && item.is_live) { if (item.status === 'downloading' && item.is_live) {
return 'fa-solid fa-globe'; return 'fa-solid fa-globe';
} }
@ -231,6 +226,7 @@ const confirmDelete = (item) => {
} }
emit('deleteItem', 'queue', item._id); emit('deleteItem', 'queue', item._id);
stateStore.remove('queue', item._id);
return true; return true;
} }

View file

@ -1,27 +1,24 @@
<template> <template>
<div> <div>
<Queue :config="config" :queue="downloading" @deleteItem="deleteItem" /> <Queue />
<History :config="config" :completed="completed" @deleteItem="deleteItem" @addItem="addItem" <History />
@playItem="playItem" @archiveItem="archiveItem" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { io } from "socket.io-client"; import { io } from "socket.io-client"
const bus = useEventBus('item_added', 'show_form', 'task_edit') import { useConfigStore } from "~/store/ConfigStore"
import { useStateStore } from "~/store/StateStore"
const config = reactive({ const bus = useEventBus('item_added', 'show_form', 'task_edit')
isConnected: false, const config = useConfigStore()
app: {},
tasks: [],
})
const runtimeConfig = useRuntimeConfig() const runtimeConfig = useRuntimeConfig()
const socket = ref() const socket = ref()
const downloading = reactive({}) const downloading = reactive({})
const completed = reactive({})
const cli_output = ref([]) const cli_output = ref([])
const cli_isLoading = ref(false) const cli_isLoading = ref(false)
const stateStore = useStateStore()
useHead({ title: 'Index' }) useHead({ title: 'Index' })
@ -34,23 +31,20 @@ onMounted(() => {
socket.value.on('disconnect', () => config.isConnected = false); socket.value.on('disconnect', () => config.isConnected = false);
socket.value.on('initial_data', stream => { socket.value.on('initial_data', stream => {
const initialData = JSON.parse(stream); const initialData = JSON.parse(stream)
config.app = initialData['config'];
config.tasks = initialData['tasks'];
config.directories = initialData['directories'];
for (const id in initialData['queue']) { config.setAll({
downloading[id] = initialData['queue'][id]; app: initialData['config'],
} tasks: initialData['tasks'],
directories: initialData['directories'],
for (const id in initialData['done']) { })
completed[id] = initialData['done'][id]; stateStore.addAll('queue', initialData['queue'] ?? {})
} stateStore.addAll('history', initialData['done'] ?? {})
}) })
socket.value.on('added', stream => { socket.value.on('added', stream => {
const item = JSON.parse(stream); const item = JSON.parse(stream);
downloading[item._id] = item; stateStore.add('queue', item);
toast.success(`Item queued successfully: ${downloading[item._id]?.title}`); toast.success(`Item queued successfully: ${downloading[item._id]?.title}`);
}); });
@ -61,15 +55,17 @@ onMounted(() => {
socket.value.on('completed', stream => { socket.value.on('completed', stream => {
const item = JSON.parse(stream); const item = JSON.parse(stream);
if (item._id in downloading) { if (true === stateStore.has('queue', item._id)) {
delete downloading[item._id]; stateStore.move('queue', 'history', item._id);
return
} }
completed[item._id] = item; stateStore.add('history', item);
}); });
socket.value.on('canceled', stream => { socket.value.on('canceled', stream => {
const id = JSON.parse(stream); const id = JSON.parse(stream);
if (false === (id in downloading)) {
if (true !== stateStore.has('queue', id)) {
return return
} }
@ -79,31 +75,28 @@ onMounted(() => {
socket.value.on('cleared', stream => { socket.value.on('cleared', stream => {
const id = JSON.parse(stream); const id = JSON.parse(stream);
if (false === (id in completed)) { if (true !== stateStore.has('history', id)) {
return; return
} }
stateStore.remove('history', id);
delete completed[id];
}); });
socket.value.on("updated", stream => { socket.value.on("updated", stream => {
const data = JSON.parse(stream); const data = JSON.parse(stream);
let dl = downloading[data._id] ?? {}; let dl = stateStore.get('queue', data._id, {});
data.deleting = dl?.deleting; data.deleting = dl?.deleting;
downloading[data._id] = data; stateStore.update('queue', data._id, data);
}); });
socket.value.on("update", stream => { socket.value.on("update", stream => {
const data = JSON.parse(stream); const data = JSON.parse(stream);
if (false === (data._id in completed)) { if (true === stateStore.has('history', data._id)) {
stateStore.update('history', data._id, data);
return; return;
} }
completed[data._id] = data;
}); });
socket.value.on('cli_close', () => cli_isLoading.value = false); socket.value.on('cli_close', () => cli_isLoading.value = false);
socket.value.on('cli_output', s => cli_output.value.push(s)); socket.value.on('cli_output', s => cli_output.value.push(s));
}); });
</script> </script>

58
ui/store/ConfigStore.js Normal file
View file

@ -0,0 +1,58 @@
import { defineStore } from 'pinia';
const CONFIG_KEYS = {
isConnected: false,
tasks: [],
app: {
host: '',
prefix: '',
keep_archive: false,
output_template: '',
},
directories: [],
};
export const useConfigStore = defineStore('config', () => {
const state = reactive(CONFIG_KEYS);
const actions = {
add(key, value) {
if (key.includes('.')) {
const [parentKey, subKey] = key.split('.');
state[parentKey][subKey] = value;
return;
}
state[key] = value;
},
get(key, defaultValue = null) {
if (key.includes('.')) {
const [parentKey, subKey] = key.split('.');
return state[parentKey][subKey] || defaultValue;
}
return state[key] || defaultValue;
},
update(key, value) {
if (key.includes('.')) {
const [parentKey, subKey] = key.split('.');
state[parentKey][subKey] = value;
return;
}
state[key] = value;
},
getAll() {
return state;
},
setAll(data) {
Object.keys(data).forEach((key) => {
if (key.includes('.')) {
const [parentKey, subKey] = key.split('.');
state[parentKey][subKey] = data[key];
return;
}
state[key] = data[key];
});
},
}
return { ...toRefs(state), ...actions };
});

45
ui/store/StateStore.js Normal file
View file

@ -0,0 +1,45 @@
import { defineStore } from 'pinia';
export const useStateStore = defineStore('state', () => {
const state = reactive({
queue: {},
history: {}
});
const actions = {
add(type, key, value) {
state[type][key] = value;
},
update(type, key, value) {
state[type][key] = value;
},
remove(type, key) {
if (state[type][key]) {
delete state[type][key];
}
},
get(type, key, defaultValue = null) {
return state[type][key] || defaultValue;
},
has(type, key) {
return !!state[type][key];
},
clearAll(type) {
state[type] = {};
},
addAll(type, data) {
state[type] = data;
},
move(fromType, toType, key) {
if (state[fromType][key]) {
state[toType][key] = state[fromType][key];
delete state[fromType][key];
}
},
count(type) {
return Object.keys(state[type]).length;
}
}
return { ...toRefs(state), ...actions };
});