Allow only images in modal image uploader. #7672

Merged
4 changed files with 63 additions and 8 deletions
Showing only changes of commit f6a941969f - Show all commits

View file

@ -299,6 +299,32 @@ app.on('before-quit', () => {
appState.isQuitting = true; appState.isQuitting = true;
}); });
// Get the content of a file as a raw buffer of bytes.
// Useful to convert a file path to a File instance.
// Example:
// const result = await ipcMain.invoke('get-file-from-path', 'path/to/file');
// const file = new File([result.buffer], result.name);
ipcMain.handle('get-file-from-path', (event, path) => {
return new Promise((resolve, reject) => {
// Encoding null ensures data results in a Buffer.
fs.readFile(path, { encoding: null }, (err, data) => {
if (err) {
reject(err);
return;
}
// Separate folders considering "\" and "/"
// as separators (cross platform)
const folders = path.split(/[\\/]/);
const fileName = folders[folders.length - 1];
resolve({
name: fileName,
path: path,
buffer: data,
});
});
});
});
ipcMain.on('get-disk-space', async (event) => { ipcMain.on('get-disk-space', async (event) => {
try { try {
const { data_dir } = await Lbry.settings_get(); const { data_dir } = await Lbry.settings_get();

View file

@ -1,6 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import * as remote from '@electron/remote'; import * as remote from '@electron/remote';
import { ipcRenderer } from 'electron';
import Button from 'component/button'; import Button from 'component/button';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
@ -14,6 +15,7 @@ type Props = {
error?: string, error?: string,
disabled?: boolean, disabled?: boolean,
autoFocus?: boolean, autoFocus?: boolean,
filters?: Array<{ name: string, extension: string[] }>,
}; };
class FileSelector extends React.PureComponent<Props> { class FileSelector extends React.PureComponent<Props> {
@ -64,13 +66,26 @@ class FileSelector extends React.PureComponent<Props> {
properties = ['openDirectory']; properties = ['openDirectory'];
} }
remote.dialog.showOpenDialog({ properties, defaultPath }).then((result) => { remote.dialog
const path = result && result.filePaths[0]; .showOpenDialog({
if (path) { properties,
// $FlowFixMe defaultPath,
this.props.onFileChosen({ path }); filters: this.props.filters,
} })
}); .then((result) => {
const path = result && result.filePaths[0];
if (path) {
return ipcRenderer.invoke('get-file-from-path', path);
}
return undefined;
})
.then((result) => {
if (!result) {
return;
}
const file = new File([result.buffer], result.name);
this.props.onFileChosen(file);
});
}; };
fileInputButton = () => { fileInputButton = () => {

View file

@ -27,6 +27,14 @@ type Props = {
// passed to the onUpdate function after the // passed to the onUpdate function after the
// upload service returns success. // upload service returns success.
buildImagePreview?: boolean, buildImagePreview?: boolean,
// File extension filtering. Files can be filtered
// but the "All Files" options always shows up. To
// avoid that, you can use the filters property.
// For example, to only accept images pass the
// following filter:
// { name: 'Images', extensions: ['jpg', 'png', 'gif'] },
filters?: Array<{ name: string, extension: string[] }>,
type?: string,
}; };
function filePreview(file) { function filePreview(file) {
@ -43,7 +51,8 @@ function filePreview(file) {
} }
function SelectAsset(props: Props) { function SelectAsset(props: Props) {
const { onUpdate, onDone, assetName, currentValue, recommended, title, inline, buildImagePreview } = props; const { onUpdate, onDone, assetName, currentValue, recommended, title, inline, buildImagePreview, filters, type } =
props;
const [pathSelected, setPathSelected] = React.useState(''); const [pathSelected, setPathSelected] = React.useState('');
const [fileSelected, setFileSelected] = React.useState<any>(null); const [fileSelected, setFileSelected] = React.useState<any>(null);
const [uploadStatus, setUploadStatus] = React.useState(SPEECH_READY); const [uploadStatus, setUploadStatus] = React.useState(SPEECH_READY);
@ -121,6 +130,8 @@ function SelectAsset(props: Props) {
/> />
) : ( ) : (
<FileSelector <FileSelector
filters={filters}
type={type}
autoFocus autoFocus
disabled={uploadStatus === SPEECH_UPLOADING} disabled={uploadStatus === SPEECH_UPLOADING}
label={fileSelectorLabel} label={fileSelectorLabel}

View file

@ -14,10 +14,13 @@ type Props = {
function ModalImageUpload(props: Props) { function ModalImageUpload(props: Props) {
const { closeModal, currentValue, title, assetName, helpText, onUpdate } = props; const { closeModal, currentValue, title, assetName, helpText, onUpdate } = props;
const filters = React.useMemo(() => [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]);
return ( return (
<Modal isOpen type="card" onAborted={closeModal} contentLabel={title}> <Modal isOpen type="card" onAborted={closeModal} contentLabel={title}>
<SelectAsset <SelectAsset
filters={filters}
type="openFile"
onUpdate={onUpdate} onUpdate={onUpdate}
currentValue={currentValue} currentValue={currentValue}
assetName={assetName} assetName={assetName}