superdesk/superdesk-client-core

View on GitHub
scripts/extensions/sams/src/components/assets/uploadAssetModal.tsx

Summary

Maintainability
A
1 hr
Test Coverage
// External modules
import * as React from 'react';
import {connect} from 'react-redux';

// Types
import {ASSET_STATE, IAssetItem, IUploadAssetModalProps} from '../../interfaces';
import {IApplicationState} from '../../store';
import {samsApi, superdeskApi} from '../../apis';

// Redux Actions & Selectors
import {getActiveSets} from '../../store/sets/selectors';

// UI
import {
    FileUploadModal,
    IContentPanelProps,
    IUploadFileListItemProps,
    IUploadItem,
} from '../../containers/FileUploadModal';
import {AssetGridItem} from './assetGridItem';
import {AssetEditor} from './assetEditor';

interface IState {
    assets: Dictionary<string, Partial<IAssetItem>>;
}

const mapStateToProps = (state: IApplicationState) => ({
    sets: getActiveSets(state),
});

export class UploadAssetModalComponent extends React.Component<IUploadAssetModalProps, IState> {
    onFieldChanged: Dictionary<string, (field: keyof IAssetItem, value: any) => void>;

    constructor(props: IUploadAssetModalProps) {
        super(props);

        this.onFieldChanged = {};
        this.state = {
            assets: this.getInitialAssets(),
        };

        this.onFileAdded = this.onFileAdded.bind(this);
        this.onFileRemoved = this.onFileRemoved.bind(this);
        this.uploadFile = this.uploadFile.bind(this);
        this.renderGridItem = this.renderGridItem.bind(this);
        this.renderRightPanel = this.renderRightPanel.bind(this);
        this.closeModal = this.closeModal.bind(this);
    }

    getInitialAssets(): Dictionary<string, Partial<IAssetItem>> {
        const assets: Dictionary<string, Partial<IAssetItem>> = {};

        if (this.props.initialFiles != null && this.props.initialFiles?.length > 0) {
            this.props.initialFiles.forEach(
                (item) => {
                    assets[item.id] = {
                        _id: item.id,
                        state: this.props.defaultAssetState ?? ASSET_STATE.DRAFT,
                        filename: item.file.name,
                        length: item.file.size,
                        mimetype: item.file.type,
                        name: item.file.name,
                        description: '',
                        set_id: this.props.sets[0]._id,
                    };
                    this.onFieldChanged[item.id] = this.onChange.bind(this, item.id);
                },
            );
        }

        return assets;
    }

    closeModal() {
        if (this.props.onModalClosed != null) {
            this.props.onModalClosed();
        }

        this.props.closeModal();
    }

    onFileAdded(id: string, file: File) {
        this.setState((state: IState) => ({
            assets: {
                ...state.assets,
                [id]: {
                    _id: id,
                    state: this.props.defaultAssetState ?? ASSET_STATE.DRAFT,
                    filename: file.name,
                    length: file.size,
                    mimetype: file.type,
                    name: file.name,
                    description: '',
                    set_id: this.props.sets[0]._id,
                },
            },
        }));

        this.onFieldChanged[id] = this.onChange.bind(this, id);
    }

    onFileRemoved(id: string) {
        this.setState((state: IState) => {
            const assets: IState['assets'] = {...state.assets};

            delete assets[id];

            return {assets: assets};
        });

        delete this.onFieldChanged[id];
    }

    uploadFile(item: IUploadItem, onProgress: (event: ProgressEvent) => void): Promise<Partial<IAssetItem>> {
        const {gettext} = superdeskApi.localization;
        const {notify} = superdeskApi.ui;

        const data = new FormData();
        const asset = this.state.assets?.[item.id];

        if (asset == null) {
            notify.error(gettext('Unable to find Asset associated with the file!'));
            return Promise.reject();
        }

        data.append('binary', item.binary, asset.filename);
        let field: keyof IAssetItem;

        for (field in asset) {
            if (['_id', 'length'].includes(field) === false) {
                if (typeof asset[field] === 'object') {
                    data.append(field, JSON.stringify(asset[field]));
                } else {
                    data.append(field, asset[field] as string);
                }
            }
        }

        return samsApi.assets.upload(data, onProgress)
            .then((newAsset) => {
                if (this.props.onAssetUploaded != null) {
                    return this.props.onAssetUploaded(newAsset)
                        .then(() => newAsset);
                }

                return newAsset;
            });
    }

    onChange(id: string, field: keyof IAssetItem, value: any) {
        this.setState((state: IState) => {
            const assets: IState['assets'] = {...state.assets};

            assets[id] = {
                ...assets[id],
                [field]: value,
            };

            return {assets: assets};
        });
    }

    renderGridItem({item, asset, selected, selectFile, removeFile}: IUploadFileListItemProps<Partial<IAssetItem>>) {
        return (
            <AssetGridItem
                asset={asset}
                file={item.binary}
                onClick={selectFile}
                onDoubleClick={selectFile}
                selected={selected}
                remove={removeFile}
                uploadProgress={item.uploadProgress}
                error={item.error}
            />
        );
    }

    renderRightPanel({item, submitting}: IContentPanelProps) {
        return (
            <AssetEditor
                key={item.id}
                asset={this.state.assets[item.id]}
                disabled={submitting}
                onChange={this.onFieldChanged[item.id]}
                allowedStates={this.props.allowedStates}
            />
        );
    }

    render() {
        const {gettext} = superdeskApi.localization;

        return (
            <FileUploadModal
                theme="dark-ui"
                modalSize="fill"
                initialFiles={this.props.initialFiles}
                multiple={true}
                closeModal={this.closeModal}
                title={gettext('Upload New Asset(s)')}
                onFileAdded={this.onFileAdded}
                onFileRemoved={this.onFileRemoved}
                uploadFile={this.uploadFile}
                assets={this.state.assets}
                ListItemComponent={this.renderGridItem}
                RightPanelComponent={this.renderRightPanel}
            />
        );
    }
}

export const UploadAssetModal = connect(mapStateToProps)(UploadAssetModalComponent);