#53 added qr-creator and button in admin interface (#424)

Co-authored-by: amueller <amueller@scopeset.de>

arminus committed Dec 31, 2023 at 19:08 UTC b0fabfcbed46c54a27f6120e7d2912bf4d4b8870
2 files changed +49 -3
admin/src/FileForm.ts
+48 -3
@@ -2,7 +2,7 @@
2
3 import { state, useSnapState } from './state'
4 import { createElement as h, forwardRef, ReactElement, ReactNode, useEffect, useMemo, useState } from 'react'
5 -import { Alert, Box, Collapse, FormHelperText, Link, MenuItem, MenuList } from '@mui/material'
5 +import { Alert, Box, Collapse, FormHelperText, Link, MenuItem, MenuList, Dialog, DialogTitle, DialogContent, Button } from '@mui/material'
6 import { BoolField, DisplayField, Field, FieldProps, Form, MultiSelectField, SelectField, StringField
7 } from '@hfs/mui-grid-form'
8 import { apiCall, UseApi, useApiEx } from './api'
@@ -15,8 +15,9 @@ import _ from 'lodash'
15 import FileField from './FileField'
16 import { alertDialog, toast, useDialogBarColors } from './dialog'
17 import yaml from 'yaml'
18 -import { Check, ContentCopy, ContentCut, ContentPaste, Delete, Edit, Save } from '@mui/icons-material'
18 +import { Check, ContentCopy, ContentCut, ContentPaste, Delete, Edit, QrCode2, Save } from '@mui/icons-material'
19 import { moveVfs } from './VfsTree'
20 +import QrCreator from 'qr-creator';
21
22 interface Account { username: string }
23
@@ -279,6 +280,32 @@ function LinkField({ value, statusApi }: LinkFieldProps) {
280 value &&= value.indexOf(root) === 1 ? value.slice(root.length) : undefined
281 const link = prefix(data?.baseUrl || '', value)
282 const RenderLink = useMemo(() => forwardRef((props: any, ref) => h(Link, { ref, ...props, href: link, style: { height: 'auto' }, target: 'frontend' }, link)), [link])
283 +
284 +
285 + const [isQrDialogOpen, setQrDialogOpen] = useState(false);
286 +
287 + const showQrDialog = () => {
288 + setQrDialogOpen(true);
289 + };
290 +
291 + const closeDialog = () => {
292 + setQrDialogOpen(false);
293 + };
294 +
295 + const generateQRCode = async (canvas: HTMLCanvasElement, text: string) => {
296 + try {
297 + QrCreator.render({
298 + text: text,
299 + radius: 0.0, // 0.0 to 0.5
300 + ecLevel: 'H', // L, M, Q, H
301 + fill: '#536DFE', // foreground color
302 + background: null, // color or null for transparent
303 + size: 300 // in pixels
304 + }, canvas);
305 + } catch (error) {
306 + console.error('Error generating QR code:', error);
307 + }
308 + };
309 return h(Box, { display: 'flex' },
310 !urls ? 'error' : // check data is ok
311 h(DisplayField, {
@@ -293,10 +320,28 @@ function LinkField({ value, statusApi }: LinkFieldProps) {
320 disabled: !link,
321 onClick: () => navigator.clipboard.writeText(link)
322 }),
323 + h(IconBtn, {
324 + icon: QrCode2,
325 + title: "QR Code",
326 + onClick: showQrDialog
327 + }),
328 h(IconBtn, { icon: Edit, title: "Change", onClick() { changeBaseUrl().then(reload) } }),
329 )
330 }),
299 - )
331 + h(Dialog, { open: isQrDialogOpen, onClose: closeDialog },
332 + h(DialogTitle, null, "QR Code"),
333 + h(DialogContent, { style: { maxWidth: '350px', margin: '0 auto' } },
334 + h('canvas', {
335 + id: 'qrcode-canvas',
336 + ref: (canvas: HTMLCanvasElement) => canvas && generateQRCode(canvas, link),
337 + style: { width: '100%' },
338 + }),
339 + ),
340 + h(Box, { display: 'flex', justifyContent: 'center', marginBottom: 2 },
341 + h(Button, { onClick: closeDialog }, "Close")
342 + )
343 + )
344 + );
345 }
346
347 export async function changeBaseUrl() {
package.json
+1
@@ -82,6 +82,7 @@
82 "minimist": "^1.2.6",
83 "nat-upnp-ts": "^2.0.1",
84 "open": "^8.4.0",
85 + "qr-creator": "^1.0.0",
86 "picomatch": "^3.0.1",
87 "tssrp6a": "^3.0.0",
88 "unzip-stream": "^0.3.1",