feat: show optimistic history queue entries
This commit is contained in:
4 files changed
+80
-5
No files matched your search
@@ -2,12 +2,14 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Plus, PlusCircle, Search } from 'lucide-vue-next'
|
import { Plus, PlusCircle, Search } from 'lucide-vue-next'
|
||||||
import { api } from '@/api/client'
|
import { api } from '@/api/client'
|
||||||
|
import { useBotStatus } from '@/composables/useBotStatus'
|
||||||
import { useToasts } from '@/composables/useToasts'
|
import { useToasts } from '@/composables/useToasts'
|
||||||
import type { BotStatus } from '@/types/status'
|
import type { BotStatus } from '@/types/status'
|
||||||
|
|
||||||
const props = defineProps<{ status: BotStatus | null }>()
|
const props = defineProps<{ status: BotStatus | null }>()
|
||||||
|
|
||||||
const { notify, run } = useToasts()
|
const { notify, run } = useToasts()
|
||||||
|
const { addPendingTrack, removePendingTrack } = useBotStatus()
|
||||||
const query = ref('')
|
const query = ref('')
|
||||||
const busy = ref(false)
|
const busy = ref(false)
|
||||||
|
|
||||||
@@ -21,7 +23,10 @@ async function submit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
busy.value = true
|
busy.value = true
|
||||||
|
const guildId = props.status.active_voice_channel.guild_id
|
||||||
|
const pendingId = addPendingTrack(guildId, trimmed)
|
||||||
const added = await run(() => api.play(trimmed), 'Dodano utwór do kolejki!', 'Błąd dodawania')
|
const added = await run(() => api.play(trimmed), 'Dodano utwór do kolejki!', 'Błąd dodawania')
|
||||||
|
removePendingTrack(pendingId)
|
||||||
if (added) query.value = ''
|
if (added) query.value = ''
|
||||||
busy.value = false
|
busy.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Clock, History, Plus, RotateCcw } from 'lucide-vue-next'
|
import { Clock, History, Plus, RotateCcw } from 'lucide-vue-next'
|
||||||
import { api } from '@/api/client'
|
import { api } from '@/api/client'
|
||||||
|
import { useBotStatus } from '@/composables/useBotStatus'
|
||||||
import { useToasts } from '@/composables/useToasts'
|
import { useToasts } from '@/composables/useToasts'
|
||||||
import { useUploads } from '@/composables/useUploads'
|
import { useUploads } from '@/composables/useUploads'
|
||||||
import { formatTime } from '@/composables/useTrackProgress'
|
import { formatTime } from '@/composables/useTrackProgress'
|
||||||
@@ -11,6 +12,7 @@ import type { BotStatus, HistoryTrack } from '@/types/status'
|
|||||||
const props = defineProps<{ status: BotStatus | null }>()
|
const props = defineProps<{ status: BotStatus | null }>()
|
||||||
|
|
||||||
const { notify, run } = useToasts()
|
const { notify, run } = useToasts()
|
||||||
|
const { addPendingTrack, removePendingTrack } = useBotStatus()
|
||||||
const { enqueue } = useUploads()
|
const { enqueue } = useUploads()
|
||||||
|
|
||||||
const history = computed(() => props.status?.history ?? [])
|
const history = computed(() => props.status?.history ?? [])
|
||||||
@@ -21,6 +23,8 @@ async function replay(entry: HistoryTrack) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pendingId = addPendingTrack(props.status.active_voice_channel.guild_id, entry.title)
|
||||||
|
try {
|
||||||
if (entry.is_local) {
|
if (entry.is_local) {
|
||||||
const path = entry.filepath ?? entry.url ?? entry.title
|
const path = entry.filepath ?? entry.url ?? entry.title
|
||||||
await enqueue(path.split(/[\\/]/).pop() ?? path)
|
await enqueue(path.split(/[\\/]/).pop() ?? path)
|
||||||
@@ -31,6 +35,9 @@ async function replay(entry: HistoryTrack) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
await run(() => api.play(entry.url as string), 'Dodano z historii do kolejki!', 'Błąd')
|
await run(() => api.play(entry.url as string), 'Dodano z historii do kolejki!', 'Błąd')
|
||||||
|
} finally {
|
||||||
|
removePendingTrack(pendingId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { Disc, ListMusic, RotateCcw } from 'lucide-vue-next'
|
import { Disc, ListMusic, LoaderCircle, RotateCcw } from 'lucide-vue-next'
|
||||||
import QueueItem from '@/components/QueueItem.vue'
|
import QueueItem from '@/components/QueueItem.vue'
|
||||||
import { api } from '@/api/client'
|
import { api } from '@/api/client'
|
||||||
import { useBotStatus } from '@/composables/useBotStatus'
|
import { useBotStatus } from '@/composables/useBotStatus'
|
||||||
@@ -11,11 +11,15 @@ import type { BotStatus } from '@/types/status'
|
|||||||
const props = defineProps<{ status: BotStatus | null }>()
|
const props = defineProps<{ status: BotStatus | null }>()
|
||||||
|
|
||||||
const { notify, run } = useToasts()
|
const { notify, run } = useToasts()
|
||||||
const { paused } = useBotStatus()
|
const { paused, pendingTracks } = useBotStatus()
|
||||||
|
|
||||||
const queue = computed(() => props.status?.queue ?? [])
|
const queue = computed(() => props.status?.queue ?? [])
|
||||||
const queueIndex = computed(() => props.status?.queue_index ?? -1)
|
const queueIndex = computed(() => props.status?.queue_index ?? -1)
|
||||||
const savedQueueAvailable = computed(() => props.status?.saved_queue_available ?? false)
|
const savedQueueAvailable = computed(() => props.status?.saved_queue_available ?? false)
|
||||||
|
const currentGuildId = computed(() => props.status?.active_voice_channel?.guild_id ?? null)
|
||||||
|
const pendingForGuild = computed(() =>
|
||||||
|
pendingTracks.value.filter((track) => track.guildId === currentGuildId.value),
|
||||||
|
)
|
||||||
|
|
||||||
const draggedId = ref<string | null>(null)
|
const draggedId = ref<string | null>(null)
|
||||||
const dropTargetId = ref<string | null>(null)
|
const dropTargetId = ref<string | null>(null)
|
||||||
@@ -89,7 +93,7 @@ const restoreQueue = () =>
|
|||||||
<h2 class="card-title">
|
<h2 class="card-title">
|
||||||
<ListMusic /> Kolejka odtwarzania
|
<ListMusic /> Kolejka odtwarzania
|
||||||
</h2>
|
</h2>
|
||||||
<span class="queue-count">{{ tracksLabel(queue.length) }}</span>
|
<span class="queue-count">{{ tracksLabel(queue.length + pendingForGuild.length) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="queue-list-container">
|
<div class="queue-list-container">
|
||||||
<ul class="queue-list">
|
<ul class="queue-list">
|
||||||
@@ -114,8 +118,25 @@ const restoreQueue = () =>
|
|||||||
@set-plays="setPlays"
|
@set-plays="setPlays"
|
||||||
@invalid-plays="warnInvalidPlays"
|
@invalid-plays="warnInvalidPlays"
|
||||||
/>
|
/>
|
||||||
|
<li
|
||||||
|
v-for="track in pendingForGuild"
|
||||||
|
:key="track.id"
|
||||||
|
class="queue-item queue-item-pending"
|
||||||
|
>
|
||||||
|
<div class="queue-index">
|
||||||
|
<LoaderCircle class="loading-icon" />
|
||||||
|
</div>
|
||||||
|
<div class="queue-track-info">
|
||||||
|
<div class="queue-track-title">
|
||||||
|
{{ track.title }}
|
||||||
|
</div>
|
||||||
|
<div class="queue-pending-label">
|
||||||
|
Ładowanie utworu…
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
<div
|
<div
|
||||||
v-if="!queue.length"
|
v-if="!queue.length && !pendingForGuild.length"
|
||||||
class="empty-queue-message"
|
class="empty-queue-message"
|
||||||
>
|
>
|
||||||
<Disc />
|
<Disc />
|
||||||
@@ -138,4 +159,25 @@ const restoreQueue = () =>
|
|||||||
.restore-queue-button {
|
.restore-queue-button {
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.queue-item-pending {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
color: var(--primary);
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-pending-label {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--primary-hover);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -9,6 +9,7 @@ const status = ref<BotStatus | null>(null)
|
|||||||
const connected = ref(false)
|
const connected = ref(false)
|
||||||
/** Wstrzymuje przyjmowanie aktualizacji, gdy użytkownik przeciąga utwory. */
|
/** Wstrzymuje przyjmowanie aktualizacji, gdy użytkownik przeciąga utwory. */
|
||||||
const paused = ref(false)
|
const paused = ref(false)
|
||||||
|
const pendingTracks = ref<{ id: string; guildId: string; title: string }[]>([])
|
||||||
|
|
||||||
let socket: WebSocket | null = null
|
let socket: WebSocket | null = null
|
||||||
let reconnectTimer: number | null = null
|
let reconnectTimer: number | null = null
|
||||||
@@ -88,7 +89,27 @@ export function useBotStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addPendingTrack(guildId: string, title: string) {
|
||||||
|
const id = `pending-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
||||||
|
pendingTracks.value.push({ id, guildId, title })
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePendingTrack(id: string) {
|
||||||
|
pendingTracks.value = pendingTracks.value.filter((track) => track.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
onUnmounted(disconnect)
|
onUnmounted(disconnect)
|
||||||
|
|
||||||
return { status, connected, paused, connect, disconnect, selectGuild }
|
return {
|
||||||
|
status,
|
||||||
|
connected,
|
||||||
|
paused,
|
||||||
|
pendingTracks,
|
||||||
|
connect,
|
||||||
|
disconnect,
|
||||||
|
selectGuild,
|
||||||
|
addPendingTrack,
|
||||||
|
removePendingTrack,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user