Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5753 from om-chauhan1/RefactorReactClassComponent…
…sStream16 Converted upload_viewer.jsx to functional component and used i18n for string literals
- Loading branch information
Showing
2 changed files
with
150 additions
and
158 deletions.
There are no files selected for viewing
296 changes: 138 additions & 158 deletions
296
app/assets/javascripts/components/uploads/upload_viewer.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,181 +1,161 @@ | ||
import React from 'react'; | ||
import React, { useEffect, useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import createReactClass from 'create-react-class'; | ||
import { forEach, get } from 'lodash-es'; | ||
import OnClickOutside from 'react-onclickoutside'; | ||
import { connect } from 'react-redux'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { setUploadViewerMetadata, setUploadPageViews, resetUploadsViews } from '../../actions/uploads_actions.js'; | ||
import { formatDateWithoutTime } from '../../utils/date_utils.js'; | ||
import { get } from 'lodash-es'; | ||
import useOutsideClick from '../../hooks/useOutsideClick.js'; | ||
|
||
const UploadViewer = createReactClass({ | ||
displayName: 'UploadViewer', | ||
const UploadViewer = ({ closeUploadViewer, upload, imageFile }) => { | ||
const dispatch = useDispatch(); | ||
const uploadMetadata = useSelector(state => state.uploads.uploadMetadata); | ||
const pageViews = useSelector(state => state.uploads.averageViews); | ||
|
||
propTypes: { | ||
upload: PropTypes.object, | ||
closeUploadViewer: PropTypes.func, | ||
}, | ||
const [loadingViews, setLoadingViews] = useState(true); | ||
|
||
getInitialState() { | ||
return { | ||
loadingViews: true | ||
useEffect(() => { | ||
dispatch(setUploadViewerMetadata(upload)); | ||
return () => { | ||
dispatch(resetUploadsViews()); | ||
}; | ||
}, | ||
}, [upload]); | ||
|
||
componentDidMount() { | ||
this.props.setUploadViewerMetadata(this.props.upload); | ||
}, | ||
|
||
componentDidUpdate() { | ||
const metadata = get(this.props.uploadMetadata, `query.pages[${this.props.upload.id}]`); | ||
useEffect(() => { | ||
const metadata = get(uploadMetadata, `query.pages[${upload.id}]`); | ||
const fileUsage = get(metadata, 'globalusage', []); | ||
if (fileUsage) { | ||
if (this.state.loadingViews) { | ||
this.handleGetFileViews(fileUsage); | ||
} | ||
if (fileUsage && loadingViews) { | ||
handleGetFileViews(fileUsage); | ||
} | ||
}, | ||
|
||
componentWillUnmount() { | ||
this.props.resetUploadsViews(); | ||
}, | ||
|
||
handleGetFileViews(files) { | ||
this.props.setUploadPageViews(files); | ||
this.setState({ | ||
loadingViews: false | ||
}, [uploadMetadata, upload.id, loadingViews]); | ||
|
||
const handleGetFileViews = (files) => { | ||
dispatch(setUploadPageViews(files)); | ||
setLoadingViews(false); | ||
}; | ||
|
||
const handleClickOutside = () => { | ||
closeUploadViewer(); | ||
}; | ||
const ref = useOutsideClick(handleClickOutside); | ||
|
||
const metadata = get(uploadMetadata, `query.pages[${upload.id}]`); | ||
const imageDescription = get(metadata, 'imageinfo[0].extmetadata.ImageDescription.value'); | ||
const width = get(metadata, 'imageinfo[0].width'); | ||
const height = get(metadata, 'imageinfo[0].height'); | ||
|
||
let size = get(metadata, 'imageinfo[0].size'); | ||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; | ||
const i = Math.floor(Math.log(size) / Math.log(1024)); | ||
size = `${parseFloat((size / (1024 ** i)).toFixed(2))} ${sizes[i]}`; | ||
|
||
const imageUrl = get(metadata, 'imageinfo[0].url'); | ||
|
||
const profileLink = `/users/${encodeURIComponent(upload.uploader)}`; | ||
const author = <a href={profileLink} target="_blank">{upload.uploader}</a>; | ||
const source = get(metadata, 'imageinfo[0].extmetadata.Credit.value'); | ||
const license = get(metadata, 'imageinfo[0].extmetadata.LicenseShortName.value'); | ||
const globalUsage = get(metadata, 'globalusage', []); | ||
let usageTableElements; | ||
if (globalUsage && pageViews !== undefined) { | ||
usageTableElements = globalUsage.map((usage, index) => { | ||
return ( | ||
<tr className="view-file-details" key={usage.url}> | ||
<td className="row-details">{usage.wiki} </td> | ||
<td className="row-details"><a href={usage.url}>{usage.title}</a> </td> | ||
<td className="text-right row-details">{pageViews[index]}</td> | ||
</tr> | ||
); | ||
}); | ||
}, | ||
|
||
handleClickOutside() { | ||
this.props.closeUploadViewer(); | ||
}, | ||
|
||
|
||
render() { | ||
const metadata = get(this.props.uploadMetadata, `query.pages[${this.props.upload.id}]`); | ||
const imageDescription = get(metadata, 'imageinfo[0].extmetadata.ImageDescription.value'); | ||
const width = get(metadata, 'imageinfo[0].width'); | ||
const height = get(metadata, 'imageinfo[0].height'); | ||
|
||
let size = get(metadata, 'imageinfo[0].size'); | ||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; | ||
const i = Math.floor(Math.log(size) / Math.log(1024)); | ||
size = `${parseFloat((size / (1024 ** i)).toFixed(2))} ${sizes[i]}`; | ||
|
||
const imageUrl = get(metadata, 'imageinfo[0].url'); | ||
} | ||
|
||
const profileLink = `/users/${encodeURIComponent(this.props.upload.uploader)}`; | ||
const author = <a href={profileLink} target="_blank">{this.props.upload.uploader}</a>; | ||
const source = get(metadata, 'imageinfo[0].extmetadata.Credit.value'); | ||
const license = get(metadata, 'imageinfo[0].extmetadata.LicenseShortName.value'); | ||
const globalUsage = get(metadata, 'globalusage', []); | ||
let usageTableElements; | ||
if (globalUsage && (this.props.pageViews !== undefined)) { | ||
usageTableElements = globalUsage.map((usage, index) => { | ||
return ( | ||
<tr className="view-file-details" key={usage.url}> | ||
<td className="row-details">{usage.wiki} </td> | ||
<td className="row-details"><a href={usage.url}>{usage.title}</a> </td> | ||
<td className="text-right row-details">{this.props.pageViews[index]}</td> | ||
let fileUsageTable; | ||
if (globalUsage.length > 0) { | ||
fileUsageTable = ( | ||
<div> | ||
<h1>{'\n'}</h1> | ||
<h4>{I18n.t('uploads.file_usage')}</h4> | ||
<table border="1"> | ||
<thead> | ||
<tr> | ||
<th>{I18n.t('uploads.wiki_big')}</th> | ||
<th>{I18n.t('uploads.article_name')}</th> | ||
<th>{I18n.t('uploads.views_per_day')}</th> | ||
</tr> | ||
); | ||
}); | ||
} | ||
</thead> | ||
<tbody> | ||
{usageTableElements} | ||
</tbody> | ||
</table> | ||
</div> | ||
); | ||
} | ||
let categoriesList = []; | ||
let categories; | ||
(metadata?.categories ?? []).forEach((category) => { | ||
categoriesList.push(<span key={`span-${category.title}`}> | </span>); | ||
categoriesList.push(<a href={`https://commons.wikimedia.org/wiki/${category.title}`} target="_blank" key={`link-${category.title}`}>{category.title.slice('Category:'.length)}</a>); | ||
}); | ||
if (categoriesList.length > 0) { | ||
categoriesList = categoriesList.splice(1); | ||
categories = ( | ||
<div> | ||
<h1>{'\n'}</h1> | ||
<h4>{I18n.t('uploads.categories')}</h4> | ||
{categoriesList} | ||
</div> | ||
); | ||
} | ||
|
||
let fileUsageTable; | ||
if (globalUsage.length > 0) { | ||
fileUsageTable = ( | ||
<div> | ||
<h1>{'\n'}</h1> | ||
<h4>File usage on other wikis</h4> | ||
<table border="1"> | ||
<thead> | ||
return ( | ||
<div className="module upload-viewer" ref={ref}> | ||
<div className="modal-header"> | ||
<button className="pull-right icon-close" onClick={handleClickOutside} /> | ||
<h3>{upload.file_name}</h3> | ||
</div> | ||
<div className="modal-body"> | ||
<div className="left"> | ||
<a href={upload.url} target="_blank"><img alt={upload.file_name} src={imageFile} /></a> | ||
<p><a href={imageUrl} target="_blank">{I18n.t('uploads.original_file')}</a>{` (${width} X ${height} pixels, file size: ${size})`}</p> | ||
<h4>{I18n.t('uploads.description')}</h4> | ||
<p dangerouslySetInnerHTML={{ __html: imageDescription }} /> | ||
</div> | ||
<div className="right"> | ||
<table className="view-file-details"> | ||
<tbody> | ||
<tr> | ||
<th>Wiki</th> | ||
<th>Article Name</th> | ||
<th>Views per day</th> | ||
<td className="row-details bg-grey">{I18n.t('uploads.date')} </td> | ||
<td className="row-details">{formatDateWithoutTime(upload.uploaded_at)}</td> | ||
</tr> | ||
<tr> | ||
<td className="row-details bg-grey">{I18n.t('uploads.author')} </td> | ||
<td className="row-details">{author}</td> | ||
</tr> | ||
<tr> | ||
<td className="row-details bg-grey">{I18n.t('uploads.source')} </td> | ||
<td className="row-details" dangerouslySetInnerHTML={{ __html: source }} /> | ||
</tr> | ||
<tr> | ||
<td className="row-details bg-grey">{I18n.t('uploads.license')} </td> | ||
<td className="row-details">{license}</td> | ||
<td>{'\n'}</td> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{usageTableElements} | ||
</tbody> | ||
</table> | ||
</div> | ||
); | ||
} | ||
let categoriesList = []; | ||
let categories; | ||
forEach(get(metadata, 'categories', []), (category) => { | ||
categoriesList.push(<span key={`span-${category.title}`}> | </span>); | ||
categoriesList.push(<a href={`https://commons.wikimedia.org/wiki/${category.title}`} target="_blank" key={`link-${category.title}`}>{category.title.slice('Category:'.length)}</a>); | ||
}); | ||
if (categoriesList.length > 0) { | ||
categoriesList = categoriesList.splice(1); | ||
categories = ( | ||
<div> | ||
<h1>{'\n'}</h1> | ||
<h4>Categories</h4> | ||
{categoriesList} | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="module upload-viewer"> | ||
<div className="modal-header"> | ||
<button className="pull-right icon-close" onClick={this.props.closeUploadViewer} /> | ||
<h3>{this.props.upload.file_name}</h3> | ||
</div> | ||
<div className="modal-body"> | ||
<div className="left"> | ||
<a href={this.props.upload.url} target="_blank"><img alt={this.props.upload.file_name} src={this.props.imageFile} /></a> | ||
<p><a href={imageUrl} target="_blank">Original File</a>{` (${width} X ${height} pixels, file size: ${size})`}</p> | ||
<h4>Description</h4> | ||
<p dangerouslySetInnerHTML={{ __html: imageDescription }} /> | ||
</div> | ||
<div className="right"> | ||
<table className="view-file-details"> | ||
<tbody> | ||
<tr> | ||
<td className="row-details bg-grey">Date: </td> | ||
<td className="row-details">{formatDateWithoutTime(this.props.upload.uploaded_at)}</td> | ||
</tr> | ||
<tr> | ||
<td className="row-details bg-grey">Author: </td> | ||
<td className="row-details">{author}</td> | ||
</tr> | ||
<tr> | ||
<td className="row-details bg-grey">Source: </td> | ||
<td className="row-details" dangerouslySetInnerHTML={{ __html: source }} /> | ||
</tr> | ||
<tr> | ||
<td className="row-details bg-grey">License: </td> | ||
<td className="row-details">{license}</td> | ||
<td>{'\n'}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
{categories} | ||
{fileUsageTable} | ||
</div> | ||
</div> | ||
<div className="modal-footer"> | ||
<a className="button dark small pull-right upload-viewer-button" href={this.props.upload.url} target="_blank">View on Commons</a> | ||
{categories} | ||
{fileUsageTable} | ||
</div> | ||
</div> | ||
); | ||
} | ||
}); | ||
|
||
const mapStateToProps = state => ({ | ||
uploadMetadata: state.uploads.uploadMetadata, | ||
pageViews: state.uploads.averageViews | ||
}); | ||
<div className="modal-footer"> | ||
<a className="button dark small pull-right upload-viewer-button" href={upload.url} target="_blank">{I18n.t('uploads.view_commons')}</a> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
const mapDispatchToProps = { | ||
setUploadViewerMetadata, | ||
setUploadPageViews, | ||
resetUploadsViews | ||
UploadViewer.propTypes = { | ||
upload: PropTypes.object, | ||
closeUploadViewer: PropTypes.func, | ||
imageFile: PropTypes.string | ||
}; | ||
|
||
export default connect(mapStateToProps, mapDispatchToProps)(OnClickOutside(UploadViewer)); | ||
export default UploadViewer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters