Skip to content

Commit a0181f9

Browse files
committed
WIP - Download Button
1 parent c1257de commit a0181f9

File tree

5 files changed

+150
-5
lines changed

5 files changed

+150
-5
lines changed

cmd/server/pactasrv/conv/pacta_to_oapi.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,13 @@ func UserToOAPI(user *pacta.User) (*api.User, error) {
9090
if user == nil {
9191
return nil, oapierr.Internal("userToOAPI: can't convert nil pointer")
9292
}
93-
lang, err := LanguageToOAPI(user.PreferredLanguage)
94-
if err != nil {
95-
return nil, oapierr.Internal("userToOAPI: languageToOAPI failed", zap.Error(err))
93+
var lang *api.Language
94+
if user.PreferredLanguage != "" {
95+
l, err := LanguageToOAPI(user.PreferredLanguage)
96+
if err != nil {
97+
return nil, oapierr.Internal("userToOAPI: languageToOAPI failed", zap.Error(err))
98+
}
99+
lang = &l
96100
}
97101
return &api.User{
98102
Admin: user.Admin,
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<script setup lang="ts">
2+
import { Blob } from '@/openapi/generated/pacta'
3+
import { computed } from 'vue'
4+
5+
const { t } = useI18n()
6+
const pactaClient = usePACTA()
7+
8+
interface Props {
9+
blobs: Blob[]
10+
cta: string
11+
}
12+
const props = defineProps<Props>()
13+
14+
const prefix = 'components/download/BlobButton'
15+
const tt = (key: string) => t(`${prefix}.${key}`)
16+
17+
enum Stage {
18+
Inactive,
19+
GettingURLs,
20+
Downloading,
21+
Done,
22+
Error,
23+
}
24+
25+
const statePrefix = `${prefix}[${useStateIDGenerator().id()}]`
26+
const stage = useState<Stage>(`${statePrefix}.stage`, () => Stage.Inactive)
27+
const downloadingPercentages = useState<number[]>(`${statePrefix}.downloadingPercentages`, () => [])
28+
29+
const message = computed(() => downloaded.value ? tt('Downloaded') : props.cta)
30+
const disabled = computed(() => stage.value !== Stage.Inactive)
31+
const downloadingPercentage = computed(() => {
32+
if (downloadingPercentages.value.length === 0) {
33+
return 0
34+
}
35+
return downloadingPercentages.value.reduce((a, b) => a + b, 0) / downloadingPercentages.value.length
36+
})
37+
38+
const progressPath = computed(() => {
39+
const radius = 45
40+
const rotate = -90
41+
const startX = 50
42+
const startY = 50 - radius
43+
const endAngle = rotate - 360 * downloadingPercentage.value / 100
44+
const endX = 50 + radius * Math.cos(endAngle * Math.PI / 180)
45+
const endY = 50 + radius * Math.sin(endAngle * Math.PI / 180)
46+
return `M ${startX} ${startY} A ${radius}${radius} 0 0 1 0 ${endX} ${endY}`
47+
})
48+
49+
interface BlobWithUrl extends Blob {
50+
url: string
51+
}
52+
53+
const download = async () => {
54+
stage.value = Stage.GettingURLs
55+
downloadingPercentages.value = props.blobs.map(() => 0)
56+
const abcResp = await pactaClient.accessBlobContent({
57+
items: props.blobs.map(b => ({ blobId: b.id })),
58+
})
59+
const blobsWithUrls: BlobWithUrl[] = props.blobs.map(b => ({ ...b, url: '' }))
60+
for (const item of abcResp.items) {
61+
presentOrFileBug(blobsWithUrls.find(b => b.id === item.blobId)).url = item.url
62+
}
63+
stage.value = Stage.Downloading
64+
const downloadPromises = blobsWithUrls.map((b, i) => downloadFile(b.url, b.name, (percentage) => {
65+
downloadingPercentages.value[i] = percentage
66+
}))
67+
await Promise.all(downloadPromises)
68+
stage.value = Stage.Done
69+
}
70+
71+
const downloadFile = async (url: string, filename: string, progressCallback: (percentage: number) => void): Promise<void> => {
72+
await new Promise<void>((resolve, reject) => {
73+
const xhr = new XMLHttpRequest()
74+
xhr.responseType = 'blob'
75+
xhr.onprogress = (event) => {
76+
if (event.lengthComputable) {
77+
const percentage = Math.round((event.loaded / event.total) * 100)
78+
progressCallback(percentage)
79+
}
80+
}
81+
xhr.onload = () => {
82+
if (xhr.status === 200) {
83+
const blob = new Blob([xhr.response], { type: 'application/octet-stream' })
84+
const downloadUrl = URL.createObjectURL(blob)
85+
const a = document.createElement('a')
86+
a.href = downloadUrl
87+
a.download = filename
88+
document.body.appendChild(a)
89+
a.click()
90+
URL.revokeObjectURL(downloadUrl)
91+
a.remove()
92+
resolve()
93+
} else {
94+
reject(new Error(`Download failed: ${xhr.statusText}`))
95+
}
96+
}
97+
xhr.onerror = (err) => {
98+
reject(err)
99+
}
100+
xhr.open('GET', url)
101+
xhr.send()
102+
})
103+
}
104+
</script>
105+
106+
<template>
107+
<PVButton
108+
:disabled="disabled"
109+
class="text-sm"
110+
@click="download"
111+
>
112+
<div class="flex gap-2">
113+
<i
114+
v-if="stage === Stage.Inactive"
115+
class="pi pi-download"
116+
/>
117+
<i
118+
v-else-if="stage === Stage.GettingURLs"
119+
class="pi pi-spin pi-spinner"
120+
/>
121+
<svg
122+
v-else-if="stage === Stage.Downloading"
123+
width="100"
124+
height="100"
125+
viewBox="0 0 100 100"
126+
>
127+
<path
128+
:d="progressPath"
129+
fill="none"
130+
stroke="green"
131+
stroke-width="10"
132+
stroke-linecap="round"
133+
/>
134+
</svg>
135+
<i
136+
v-else-if="stage === Stage.Done"
137+
class="pi pi-check"
138+
/>
139+
<span>{{ message }}<span v-if="props.blobs.length > 1">({{ props.blobs.length }})</span></span>
140+
</div>
141+
</PVButton>
142+
</template>

frontend/components/DownloadButton.vue renamed to frontend/components/download/Button.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ interface Props {
1010
}
1111
const props = defineProps<Props>()
1212
13-
const prefix = 'components/DownloadButton'
13+
const prefix = 'components/download/Button'
1414
const tt = (key: string) => t(`${prefix}.${key}`)
1515
1616
const statePrefix = `${prefix}[${useStateIDGenerator().id()}]`

frontend/pages/button-test.vue

Whitespace-only changes.

openapi/pacta.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1370,7 +1370,6 @@ components:
13701370
- admin
13711371
- superAdmin
13721372
- name
1373-
- preferredLanguage
13741373
properties:
13751374
id:
13761375
type: string

0 commit comments

Comments
 (0)