personal-gallery-node/packages/web/.eslintcache

1 line
32 KiB
Plaintext

[{"/home/max/personal-gallery/packages/web/src/index.tsx":"1","/home/max/personal-gallery/packages/web/src/reportWebVitals.ts":"2","/home/max/personal-gallery/packages/web/src/i18n.ts":"3","/home/max/personal-gallery/packages/web/src/App.tsx":"4","/home/max/personal-gallery/packages/web/src/services/images.ts":"5","/home/max/personal-gallery/packages/web/src/services/settings.ts":"6","/home/max/personal-gallery/packages/web/src/services/login.ts":"7","/home/max/personal-gallery/packages/web/src/services/meta.ts":"8","/home/max/personal-gallery/packages/web/src/services/user.ts":"9","/home/max/personal-gallery/packages/web/src/components/ImageGridList.tsx":"10","/home/max/personal-gallery/packages/web/src/components/PicturesAppBar.tsx":"11","/home/max/personal-gallery/packages/web/src/components/UploadDialog.tsx":"12","/home/max/personal-gallery/packages/web/src/components/LoginView.tsx":"13","/home/max/personal-gallery/packages/web/src/components/ConfiguarionDialog.tsx":"14","/home/max/personal-gallery/packages/web/src/components/ConfirmationDialog.tsx":"15","/home/max/personal-gallery/packages/web/src/components/CredentialChangeDialog.tsx":"16","/home/max/personal-gallery/packages/web/src/consts.ts":"17","/home/max/personal-gallery/packages/web/src/types.ts":"18","/home/max/personal-gallery/packages/web/src/components/ImageThumbnail.tsx":"19","/home/max/personal-gallery/packages/web/src/utils/ShareX.tsx":"20"},{"size":1000,"mtime":1615816463730,"results":"21","hashOfConfig":"22"},{"size":436,"mtime":1615818602060,"results":"23","hashOfConfig":"22"},{"size":764,"mtime":1615815861840,"results":"24","hashOfConfig":"22"},{"size":12308,"mtime":1615885799190,"results":"25","hashOfConfig":"22"},{"size":2119,"mtime":1615817648590,"results":"26","hashOfConfig":"22"},{"size":968,"mtime":1615817877420,"results":"27","hashOfConfig":"22"},{"size":1248,"mtime":1615817706350,"results":"28","hashOfConfig":"22"},{"size":677,"mtime":1615817723660,"results":"29","hashOfConfig":"22"},{"size":806,"mtime":1615817777070,"results":"30","hashOfConfig":"22"},{"size":898,"mtime":1615817335220,"results":"31","hashOfConfig":"22"},{"size":2672,"mtime":1615817441900,"results":"32","hashOfConfig":"22"},{"size":2575,"mtime":1615818415450,"results":"33","hashOfConfig":"22"},{"size":2631,"mtime":1615817416490,"results":"34","hashOfConfig":"22"},{"size":6483,"mtime":1615817971790,"results":"35","hashOfConfig":"22"},{"size":972,"mtime":1615817278210,"results":"36","hashOfConfig":"22"},{"size":3009,"mtime":1615817299500,"results":"37","hashOfConfig":"22"},{"size":93,"mtime":1615818609570,"results":"38","hashOfConfig":"22"},{"size":530,"mtime":1615818003610,"results":"39","hashOfConfig":"22"},{"size":3216,"mtime":1615817369930,"results":"40","hashOfConfig":"22"},{"size":462,"mtime":1615817925910,"results":"41","hashOfConfig":"22"},{"filePath":"42","messages":"43","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},"16j1s31",{"filePath":"45","messages":"46","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"47","messages":"48","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"49","messages":"50","errorCount":0,"warningCount":9,"fixableErrorCount":0,"fixableWarningCount":0,"source":"51","usedDeprecatedRules":"44"},{"filePath":"52","messages":"53","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"54","messages":"55","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"56","messages":"57","errorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"58","usedDeprecatedRules":"44"},{"filePath":"59","messages":"60","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"61","usedDeprecatedRules":"44"},{"filePath":"62","messages":"63","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"64","usedDeprecatedRules":"44"},{"filePath":"65","messages":"66","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"67","messages":"68","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"69","messages":"70","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"71","messages":"72","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"73","messages":"74","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"75","usedDeprecatedRules":"44"},{"filePath":"76","messages":"77","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"78","messages":"79","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"80","messages":"81","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"82","messages":"83","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"84","messages":"85","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"86","messages":"87","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"88"},"/home/max/personal-gallery/packages/web/src/index.tsx",[],["89","90","91","92","93","94"],"/home/max/personal-gallery/packages/web/src/reportWebVitals.ts",[],"/home/max/personal-gallery/packages/web/src/i18n.ts",[],"/home/max/personal-gallery/packages/web/src/App.tsx",["95","96","97","98","99","100","101","102","103"],"import React, { useEffect, useRef, useState } from 'react';\nimport {\n Container,\n makeStyles,\n Theme,\n createStyles,\n withWidth,\n WithWidth,\n Dialog,\n CircularProgress,\n IconButton,\n Snackbar,\n Grid,\n Typography,\n} from '@material-ui/core';\nimport InfiniteScroll from 'react-infinite-scroller';\nimport { AxiosError } from 'axios';\nimport { useTranslation } from 'react-i18next';\nimport * as imageService from './services/images';\nimport * as settingsService from './services/settings';\nimport * as loginService from './services/login';\nimport * as metaService from './services/meta';\nimport * as userService from './services/user';\nimport ImageGridListTile from './components/ImageGridList';\nimport {\n Image, Config, SortBy, SortOrder,\n} from './types';\nimport PicturesAppBar from './components/PicturesAppBar';\nimport UploadDialog from './components/UploadDialog';\nimport ConfigurationDialog from './components/ConfiguarionDialog';\nimport LoginView from './components/LoginView';\nimport ConfirmationDialog from './components/ConfirmationDialog';\nimport CredentialChangeDialog from './components/CredentialChangeDialog';\n\nconst useStyles = makeStyles((theme: Theme) => createStyles({\n root: {\n height: '100vh',\n },\n titleBar: {\n background:\n 'linear-gradient(to bottom, rgba(0,0,0,0) 0%, '\n + 'rgba(0,0,0,0) 70%, rgba(0,0,0,0) 100%)',\n transition: 'background 2s ease-out',\n '&:hover': {\n background:\n 'linear-gradient(to bottom, rgba(0,0,0,0) 0%, '\n + 'rgba(0,0,0,0) 70%, rgba(0,0,0,0) 100%)',\n },\n },\n icon: {\n color: 'white',\n filter: 'drop-shadow(2px 4px 3px #222222)',\n },\n listItem: {\n cursor: 'pointer',\n '&:hover': {\n opacity: '0.9',\n },\n },\n dialogImage: {\n maxHeight: '80vh',\n },\n loader: {\n margin: '1rem',\n },\n toolbarTitle: {\n flexGrow: 1,\n },\n toolbarButton: {\n flexGrow: 1,\n },\n placeholderText: {\n textAlign: 'center',\n margin: theme.spacing(1),\n color: '#696969',\n },\n}));\n\nfunction App(props: WithWidth) {\n const [modalImage, setModalImage] = useState('');\n const [modalVideo, setModalVideo] = useState('');\n const [imagesData, setImagesData] = useState<Image[] | undefined>(undefined);\n const imagesPage = useRef(0);\n const [hasMore, setHasMore] = useState(true);\n const [userSettings, setUserSettings] = useState<Config | undefined>(\n undefined,\n );\n const [dragOpen, setDragOpen] = useState(false);\n const [configurationDialogOpen, setConfigurationDialogOpen] = useState(false);\n const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);\n const [credentialChangeDialogOpen, setCredentialChangeDialogOpen] = useState(\n false,\n );\n const [notification, setNotification] = useState('');\n const [userLoggedIn, setUserLoggedIn] = useState<boolean | undefined>(\n undefined,\n );\n const [acceptedUploadFiletypes, setAcceptedUploadFiletypes] = useState([\n 'image/webp',\n 'image/avif',\n 'image/gif',\n 'image/png',\n 'image/jpeg',\n 'image/bmp',\n ]);\n const [setupFinished, setSetupFinished] = useState(true);\n const [apiKey, setApiKey] = useState<string | undefined>();\n\n const { width } = props;\n const { t, i18n } = useTranslation();\n\n const widthMap = {\n xs: 3, sm: 4, md: 5, lg: 6, xl: 6,\n };\n const cols = widthMap[width];\n const classes = useStyles();\n\n useEffect(() => {\n // imageService.getAll().then(result => setImagesData(result));\n metaService.getMeta().then((result) => {\n setAcceptedUploadFiletypes(result.accepted);\n setSetupFinished(result.setupFinished);\n setUserSettings(settingsService.getSettings());\n setUserLoggedIn(settingsService.getUserState());\n });\n }, []);\n const imageTileClickHandler = (url: string) => {\n if (/\\.(mp4|webm)$/.test(url)) {\n setModalVideo(url);\n } else {\n setModalImage(url);\n }\n };\n\n const handleLogout = async (clientOnly: boolean = false) => {\n if (!clientOnly) {\n loginService.doLogout();\n }\n setUserLoggedIn(false);\n setDragOpen(false);\n setConfirmDialogOpen(false);\n setConfigurationDialogOpen(false);\n setCredentialChangeDialogOpen(false);\n localStorage.clear();\n sessionStorage.clear();\n settingsService.setUserState(false);\n };\n\n const onDataNext = () => {\n console.log('requested more', imagesPage);\n imageService\n .getPage(\n imagesPage.current,\n userSettings?.sortBy,\n userSettings?.sortOrder,\n )\n .then((result) => {\n console.log(result);\n if (result.length === 0) {\n setHasMore(false);\n } else {\n setImagesData([...new Set([...(imagesData || []), ...result])]);\n }\n })\n .catch((e: AxiosError) => {\n if (e.response?.status === 401) {\n handleLogout(true);\n setNotification(t('Authorization error, please login'));\n } else {\n setNotification(t('Error getting image list from server'));\n }\n imagesPage.current -= 1;\n });\n imagesPage.current += 1;\n };\n\n const refreshData = () => {\n imagesPage.current = 0;\n setHasMore(true);\n setImagesData(undefined);\n };\n\n const handleUpload = async (images: File[]) => {\n setDragOpen(false);\n // const combinedResult: Image[] = [];\n const promises = images.map(async (image) => imageService.uploadImage(image));\n try {\n const combinedResult = await Promise.all(promises);\n if (combinedResult.length > 0) {\n if (imagesData !== undefined) {\n // refreshData(); // reset all pictures, keeps correct order but slower\n // eslint-disable-next-line max-len\n setImagesData([...combinedResult, ...imagesData]); // adds pictures to the start, faster and prettier but breaks sort\n } else {\n setImagesData([...combinedResult]);\n }\n }\n } catch (e) {\n setNotification(t('Error uploading image'));\n // refreshData();\n }\n };\n\n const handlePaste = (e: React.ClipboardEvent) => {\n console.log(e.clipboardData);\n if (e.clipboardData.items.length !== 0) {\n Array.from(e.clipboardData.items).forEach((item) => {\n console.log(item);\n if (acceptedUploadFiletypes.includes(item.type)) {\n const pasteAsFile = item.getAsFile();\n if (pasteAsFile !== null) {\n handleUpload([pasteAsFile]);\n }\n }\n });\n }\n };\n\n const handleSettingsChange = (sortBy: SortBy, sortOrder: SortOrder) => {\n setUserSettings({ ...userSettings, sortBy, sortOrder });\n setConfigurationDialogOpen(false);\n settingsService.saveSettings({ ...userSettings, sortBy, sortOrder });\n setNotification(t('Settings changed'));\n refreshData();\n };\n\n const handleLogin = async (username: string, password: string) => {\n const result = setupFinished\n ? await loginService.doLogin(username, password)\n : await loginService.doRegister(username, password);\n switch (result) {\n case 200:\n setUserLoggedIn(true);\n settingsService.setUserState(true);\n if (!setupFinished) {\n setSetupFinished(true);\n }\n break;\n case 401:\n setNotification(t('Icorrect username or password'));\n break;\n case 429:\n setNotification(t('Too many attempts, try again later'));\n break;\n default:\n setNotification(t('Unknown error occured, try again later'));\n break;\n }\n };\n\n const handleApiKeyChange = async () => {\n try {\n const key = await userService.getApiKey();\n await navigator.clipboard.writeText(key);\n setNotification(t('API token copied to clipboard'));\n setApiKey(key);\n console.log(key);\n } catch (e) {\n console.log(e);\n }\n };\n\n const handleCredentialsChange = async (\n oldPassword: string,\n username: string,\n password: string,\n ) => {\n try {\n await userService.updateCredentials(oldPassword, username, password);\n handleLogout();\n setNotification(t('Please login with your new credentials'));\n } catch (e) {\n if (e.response.status === 401) {\n setNotification(t('Check your old password and try again'));\n }\n }\n };\n\n console.log('current settings:', userSettings);\n if (userSettings === undefined || userLoggedIn === undefined) {\n return (\n <Grid container justify=\"center\">\n <CircularProgress className={classes.loader} />\n </Grid>\n );\n }\n console.log('setup status:', t('TEST_TEST'));\n console.log(i18n);\n return (\n <div\n className={classes.root}\n onDragEnter={() => setDragOpen(true)}\n onPaste={handlePaste}\n >\n {userLoggedIn ? (\n <>\n <PicturesAppBar\n onUploadClick={() => setDragOpen(true)}\n onSettingsClick={() => setConfigurationDialogOpen(true)}\n onLogoutClick={handleLogout}\n />\n <Container>\n <InfiniteScroll\n loadMore={onDataNext}\n pageStart={-1}\n hasMore={hasMore}\n loader={(\n <Grid container justify=\"center\">\n <CircularProgress className={classes.loader} />\n </Grid>\n )}\n >\n {imagesData !== undefined ? (\n <ImageGridListTile\n images={imagesData}\n cols={cols}\n onTileClick={imageTileClickHandler}\n onNotification={setNotification}\n />\n ) : (\n <Typography className={classes.placeholderText}>\n {t('Upload your first image')}\n </Typography>\n )}\n </InfiniteScroll>\n </Container>\n </>\n ) : (\n <LoginView onLogin={handleLogin} setupFinished={setupFinished} />\n )}\n\n <Dialog\n open={modalImage !== '' || modalVideo !== ''}\n onClose={() => {\n setModalImage('');\n setModalVideo('');\n }}\n aria-labelledby=\"alert-dialog-title\"\n aria-describedby=\"alert-dialog-description\"\n maxWidth=\"lg\"\n >\n {modalImage !== '' ? (\n <img className={classes.dialogImage} src={modalImage} alt=\"\" />\n ) : null}\n {modalVideo !== '' ? (\n // eslint-disable-next-line jsx-a11y/media-has-caption\n <video\n className={classes.dialogImage}\n autoPlay\n controls\n src={modalVideo}\n />\n ) : null}\n </Dialog>\n <UploadDialog\n isOpen={dragOpen}\n onClose={() => setDragOpen(false)}\n onDrop={handleUpload}\n accept={acceptedUploadFiletypes}\n />\n <ConfigurationDialog\n open={configurationDialogOpen}\n onDialogClose={() => setConfigurationDialogOpen(false)}\n currentSettings={userSettings}\n onSave={handleSettingsChange}\n onApiKeyChange={() => setConfirmDialogOpen(true)}\n apiKey={apiKey}\n onCredentialsChange={() => setCredentialChangeDialogOpen(true)}\n />\n <ConfirmationDialog\n header={t('Get new API key?')}\n content={t('This will invalidate your previous API key, continue?')}\n open={confirmDialogOpen}\n onConfirm={() => {\n handleApiKeyChange();\n setConfirmDialogOpen(false);\n }}\n onCancel={() => setConfirmDialogOpen(false)}\n />\n <CredentialChangeDialog\n open={credentialChangeDialogOpen}\n onDialogClose={() => setCredentialChangeDialogOpen(false)}\n onCredentialsUpdate={handleCredentialsChange}\n onNotification={(text) => setNotification(text)}\n />\n <Snackbar\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n open={notification.length !== 0}\n autoHideDuration={4000}\n onClose={() => setNotification('')}\n message={notification}\n action={(\n <>\n <IconButton\n size=\"small\"\n aria-label=\"close\"\n color=\"inherit\"\n onClick={() => setNotification('')}\n >\n <span className=\"material-icons\">close</span>\n </IconButton>\n </>\n )}\n />\n </div>\n );\n}\n\nexport default withWidth()(App);\n","/home/max/personal-gallery/packages/web/src/services/images.ts",[],"/home/max/personal-gallery/packages/web/src/services/settings.ts",[],"/home/max/personal-gallery/packages/web/src/services/login.ts",["104","105","106","107"],"import axios from 'axios';\nimport { API_BASE_URL } from '../consts';\n\n/**\n *\n * @param username Plaintext username\n * @param password Plaintext password\n * @returns http status code\n */\nexport const doLogin = async (\n username: string,\n password: string,\n): Promise<number> => {\n try {\n const { status } = await axios.post(\n `${API_BASE_URL}/login`,\n {\n username,\n password,\n },\n {\n validateStatus: (statusCode) => statusCode >= 200 && statusCode < 500,\n },\n );\n return status;\n } catch (e) {\n console.log(e);\n return e.response.status || 500;\n }\n};\n\nexport const doRegister = async (\n username: string,\n password: string,\n): Promise<number> => {\n try {\n const { status } = await axios.post(\n `${API_BASE_URL}/login/register`,\n {\n username,\n password,\n },\n {\n validateStatus: (statusCode) => statusCode >= 200 && statusCode < 500,\n },\n );\n return status;\n } catch (e) {\n console.log(e);\n return e.response.status || 500;\n }\n};\n\nexport const doLogout = async () => {\n try {\n const result = await axios.post(`${API_BASE_URL}/login/logout`);\n console.log(result.data);\n } catch (e) {\n console.log(e);\n }\n};\n","/home/max/personal-gallery/packages/web/src/services/meta.ts",["108","109"],"import axios from 'axios';\nimport { API_BASE_URL } from '../consts';\nimport { Meta } from '../types';\n\n// eslint-disable-next-line import/prefer-default-export\nexport const getMeta = async (): Promise<Meta> => {\n const { data } = await axios.get(`${API_BASE_URL}/meta`);\n if (\n Array.isArray(data.accepted)\n && data.accepted.every((x: any): x is string => typeof x === 'string')\n && typeof data.setupFinished === 'boolean'\n ) {\n return { accepted: data.accepted, setupFinished: data.setupFinished };\n }\n console.log('truth:', Object.prototype.toString.call(data.accepted));\n console.log(data.setupFinished);\n throw new Error('Malformed server response');\n};\n","/home/max/personal-gallery/packages/web/src/services/user.ts",["110"],"import axios from 'axios';\nimport { API_BASE_URL } from '../consts';\n\nexport const getApiKey = async (): Promise<string> => {\n const { data } = await axios.post(`${API_BASE_URL}/user/getApiKey`);\n if (typeof data?.token === 'string') {\n return data.token;\n }\n throw new Error('Malformed server response');\n};\n\nexport const updateCredentials = async (\n oldPassword: string,\n username: string,\n password: string,\n) => {\n console.log('username:', username, ' password:', password);\n const { data } = await axios.post(\n `${API_BASE_URL}/user/updateCredentials`,\n {\n ...(username !== '' ? { username } : {}),\n ...(password !== '' ? { password } : {}),\n oldPassword,\n },\n {},\n );\n if (data.status !== 'success') {\n throw new Error('Malformed server response');\n }\n};\n","/home/max/personal-gallery/packages/web/src/components/ImageGridList.tsx",[],"/home/max/personal-gallery/packages/web/src/components/PicturesAppBar.tsx",[],"/home/max/personal-gallery/packages/web/src/components/UploadDialog.tsx",[],"/home/max/personal-gallery/packages/web/src/components/LoginView.tsx",[],"/home/max/personal-gallery/packages/web/src/components/ConfiguarionDialog.tsx",["111","112"],"import {\n Button,\n createStyles,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControl,\n FormControlLabel,\n FormLabel,\n InputLabel,\n makeStyles,\n MenuItem,\n Radio,\n RadioGroup,\n Select,\n Theme,\n Tooltip,\n} from '@material-ui/core';\nimport React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport generateConfig from '../utils/ShareX';\nimport { availableLanguages } from '../i18n';\nimport { Config, SortBy, SortOrder } from '../types';\n\nconst useStyles = makeStyles((theme: Theme) => createStyles({\n form: {\n display: 'flex',\n flexDirection: 'column',\n margin: 'auto',\n width: 'fit-content',\n },\n formControl: {\n margin: theme.spacing(1),\n },\n wide: {\n width: '100%',\n },\n buttonSpan: {\n display: 'inline-flex',\n flexGrow: 1,\n },\n button: {\n flexGrow: 1,\n },\n}));\n\nconst ConfigurationDialog = ({\n open,\n onDialogClose,\n currentSettings,\n onSave,\n onApiKeyChange,\n onCredentialsChange,\n apiKey,\n}: {\n open: boolean;\n onDialogClose: () => void;\n currentSettings: Config;\n onSave: (sortBy: SortBy, sortOrder: SortOrder) => void;\n onApiKeyChange: () => void;\n onCredentialsChange: () => void;\n apiKey?: string;\n}) => {\n const [sortBy, setSortBy] = useState(currentSettings.sortBy);\n const [sortOrder, setSortOrder] = useState(currentSettings.sortOrder);\n const classes = useStyles();\n const { t, i18n } = useTranslation();\n const handleSave = () => {\n onSave(sortBy, sortOrder);\n };\n const downloadSharexConfig = () => {\n if (typeof apiKey === 'undefined') {\n console.log('This should never happen');\n return;\n }\n const configText = generateConfig(\n window.location.href.replace(/\\/$/, ''),\n apiKey,\n );\n const element = document.createElement('a');\n element.setAttribute(\n 'href',\n `data:text/plain;charset=utf-8,${encodeURIComponent(configText)}`,\n );\n element.setAttribute(\n 'download',\n `${window.location.href.replace(/\\/$/, '')}-sharex.sxcu`,\n );\n element.style.display = 'none';\n document.body.appendChild(element);\n element.click();\n document.body.removeChild(element);\n };\n console.log();\n return (\n <Dialog onClose={onDialogClose} open={open} aria-labelledby=\"dialog-title\">\n <DialogTitle id=\"dialog-title\">{t('Settings')}</DialogTitle>\n <DialogContent>\n {/* <DialogContentText>\n Configure how you want your list to be displayed.\n </DialogContentText> */}\n <form className={classes.form} noValidate>\n <FormControl className={classes.formControl}>\n <FormLabel component=\"legend\">{t('Sort By')}</FormLabel>\n <RadioGroup\n row\n aria-label=\"position\"\n name=\"position\"\n value={sortBy}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => (\n setSortBy(e.target.value as SortBy)\n )}\n >\n <FormControlLabel\n value={SortBy.Name}\n control={<Radio color=\"primary\" />}\n label={t('Name')}\n />\n <FormControlLabel\n value={SortBy.Date}\n control={<Radio color=\"primary\" />}\n label={t('Upload date')}\n />\n </RadioGroup>\n </FormControl>\n <FormControl className={classes.formControl}>\n <FormLabel component=\"legend\">{t('Sort Direction')}</FormLabel>\n <RadioGroup\n row\n aria-label=\"position\"\n name=\"position\"\n value={sortOrder}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => (\n setSortOrder(e.target.value as SortOrder)\n )}\n >\n <FormControlLabel\n value={SortOrder.Ascending}\n control={<Radio color=\"primary\" />}\n label={t('Ascending')}\n />\n <FormControlLabel\n value={SortOrder.Descending}\n control={<Radio color=\"primary\" />}\n label={t('Descending')}\n />\n </RadioGroup>\n </FormControl>\n </form>\n <div className={classes.form}>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"demo-simple-select-label\">Language</InputLabel>\n <Select\n labelId=\"demo-simple-select-label\"\n id=\"demo-simple-select\"\n value={i18n.language.slice(0, 2)}\n onChange={(e) => i18n.changeLanguage(e.target.value as string)}\n >\n {availableLanguages.map((lang) => (\n <MenuItem value={lang.short} key={lang.short}>\n {lang.longLocal}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n <Button\n className={classes.formControl}\n variant=\"contained\"\n color=\"primary\"\n onClick={onCredentialsChange}\n >\n {t('Change username/password')}\n </Button>\n <Button\n className={classes.formControl}\n variant=\"contained\"\n color=\"primary\"\n onClick={onApiKeyChange}\n >\n {t('Get API key')}\n </Button>\n <Tooltip\n title={(\n <>\n {t('You must first get a new API key')}\n </>\n )}\n disableFocusListener={typeof apiKey !== 'undefined'}\n disableHoverListener={typeof apiKey !== 'undefined'}\n disableTouchListener={typeof apiKey !== 'undefined'}\n >\n <span className={classes.buttonSpan}>\n <Button\n disabled={typeof apiKey === 'undefined'}\n className={`${classes.formControl} ${classes.button}`}\n variant=\"contained\"\n color=\"primary\"\n onClick={downloadSharexConfig}\n >\n {t('Get ShareX config')}\n </Button>\n </span>\n </Tooltip>\n </div>\n </DialogContent>\n <DialogActions>\n <Button color=\"primary\" onClick={handleSave}>\n {t('Save')}\n </Button>\n <Button color=\"primary\" onClick={onDialogClose}>\n {t('Cancel')}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n\nexport default ConfigurationDialog;\n","/home/max/personal-gallery/packages/web/src/components/ConfirmationDialog.tsx",[],"/home/max/personal-gallery/packages/web/src/components/CredentialChangeDialog.tsx",[],"/home/max/personal-gallery/packages/web/src/consts.ts",[],"/home/max/personal-gallery/packages/web/src/types.ts",[],"/home/max/personal-gallery/packages/web/src/components/ImageThumbnail.tsx",[],"/home/max/personal-gallery/packages/web/src/utils/ShareX.tsx",[],["113","114","115","116","117","118"],{"ruleId":"119","replacedBy":"120"},{"ruleId":"121","replacedBy":"122"},{"ruleId":"123","replacedBy":"124"},{"ruleId":"125","replacedBy":"126"},{"ruleId":"127","replacedBy":"128"},{"ruleId":"129","replacedBy":"130"},{"ruleId":"131","severity":1,"message":"132","line":150,"column":5,"nodeType":"133","messageId":"134","endLine":150,"endColumn":16},{"ruleId":"131","severity":1,"message":"132","line":158,"column":9,"nodeType":"133","messageId":"134","endLine":158,"endColumn":20},{"ruleId":"131","severity":1,"message":"132","line":205,"column":5,"nodeType":"133","messageId":"134","endLine":205,"endColumn":16},{"ruleId":"131","severity":1,"message":"132","line":208,"column":9,"nodeType":"133","messageId":"134","endLine":208,"endColumn":20},{"ruleId":"131","severity":1,"message":"132","line":257,"column":7,"nodeType":"133","messageId":"134","endLine":257,"endColumn":18},{"ruleId":"131","severity":1,"message":"132","line":259,"column":7,"nodeType":"133","messageId":"134","endLine":259,"endColumn":18},{"ruleId":"131","severity":1,"message":"132","line":279,"column":3,"nodeType":"133","messageId":"134","endLine":279,"endColumn":14},{"ruleId":"131","severity":1,"message":"132","line":287,"column":3,"nodeType":"133","messageId":"134","endLine":287,"endColumn":14},{"ruleId":"131","severity":1,"message":"132","line":288,"column":3,"nodeType":"133","messageId":"134","endLine":288,"endColumn":14},{"ruleId":"131","severity":1,"message":"132","line":27,"column":5,"nodeType":"133","messageId":"134","endLine":27,"endColumn":16},{"ruleId":"131","severity":1,"message":"132","line":49,"column":5,"nodeType":"133","messageId":"134","endLine":49,"endColumn":16},{"ruleId":"131","severity":1,"message":"132","line":57,"column":5,"nodeType":"133","messageId":"134","endLine":57,"endColumn":16},{"ruleId":"131","severity":1,"message":"132","line":59,"column":5,"nodeType":"133","messageId":"134","endLine":59,"endColumn":16},{"ruleId":"131","severity":1,"message":"132","line":15,"column":3,"nodeType":"133","messageId":"134","endLine":15,"endColumn":14},{"ruleId":"131","severity":1,"message":"132","line":16,"column":3,"nodeType":"133","messageId":"134","endLine":16,"endColumn":14},{"ruleId":"131","severity":1,"message":"132","line":17,"column":3,"nodeType":"133","messageId":"134","endLine":17,"endColumn":14},{"ruleId":"131","severity":1,"message":"132","line":74,"column":7,"nodeType":"133","messageId":"134","endLine":74,"endColumn":18},{"ruleId":"131","severity":1,"message":"132","line":95,"column":3,"nodeType":"133","messageId":"134","endLine":95,"endColumn":14},{"ruleId":"119","replacedBy":"135"},{"ruleId":"121","replacedBy":"136"},{"ruleId":"123","replacedBy":"137"},{"ruleId":"125","replacedBy":"138"},{"ruleId":"127","replacedBy":"139"},{"ruleId":"129","replacedBy":"140"},"lines-around-directive",["141"],"no-spaced-func",["142"],"global-require",[],"no-buffer-constructor",[],"no-new-require",[],"no-path-concat",[],"no-console","Unexpected console statement.","MemberExpression","unexpected",["141"],["142"],[],[],[],[],"padding-line-between-statements","func-call-spacing"]