parent
35cd904a63
commit
8fcc67022d
17 changed files with 22718 additions and 153 deletions
@ -0,0 +1,31 @@ |
|||||||
|
import { Button } from '@material-ui/core'; |
||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
export default function FileUploadButton(props: any) { |
||||||
|
const hiddenFileInput = React.useRef<null | any>(null); |
||||||
|
const { onGetFile, ...restProps } = props; |
||||||
|
|
||||||
|
const handleClick = (event: any) => { |
||||||
|
if (hiddenFileInput) { |
||||||
|
hiddenFileInput.current.click(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const handleChange = (event: any) => { |
||||||
|
const fileUploaded = event.target.files[0]; |
||||||
|
onGetFile(fileUploaded); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Button onClick={handleClick} {...restProps}> |
||||||
|
{props.children} |
||||||
|
</Button> |
||||||
|
<input type="file" |
||||||
|
ref={hiddenFileInput} |
||||||
|
onChange={handleChange} |
||||||
|
style={{ display: 'none' }} |
||||||
|
/> |
||||||
|
</> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,124 @@ |
|||||||
|
import React, { ReactFragment, useReducer, useState } from 'react'; |
||||||
|
import { WindowState } from "../Windows"; |
||||||
|
import { Box, Paper, Typography, TextField, Button, Dialog, CircularProgress } from "@material-ui/core"; |
||||||
|
import SaveIcon from '@material-ui/icons/Save'; |
||||||
|
import GetAppIcon from '@material-ui/icons/GetApp'; |
||||||
|
import PublishIcon from '@material-ui/icons/Publish'; |
||||||
|
import DeleteForeverIcon from '@material-ui/icons/DeleteForever'; |
||||||
|
import { Alert } from '@material-ui/lab'; |
||||||
|
import * as serverApi from '../../../api/api'; |
||||||
|
import { getDBExportLink, importDB, wipeDB } from '../../../lib/backend/data'; |
||||||
|
import FileUploadButton from '../../common/FileUploadButton'; |
||||||
|
import { DBImportRequest } from '../../../api/api'; |
||||||
|
|
||||||
|
export interface ManageDataWindowState extends WindowState { |
||||||
|
dummy: boolean |
||||||
|
} |
||||||
|
export enum ManageDataWindowActions { |
||||||
|
SetDummy = "SetDummy", |
||||||
|
} |
||||||
|
export function ManageDataWindowReducer(state: ManageDataWindowState, action: any) { |
||||||
|
switch (action.type) { |
||||||
|
case ManageDataWindowActions.SetDummy: { |
||||||
|
return state; |
||||||
|
} |
||||||
|
default: |
||||||
|
throw new Error("Unimplemented ManageDataWindow state update.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default function ManageDataWindow(props: {}) { |
||||||
|
const [state, dispatch] = useReducer(ManageDataWindowReducer, { |
||||||
|
dummy: true, |
||||||
|
}); |
||||||
|
|
||||||
|
return <ManageDataWindowControlled state={state} dispatch={dispatch} /> |
||||||
|
} |
||||||
|
|
||||||
|
export function ManageDataWindowControlled(props: { |
||||||
|
state: ManageDataWindowState, |
||||||
|
dispatch: (action: any) => void, |
||||||
|
}) { |
||||||
|
let [alert, setAlert] = useState<ReactFragment>(<></>); |
||||||
|
|
||||||
|
let handleImport = async (jsonData: DBImportRequest) => {
|
||||||
|
try { |
||||||
|
setAlert(<CircularProgress/>) |
||||||
|
await importDB(jsonData); |
||||||
|
} catch (e) { |
||||||
|
setAlert(<Alert severity="error">Failed to import database.</Alert>) |
||||||
|
return; |
||||||
|
} |
||||||
|
setAlert(<Alert severity="success">Successfully imported database.</Alert>) |
||||||
|
} |
||||||
|
|
||||||
|
let handleWipe = async () => { |
||||||
|
try { |
||||||
|
setAlert(<CircularProgress/>) |
||||||
|
await wipeDB(); |
||||||
|
} catch (e) { |
||||||
|
setAlert(<Alert severity="error">Failed to wipe database.</Alert>) |
||||||
|
return; |
||||||
|
} |
||||||
|
setAlert(<Alert severity="success">Successfully wiped database.</Alert>) |
||||||
|
} |
||||||
|
|
||||||
|
return <> |
||||||
|
<Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
<SaveIcon style={{ fontSize: 80 }} /> |
||||||
|
</Box> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
<Typography variant="h4">Manage Data</Typography> |
||||||
|
<Box mt={2} /> |
||||||
|
<Typography> |
||||||
|
An exported database contains all your artists, albums, tracks and tags.<br /> |
||||||
|
It is represented as a JSON structure. |
||||||
|
</Typography> |
||||||
|
<Box mt={2} /> |
||||||
|
<Alert severity="warning">Imported items will not be merged with existing ones. If you wish to replace your database, wipe it first.</Alert> |
||||||
|
{alert} |
||||||
|
<Box display="flex" flexDirection="column" alignItems="left"> |
||||||
|
<Box mt={2}> |
||||||
|
<a href={getDBExportLink()}> |
||||||
|
<Button variant="outlined"> |
||||||
|
<GetAppIcon /> |
||||||
|
Export |
||||||
|
</Button> |
||||||
|
</a> |
||||||
|
</Box> |
||||||
|
<Box mt={2} > |
||||||
|
<FileUploadButton variant="outlined" |
||||||
|
onGetFile={(file: any) => { |
||||||
|
const fileReader = new FileReader(); |
||||||
|
fileReader.readAsText(file, "UTF-8"); |
||||||
|
fileReader.onload = (e: any) => { |
||||||
|
let json: DBImportRequest = JSON.parse(e.target.result); |
||||||
|
handleImport(json); |
||||||
|
} |
||||||
|
}}> |
||||||
|
<PublishIcon /> |
||||||
|
Import |
||||||
|
</FileUploadButton> |
||||||
|
</Box> |
||||||
|
<Box mt={2} > |
||||||
|
<Button variant="outlined" |
||||||
|
onClick={handleWipe} |
||||||
|
> |
||||||
|
<DeleteForeverIcon /> |
||||||
|
Wipe |
||||||
|
</Button> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
</> |
||||||
|
} |
@ -1,65 +0,0 @@ |
|||||||
import React, { useReducer, useState } from 'react'; |
|
||||||
import { WindowState } from "../Windows"; |
|
||||||
import { Box, Paper, Typography, TextField, Button } from "@material-ui/core"; |
|
||||||
import SaveIcon from '@material-ui/icons/Save'; |
|
||||||
import { Alert } from '@material-ui/lab'; |
|
||||||
import * as serverApi from '../../../api/api'; |
|
||||||
|
|
||||||
export interface ManageImportExportWindowState extends WindowState { |
|
||||||
dummy: boolean |
|
||||||
} |
|
||||||
export enum ManageImportExportWindowActions { |
|
||||||
SetDummy = "SetDummy", |
|
||||||
} |
|
||||||
export function ManageImportExportWindowReducer(state: ManageImportExportWindowState, action: any) { |
|
||||||
switch (action.type) { |
|
||||||
case ManageImportExportWindowActions.SetDummy: { |
|
||||||
return state; |
|
||||||
} |
|
||||||
default: |
|
||||||
throw new Error("Unimplemented ManageImportExportWindow state update.") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default function ManageImportExportWindow(props: {}) { |
|
||||||
const [state, dispatch] = useReducer(ManageImportExportWindowReducer, { |
|
||||||
dummy: true, |
|
||||||
}); |
|
||||||
|
|
||||||
return <ManageImportExportWindowControlled state={state} dispatch={dispatch} /> |
|
||||||
} |
|
||||||
|
|
||||||
export function ManageImportExportWindowControlled(props: { |
|
||||||
state: ManageImportExportWindowState, |
|
||||||
dispatch: (action: any) => void, |
|
||||||
}) { |
|
||||||
return <> |
|
||||||
<Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
|
||||||
<Box |
|
||||||
m={1} |
|
||||||
mt={4} |
|
||||||
width="80%" |
|
||||||
> |
|
||||||
<SaveIcon style={{ fontSize: 80 }} /> |
|
||||||
</Box> |
|
||||||
<Box |
|
||||||
m={1} |
|
||||||
mt={4} |
|
||||||
width="80%" |
|
||||||
> |
|
||||||
<Typography variant="h4">Import / Export</Typography> |
|
||||||
<Box mt={2} /> |
|
||||||
<Typography> |
|
||||||
An exported database contains all your artists, albums, tracks and tags.<br /> |
|
||||||
It is represented as a JSON structure. |
|
||||||
</Typography> |
|
||||||
<Box mt={2} /> |
|
||||||
<Alert severity="warning">Upon importing a previously exported database, your database will be completely replaced!</Alert> |
|
||||||
<Box mt={2} /> |
|
||||||
<a href={(process.env.REACT_APP_BACKEND || "") + serverApi.DBExportEndpoint}> |
|
||||||
<Button variant="outlined">Export</Button> |
|
||||||
</a> |
|
||||||
</Box> |
|
||||||
</Box> |
|
||||||
</> |
|
||||||
} |
|
@ -0,0 +1,32 @@ |
|||||||
|
import { DBExportEndpoint, DBImportEndpoint, DBImportRequest, DBWipeEndpoint } from "../../api/api"; |
||||||
|
import backendRequest from "./request"; |
||||||
|
|
||||||
|
export function getDBExportLink() { |
||||||
|
return (process.env.REACT_APP_BACKEND || "") + DBExportEndpoint; |
||||||
|
} |
||||||
|
|
||||||
|
export async function wipeDB() { |
||||||
|
const requestOpts = { |
||||||
|
method: 'POST' |
||||||
|
}; |
||||||
|
|
||||||
|
const response = await backendRequest((process.env.REACT_APP_BACKEND || "") + DBWipeEndpoint, requestOpts) |
||||||
|
if (!response.ok) { |
||||||
|
throw new Error("Response to DB wipe not OK: " + JSON.stringify(response)); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
export async function importDB(jsonData: DBImportRequest) { |
||||||
|
const requestOpts = { |
||||||
|
method: 'POST', |
||||||
|
headers: { 'Content-Type': 'application/json' }, |
||||||
|
body: JSON.stringify(jsonData), |
||||||
|
}; |
||||||
|
|
||||||
|
const response = await backendRequest((process.env.REACT_APP_BACKEND || "") + DBImportEndpoint, requestOpts) |
||||||
|
if (!response.ok) { |
||||||
|
throw new Error("Response to DB import not OK: " + JSON.stringify(response)); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue