Skip to content

Commit 866de4d

Browse files
committed
break up UploadQueue into separate components
1 parent efcca4f commit 866de4d

File tree

6 files changed

+210
-187
lines changed

6 files changed

+210
-187
lines changed

src/components/UploadQueue.tsx

Lines changed: 0 additions & 186 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Component, createMemo } from 'solid-js'
2+
3+
import { COMMA_CONNECT_PRIORITY } from '~/api/athena'
4+
import Icon from '~/components/material/Icon'
5+
import LinearProgress from '~/components/material/LinearProgress'
6+
import { UploadItem } from '~/types'
7+
8+
const QueueItem: Component<{ item: UploadItem }> = (props) => {
9+
const progress = createMemo(() => {
10+
if (props.item.status === 'waiting_for_network') return 'Waiting for network'
11+
if (props.item.status === 'queued') return 'Queued'
12+
if (props.item.progress === 100) return 'Finishing'
13+
return `${props.item.progress}%`
14+
})
15+
16+
const progressColor = createMemo(() => {
17+
switch (props.item.status) {
18+
case 'uploading':
19+
return 'primary'
20+
case 'queued':
21+
return 'secondary'
22+
case 'completed':
23+
return 'tertiary'
24+
case 'waiting_for_network':
25+
return 'secondary'
26+
default:
27+
return 'primary'
28+
}
29+
})
30+
31+
return (
32+
<div class="flex flex-col">
33+
<div class="flex items-center justify-between flex-wrap mb-1 gap-x-4 min-w-0">
34+
<div class="flex items-center min-w-0 flex-1">
35+
<Icon
36+
class="text-on-surface-variant flex-shrink-0 mr-2"
37+
name={props.item.priority === COMMA_CONNECT_PRIORITY ? 'face' : 'local_fire_department'}
38+
/>
39+
<div class="flex min-w-0 gap-1">
40+
<span class="text-body-sm font-mono truncate text-on-surface">
41+
{[props.item.route, props.item.segment, props.item.filename].join(' ')}
42+
</span>
43+
</div>
44+
</div>
45+
<div class="flex items-center gap-2 flex-shrink-0 justify-end">
46+
<span class="text-body-sm font-mono whitespace-nowrap">{progress()}</span>
47+
</div>
48+
</div>
49+
<div class="h-1.5 w-full overflow-hidden rounded-full bg-surface-container-highest">
50+
<LinearProgress progress={props.item.progress / 100} color={progressColor()} />
51+
</div>
52+
</div>
53+
)
54+
}
55+
56+
export default QueueItem
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Component } from 'solid-js'
2+
import { For, Show } from 'solid-js'
3+
import { Transition, TransitionGroup } from 'solid-transition-group'
4+
5+
import Icon from '~/components/material/Icon'
6+
import { UploadItem } from '~/types'
7+
8+
import QueueItem from './QueueItem'
9+
10+
const QueueItemTable: Component<{ loading: boolean; items: () => UploadItem[]; error?: string; offline?: boolean }> = (props) => {
11+
return (
12+
<div class="relative h-[calc(4*3rem)] sm:h-[calc(6*3rem)]">
13+
<Transition
14+
appear
15+
enterActiveClass="transition-all duration-250 ease-in-out"
16+
exitActiveClass="transition-all duration-250 ease-in-out"
17+
enterClass="opacity-0"
18+
enterToClass="opacity-100"
19+
exitClass="opacity-100"
20+
exitToClass="opacity-0"
21+
>
22+
<Show
23+
when={!props.loading}
24+
fallback={
25+
<div class="flex justify-center items-center h-full animate-spin absolute inset-0">
26+
<Icon name="progress_activity" />
27+
</div>
28+
}
29+
>
30+
<Show
31+
when={!(props.offline && props.items().length === 0)}
32+
fallback={
33+
<div class="flex items-center justify-center h-full gap-2 text-on-surface-variant absolute inset-0">
34+
<Icon name="signal_disconnected" />
35+
<span>{props.error}</span>
36+
</div>
37+
}
38+
>
39+
<Show
40+
when={props.items().length > 0}
41+
fallback={
42+
<div class="flex items-center justify-center h-full gap-2 text-on-surface-variant absolute inset-0">
43+
<Icon name="cloud_done" />
44+
<span>No files in queue</span>
45+
</div>
46+
}
47+
>
48+
<div class="absolute inset-0 overflow-y-auto hide-scrollbar">
49+
<TransitionGroup
50+
name="list"
51+
enterActiveClass="transition-all duration-300 ease-in-out"
52+
exitActiveClass="transition-all duration-300 ease-in-out"
53+
enterClass="opacity-0 transform translate-x-4"
54+
enterToClass="opacity-100 transform translate-x-0"
55+
exitClass="opacity-100 transform translate-x-0"
56+
exitToClass="opacity-0 transform -translate-x-4"
57+
moveClass="transition-transform duration-300"
58+
>
59+
<For each={props.items()}>
60+
{(item) => (
61+
<div class="bg-surface-container-lowest rounded-md pb-1 sm:pb-2">
62+
<QueueItem item={item} />
63+
</div>
64+
)}
65+
</For>
66+
</TransitionGroup>
67+
</div>
68+
</Show>
69+
</Show>
70+
</Show>
71+
</Transition>
72+
</div>
73+
)
74+
}
75+
76+
export default QueueItemTable
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import clsx from 'clsx'
2+
import { createMemo } from 'solid-js'
3+
import type { Component } from 'solid-js'
4+
5+
import { UploadItem } from '~/types'
6+
7+
const QueueStatistics: Component<{ loading: boolean; items: () => UploadItem[]; class: string }> = (props) => {
8+
const uploadingCount = createMemo(() => (props.loading ? undefined : props.items().filter((i) => i.status === 'uploading').length))
9+
const waitingCount = createMemo(() => (props.loading ? undefined : props.items().filter((i) => i.status === 'queued').length))
10+
const totalCount = createMemo(() => (props.loading ? undefined : props.items().length))
11+
12+
return (
13+
<div class={clsx('flex h-10 w-full', props.class)}>
14+
<div class="flex grow flex-col justify-between">
15+
<span class="text-body-sm text-on-surface-variant">Uploading</span>
16+
<span class="font-mono text-label-lg uppercase">{uploadingCount()}</span>
17+
</div>
18+
19+
<div class="flex grow flex-col justify-between">
20+
<span class="text-body-sm text-on-surface-variant">Waiting</span>
21+
<span class="font-mono text-label-lg uppercase">{waitingCount()}</span>
22+
</div>
23+
24+
<div class="flex grow flex-col justify-between">
25+
<span class="text-body-sm text-on-surface-variant">Queued</span>
26+
<span class="font-mono text-label-lg uppercase">{totalCount()}</span>
27+
</div>
28+
</div>
29+
)
30+
}
31+
32+
export default QueueStatistics
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import clsx from 'clsx'
2+
import { Show } from 'solid-js'
3+
import type { Component } from 'solid-js'
4+
5+
import IconButton from '~/components/material/IconButton'
6+
import { useUploadQueue } from '~/hooks/use-upload-queue'
7+
8+
import QueueStatistics from './QueueStatistics'
9+
import QueueItemTable from './QueueItemTable'
10+
11+
interface UploadQueueProps {
12+
dongleId: string
13+
}
14+
15+
const UploadQueue: Component<UploadQueueProps> = (props) => {
16+
const { loading, error, items, offline, clearQueue, clearingQueue, clearQueueError } = useUploadQueue(props.dongleId)
17+
18+
return (
19+
<div class="flex flex-col border-2 border-t-0 border-surface-container-high bg-surface-container-lowest">
20+
<div class="flex">
21+
<div class="flex-auto">
22+
<QueueStatistics loading={loading()} items={items} class="p-4" />
23+
</div>
24+
<div class="flex p-4">
25+
<Show
26+
when={!clearQueueError()}
27+
fallback={<IconButton name="error" onClick={() => void clearQueue()} disabled={clearingQueue()} />}
28+
>
29+
<IconButton
30+
class={clsx(clearingQueue() && 'animate-spin')}
31+
name={clearingQueue() ? 'progress_activity' : 'delete'}
32+
onClick={() => void clearQueue()}
33+
disabled={clearingQueue()}
34+
/>
35+
</Show>
36+
</div>
37+
</div>
38+
<div class="rounded-md border-2 border-surface-container-high mx-4 mb-4 p-4">
39+
<QueueItemTable loading={loading()} items={items} error={error()} offline={offline()} />
40+
</div>
41+
</div>
42+
)
43+
}
44+
45+
export default UploadQueue

0 commit comments

Comments
 (0)