Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add private media library for integrations #834

Merged
merged 1 commit into from
Nov 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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