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