Skip to content

Commit

Permalink
add private media library for integrations
Browse files Browse the repository at this point in the history
  • Loading branch information
erquhart committed Nov 19, 2017
1 parent d9905b4 commit 9569f18
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 38 deletions.
48 changes: 28 additions & 20 deletions src/actions/mediaLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function insertMedia(mediaPath) {
}

export function loadMedia(opts = {}) {
const { delay = 0, query = '', page = 1 } = opts;
const { delay = 0, query = '', page = 1, privateUpload } = opts;
return async (dispatch, getState) => {
const state = getState();
const backend = currentBackend(state.config);
Expand All @@ -42,17 +42,18 @@ export function loadMedia(opts = {}) {
const provider = getIntegrationProvider(state.integrations, backend.getToken, integration);
dispatch(mediaLoading(page));
try {
const files = await provider.retrieve(query, page);
const files = await provider.retrieve(query, page, privateUpload);
const mediaLoadedOpts = {
page,
canPaginate: true,
dynamicSearch: true,
dynamicSearchQuery: query
dynamicSearchQuery: query,
privateUpload,
};
return dispatch(mediaLoaded(files, mediaLoadedOpts));
}
catch(error) {
return dispatch(mediaLoadFailed());
return dispatch(mediaLoadFailed({ privateUpload }));
}
}
dispatch(mediaLoading(page));
Expand All @@ -66,7 +67,8 @@ export function loadMedia(opts = {}) {
};
}

export function persistMedia(file, privateUpload) {
export function persistMedia(file, opts = {}) {
const { privateUpload } = opts;
return async (dispatch, getState) => {
const state = getState();
const backend = currentBackend(state.config);
Expand All @@ -81,7 +83,7 @@ export function persistMedia(file, privateUpload) {
const asset = await backend.persistMedia(assetProxy);
return dispatch(mediaPersisted(asset));
}
return dispatch(mediaPersisted(assetProxy.asset));
return dispatch(mediaPersisted(assetProxy.asset, { privateUpload }));
}
catch(error) {
console.error(error);
Expand All @@ -90,12 +92,13 @@ export function persistMedia(file, privateUpload) {
kind: 'danger',
dismissAfter: 8000,
}));
return dispatch(mediaPersistFailed());
return dispatch(mediaPersistFailed({ privateUpload }));
}
};
}

export function deleteMedia(file) {
export function deleteMedia(file, opts = {}) {
const { privateUpload } = opts;
return (dispatch, getState) => {
const state = getState();
const backend = currentBackend(state.config);
Expand All @@ -105,7 +108,7 @@ export function deleteMedia(file) {
dispatch(mediaDeleting());
return provider.delete(file.id)
.then(() => {
return dispatch(mediaDeleted(file));
return dispatch(mediaDeleted(file, { privateUpload }));
})
.catch(error => {
console.error(error);
Expand All @@ -114,7 +117,7 @@ export function deleteMedia(file) {
kind: 'danger',
dismissAfter: 8000,
}));
return dispatch(mediaDeleteFailed());
return dispatch(mediaDeleteFailed({ privateUpload }));
});
}
dispatch(mediaDeleting());
Expand Down Expand Up @@ -148,36 +151,41 @@ export function mediaLoaded(files, opts = {}) {
};
}

export function mediaLoadFailed(error) {
return { type: MEDIA_LOAD_FAILURE };
export function mediaLoadFailed(error, opts = {}) {
const { privateUpload } = opts;
return { type: MEDIA_LOAD_FAILURE, payload: { privateUpload } };
}

export function mediaPersisting() {
return { type: MEDIA_PERSIST_REQUEST };
}

export function mediaPersisted(asset) {
export function mediaPersisted(asset, opts = {}) {
const { privateUpload } = opts;
return {
type: MEDIA_PERSIST_SUCCESS,
payload: { file: asset },
payload: { file: asset, privateUpload },
};
}

export function mediaPersistFailed(error) {
return { type: MEDIA_PERSIST_FAILURE };
export function mediaPersistFailed(error, opts = {}) {
const { privateUpload } = opts;
return { type: MEDIA_PERSIST_FAILURE, payload: { privateUpload } };
}

export function mediaDeleting() {
return { type: MEDIA_DELETE_REQUEST };
}

export function mediaDeleted(file) {
export function mediaDeleted(file, opts = {}) {
const { privateUpload } = opts;
return {
type: MEDIA_DELETE_SUCCESS,
payload: { file },
payload: { file, privateUpload },
};
}

export function mediaDeleteFailed(error) {
return { type: MEDIA_DELETE_FAILURE };
export function mediaDeleteFailed(error, opts = {}) {
const { privateUpload } = opts;
return { type: MEDIA_DELETE_FAILURE, payload: { privateUpload } };
}
22 changes: 22 additions & 0 deletions src/components/MediaLibrary/MediaLibrary.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,25 @@
overflow-wrap: break-word;
line-height: 1.3 !important;
}

.nc-mediaLibrary-dialogPrivate {
background-color: var(--backgroundAltColor);

& .nc-mediaLibrary-title,
& .nc-mediaLibrary-emptyMessage,
& .nc-mediaLibrary-paginatingMessage,
& h1 {
color: var(--textFieldBorderColor);
}

& .nc-mediaLibrary-card,
& .nc-mediaLibrary-searchInput {
background-color: var(--textFieldBorderColor);
}

& button:disabled,
& label[disabled] {
background-color: rgba(217, 217, 217, 0.15);
}
}

29 changes: 19 additions & 10 deletions src/components/MediaLibrary/MediaLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ class MediaLibrary extends React.Component {
if (isOpening) {
this.setState({ selectedFile: {}, query: '' });
}

if (isOpening && (this.props.privateUpload !== nextProps.privateUpload)) {
this.props.loadMedia({ privateUpload: nextProps.privateUpload });
}
}

/**
Expand Down Expand Up @@ -111,7 +115,7 @@ class MediaLibrary extends React.Component {
*/
event.stopPropagation();
event.preventDefault();
const { loadMedia, persistMedia, privateUpload } = this.props;
const { persistMedia, privateUpload } = this.props;
const { files: fileList } = event.dataTransfer || event.target;
const files = [...fileList];
const file = files[0];
Expand All @@ -121,7 +125,7 @@ class MediaLibrary extends React.Component {
* improved in the future, but isn't currently resulting in noticeable
* performance/load time issues.
*/
await persistMedia(file, privateUpload);
await persistMedia(file, { privateUpload });
this.scrollToTop();
};

Expand All @@ -143,20 +147,20 @@ class MediaLibrary extends React.Component {
*/
handleDelete = () => {
const { selectedFile } = this.state;
const { files, deleteMedia } = this.props;
const { files, deleteMedia, privateUpload } = this.props;
if (!window.confirm('Are you sure you want to delete selected media?')) {
return;
}
const file = files.find(file => selectedFile.key === file.key);
deleteMedia(file)
deleteMedia(file, { privateUpload })
.then(() => {
this.setState({ selectedFile: {} });
});
};

handleLoadMore = () => {
const { loadMedia, dynamicSearchQuery, page } = this.props;
loadMedia({ query: dynamicSearchQuery, page: page + 1 });
const { loadMedia, dynamicSearchQuery, page, privateUpload } = this.props;
loadMedia({ query: dynamicSearchQuery, page: page + 1, privateUpload });
};

/**
Expand All @@ -167,8 +171,9 @@ class MediaLibrary extends React.Component {
* so this handler has no impact.
*/
handleSearchKeyDown = async (event) => {
if (event.key === 'Enter' && this.props.dynamicSearch) {
await this.props.loadMedia({ query: this.state.query })
const { dynamicSearch, loadMedia, privateUpload } = this.props;
if (event.key === 'Enter' && dynamicSearch) {
await loadMedia({ query: this.state.query, privateUpload })
this.scrollToTop();
}
};
Expand Down Expand Up @@ -216,6 +221,7 @@ class MediaLibrary extends React.Component {
hasNextPage,
page,
isPaginating,
privateUpload,
} = this.props;
const { query, selectedFile } = this.state;
const filteredFiles = forImage ? this.filterImages(files) : files;
Expand All @@ -236,7 +242,7 @@ class MediaLibrary extends React.Component {
<Dialog
isVisible={isVisible}
onClose={this.handleClose}
className="nc-mediaLibrary-dialog"
className={c('nc-mediaLibrary-dialog', { 'nc-mediaLibrary-dialogPrivate': privateUpload })}
footer={
<MediaLibraryFooter
onDelete={this.handleDelete}
Expand All @@ -251,7 +257,10 @@ class MediaLibrary extends React.Component {
/>
}
>
<h1 className="nc-mediaLibrary-title">{forImage ? 'Images' : 'Assets'}</h1>
<h1 className="nc-mediaLibrary-title">
{privateUpload ? 'Private ' : null}
{forImage ? 'Images' : 'Assets'}
</h1>
<input
className="nc-mediaLibrary-searchInput"
value={query}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Widgets/FileControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default class FileControl extends React.Component {

handleClick = (e) => {
const { field, onOpenMediaLibrary } = this.props;
return onOpenMediaLibrary({ controlID: this.controlID, privateUpload: field.private });
return onOpenMediaLibrary({ controlID: this.controlID, privateUpload: field.get('private') });
};

renderFileName = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Widgets/ImageControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class ImageControl extends React.Component {

handleClick = (e) => {
const { field, onOpenMediaLibrary } = this.props;
return onOpenMediaLibrary({ controlID: this.controlID, forImage: true, privateUpload: field.private });
return onOpenMediaLibrary({ controlID: this.controlID, forImage: true, privateUpload: field.get('private') });
};

renderFileName = () => {
Expand Down
8 changes: 4 additions & 4 deletions src/integrations/providers/assetStore/implementation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pickBy } from 'lodash';
import { pickBy, trimEnd } from 'lodash';
import { addParams } from '../../../lib/urlHelper';

export default class AssetStore {
Expand All @@ -10,7 +10,7 @@ export default class AssetStore {
this.getToken = getToken;

this.shouldConfirmUpload = config.get('shouldConfirmUpload', false);
this.getSignedFormURL = config.get('getSignedFormURL');
this.getSignedFormURL = trimEnd(config.get('getSignedFormURL'), '/');
}

parseJsonResponse(response) {
Expand Down Expand Up @@ -65,8 +65,8 @@ export default class AssetStore {
return content;
}

async retrieve(query, page) {
const params = pickBy({ search: query, page }, val => !!val);
async retrieve(query, page, privateUpload) {
const params = pickBy({ search: query, page, filter: privateUpload ? 'private' : 'public' }, val => !!val);
const url = addParams(this.getSignedFormURL, params);
const token = await this.getToken();
const headers = {
Expand Down
36 changes: 34 additions & 2 deletions src/reducers/mediaLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@ import {
} from '../actions/mediaLibrary';

const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), action) => {
const privateUploadChanged = state.get('privateUpload') !== get(action, ['payload', 'privateUpload']);
switch (action.type) {
case MEDIA_LIBRARY_OPEN: {
const { controlID, forImage } = action.payload || {};
const { controlID, forImage, privateUpload } = action.payload || {};
if (privateUploadChanged) {
return Map({
isVisible: true,
forImage,
controlID,
canInsert: !!controlID,
privateUpload,
controlMedia: Map(),
});
}
return state.withMutations(map => {
map.set('isVisible', true);
map.set('forImage', forImage);
map.set('controlID', controlID);
map.set('canInsert', !!controlID);
map.set('privateUpload', privateUpload);
});
}
case MEDIA_LIBRARY_CLOSE:
Expand All @@ -40,7 +52,12 @@ const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), ac
map.set('isPaginating', action.payload.page > 1);
});
case MEDIA_LOAD_SUCCESS: {
const { files = [], page, canPaginate, dynamicSearch, dynamicSearchQuery } = action.payload;
const { files = [], page, canPaginate, dynamicSearch, dynamicSearchQuery, privateUpload } = action.payload;

if (privateUploadChanged) {
return state;
}

const filesWithKeys = files.map(file => ({ ...file, key: uuid() }));
return state.withMutations(map => {
map.set('isLoading', false);
Expand All @@ -59,11 +76,17 @@ const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), ac
});
}
case MEDIA_LOAD_FAILURE:
if (privateUploadChanged) {
return state;
}
return state.set('isLoading', false);
case MEDIA_PERSIST_REQUEST:
return state.set('isPersisting', true);
case MEDIA_PERSIST_SUCCESS: {
const { file } = action.payload;
if (privateUploadChanged) {
return state;
}
return state.withMutations(map => {
const fileWithKey = { ...file, key: uuid() };
const updatedFiles = [fileWithKey, ...map.get('files')];
Expand All @@ -72,18 +95,27 @@ const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), ac
});
}
case MEDIA_PERSIST_FAILURE:
if (privateUploadChanged) {
return state;
}
return state.set('isPersisting', false);
case MEDIA_DELETE_REQUEST:
return state.set('isDeleting', true);
case MEDIA_DELETE_SUCCESS: {
const { key } = action.payload.file;
if (privateUploadChanged) {
return state;
}
return state.withMutations(map => {
const updatedFiles = map.get('files').filter(file => file.key !== key);
map.set('files', updatedFiles);
map.set('isDeleting', false);
});
}
case MEDIA_DELETE_FAILURE:
if (privateUploadChanged) {
return state;
}
return state.set('isDeleting', false);
default:
return state;
Expand Down

0 comments on commit 9569f18

Please sign in to comment.