diff --git a/src/Components/CollectionAuthor.js b/src/Components/CollectionAuthor.js index c81f227aadb22527f80b95f72cd226920a63379f..c6c299cf08bba817e4a14e684609084840c44f4b 100644 --- a/src/Components/CollectionAuthor.js +++ b/src/Components/CollectionAuthor.js @@ -29,17 +29,17 @@ export default function CollectionAuthor(props) { direction="column" justify="center" alignItems="center"> - {props.imgsrc ? + {props.imgsrc ? <UserLink to={`/usuario-publico/${props.author_id}`} - > - <UserAvatar src={props.imgsrc}/> + > + <UserAvatar src={props.imgsrc} /> </UserLink> : - <CircularProgress color="secondary"/> + <CircularProgress color="secondary" /> } <InfoText>Coleção organizada por:</InfoText> - {props.name ? + {props.name ? <UserLink to={`/usuario-publico/${props.author_id}`} > <UserName>{props.name}</UserName> </UserLink> diff --git a/src/Components/CollectionCommentSection.js b/src/Components/CollectionCommentSection.js index 0db277fe8a16bcb5c108bbb50ef4f2c39406b150..3b5b07b4e0c3cebe9ffc4b1303955dcaace7b9ce 100644 --- a/src/Components/CollectionCommentSection.js +++ b/src/Components/CollectionCommentSection.js @@ -16,7 +16,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ -import React, { useRef, useState, useEffect, Fragment, useContext } from 'react'; +import React, { useRef, useState, useEffect, Fragment } from 'react'; import { Grid } from '@material-ui/core'; import Card from '@material-ui/core/Card'; import Button from '@material-ui/core/Button'; @@ -33,11 +33,10 @@ import SignUpModal from './SignUpModal.js'; import LoginModal from './LoginModal.js'; import SnackBarComponent from './SnackbarComponent'; import CircularProgress from '@material-ui/core/CircularProgress'; -import { apiDomain } from '../env'; -import { Store } from '../Store' +import IconButton from '@material-ui/core/IconButton'; +import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; export default function CollectionCommentSection(props) { - const { state } = useContext(Store) const [post_snack_open, setPostSnackOpen] = useState(false); const [delete_snack_open, setDeleteSnackOpen] = useState(false); const [render_state, setRenderState] = useState(false); @@ -164,9 +163,22 @@ export default function CollectionCommentSection(props) { ); } + function handleSuccessGet(data, headers) { + setReviews((previousState) => previousState.concat(data)); + setIsLoading(false); + } + + function handleFailGet(error) { + console.log(error); setIsLoading(false) + } + useEffect(() => { setIsLoading(true) - getRequest(`/collections/${props.id}/reviews`, (data) => { setReviews(data); setIsLoading(false) }, (error) => { console.log(error); setIsLoading(false) }) + getRequest( + `/collections/${props.id}/reviews`, + handleSuccessGet, + (error) => { handleFailGet(error) } + ) }, [render_state]); return ( @@ -198,7 +210,7 @@ export default function CollectionCommentSection(props) { <Grid container style={{ paddingTop: "20px" }} spacing={1}> <Grid item xs={12} sm={2} style={{ paddingLeft: "15px", paddingRight: "15px" }}> <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}> - <Avatar src={apiDomain + state.currentUser.avatar} alt="user avatar" /> + <Avatar src={props.avatar} alt="user avatar" /> </div> </Grid> <Grid item xs={12} sm={10}> @@ -276,6 +288,17 @@ const ComentariosBox = styled.div` padding : 20px 0; border-bottom : 1px solid #f4f4f4; } + + .load-more{ + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + } + + .button{ + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); + } ` const Avatar = styled.img` diff --git a/src/Components/CollectionDowloadButton.js b/src/Components/CollectionDowloadButton.js index d7820da26c20262c585c60eff2723a2d5bc78b52..bda45946d26e9fb373ba79d5e006d4cacb5bb495 100644 --- a/src/Components/CollectionDowloadButton.js +++ b/src/Components/CollectionDowloadButton.js @@ -5,29 +5,62 @@ import Button from '@material-ui/core/Button'; import styled from 'styled-components'; import { apiUrl, apiDomain } from '../env'; import { getRequest } from './HelperFunctions/getAxiosConfig' +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert from '@material-ui/lab/Alert'; +function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; +} const DowloadButton = (props) => { const [download_url, setDownloadUrl] = useState(''); + const [snackInfo, setSnackInfo] = useState({ + open: false, + text: "", + severity: "", + }); useEffect(() => { - const body = { - "package": { - "object": [{ "type": "Collection", "id": props.id }] - } - }; - axios - .post(apiUrl + '/package', body) - .catch(err => { - if (err.response && err.response.status === 302) { - setDownloadUrl(apiDomain + '/' + err.response.data.url); + if (props.id && props.id !== "0") { + const body = { + "package": { + "object": [{ "type": "Collection", "id": props.id }] } - }); + }; + axios + .post(apiUrl + '/package', body) + .catch(err => { + if (err.response && err.response.status === 302) { + setDownloadUrl(apiDomain + '/' + err.response.data.url); + } + }); + } + }, [props.id]); - const handleDowloadCollection = () => { + const handleDowloadCollection = (e) => { // there is no error controller here because the router ///:type/:id/download is always returning error + e.preventDefault() + + let snackInfo = {} + if (download_url) { + window.open(download_url, '_blank') + snackInfo = { + open: true, + text: "A coleção está sendo baixada...", + severity: "success", + } + handleSnackInfo(snackInfo) + } + else { + snackInfo = { + open: true, + text: "Não foi possÃvel baixar a coleção", + severity: "warning", + } + handleSnackInfo(snackInfo) + } getRequest( `/collections/${props.id}/download`, (data, header) => { @@ -37,18 +70,40 @@ const DowloadButton = (props) => { ) } + const handleSnackInfo = (info) => { + setSnackInfo({ ...info }) + } + + const handleCloseSnack = () => { + const snackInfo = { + open: false, + text: "", + severity: "", + } + handleSnackInfo(snackInfo) + } + return ( <> - <DownloadAnchor href={download_url} > - <DownloadButton - variant="outlined" - color="primary" - startIcon={<GetAppIcon fontSize="large" />} - size="small" - > - <ButtonText onClick={handleDowloadCollection}>Baixar Coleção</ButtonText> - </DownloadButton> - </DownloadAnchor> + <Snackbar + open={snackInfo.open} + autoHideDuration={6000} + onClose={handleCloseSnack} + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + > + <Alert onClose={handleCloseSnack} severity={snackInfo.severity}> + {snackInfo.text} + </Alert> + </Snackbar> + <DownloadButton + variant="outlined" + color="primary" + startIcon={<GetAppIcon fontSize="large" />} + size="small" + onClick={handleDowloadCollection} + > + <ButtonText>Baixar Coleção</ButtonText> + </DownloadButton> </> ) } @@ -62,8 +117,5 @@ const DownloadButton = styled(Button)` padding-right: 10; width: 250px; ` -const DownloadAnchor = styled.a` - text-decoration: none !important; -` export default DowloadButton; diff --git a/src/Components/FloatingDownloadButton.js b/src/Components/FloatingDownloadButton.js index 96537210b046c1d0fc841ec4016e16c52e12c3c1..dcd9db430c7d96705bd4dc91e5295710a5642c21 100644 --- a/src/Components/FloatingDownloadButton.js +++ b/src/Components/FloatingDownloadButton.js @@ -16,54 +16,24 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ -import React, { useState } from 'react'; +import React from 'react'; import styled from 'styled-components'; import GetAppIcon from '@material-ui/icons/GetApp'; import Fab from '@material-ui/core/Fab'; -import Snackbar from '@material-ui/core/Snackbar'; -import MuiAlert from '@material-ui/lab/Alert'; - -function Alert(props) { - return <MuiAlert elevation={6} variant="filled" {...props} />; -} - -export default function FloatingDownloadButton (props) { - const [snackbar, setSnackbar] = useState(false); - - const handleClickDownload = () => { - if (props.empty_selection) - setSnackbar(true); - } - - const handleClose = () => { - setSnackbar(false); - } +export default function FloatingDownloadButton(props) { return ( - <div> - <DownloadAnchor href={props.url} alt="Baixar recursos selecionados"> - <FloatingDownload - color="primary" - aria-label="download" - onClick={handleClickDownload} - > - <GetAppIcon /> - </FloatingDownload> - </DownloadAnchor> - <Snackbar open={snackbar} autoHideDuration={6000} onClose={handleClose}> - <Alert onClose={handleClose} severity="alert"> - Selecione recursos para poder baixar - </Alert> - </Snackbar> - </div> + <FloatingDownload + color="primary" + aria-label="download" + onClick={props.handleDownloadSelection} + > + <GetAppIcon /> + </FloatingDownload> ); } -const DownloadAnchor=styled.a` - text-decoration: none !important; -` - -const FloatingDownload=styled(Fab)` +const FloatingDownload = styled(Fab)` position: fixed !important; right: 15px !important; bottom: 25px !important; diff --git a/src/Components/ResourceList.js b/src/Components/ResourceList.js index 4f2572f4fae05fe6ee995918d48a4584af4b0264..e32176c7166f62820cf88517c7f8f5dc8faf503c 100644 --- a/src/Components/ResourceList.js +++ b/src/Components/ResourceList.js @@ -41,7 +41,11 @@ export default function ResourceList(props) { null, { length: props.resources.length }).map(i => false)); const [selectable, setSelectable] = useState(false); const [download_url, setDownloadUrl] = useState(''); - const [snackbar_open, setSnackbarOpen] = useState(false); + const [snackInfo, setSnackInfo] = useState({ + open: false, + text: "", + severity: "", + }); const updateSelected = (index) => { let new_selected = selected.slice(); @@ -49,6 +53,10 @@ export default function ResourceList(props) { setSelected(new_selected); } + const handleSnackInfo = (info) => { + setSnackInfo({ ...info }) + } + const checkBoxIcon = (s) => { if (s) return <CheckBoxIcon />; @@ -56,9 +64,36 @@ export default function ResourceList(props) { return <CheckBoxOutlineBlankIcon />; } - const handleDownloadSelection = () => { + const handleDownloadSelection = (e) => { + e.preventDefault() // const selected_resources = props.resources.filter(resource => selected[props.resources.indexOf(resource)]); - setSnackbarOpen(true); + let snackInfo = {} + if (download_url) { + window.open(download_url, '_blank') + snackInfo = { + open: true, + text: "Os recursos estão sendo baixados...", + severity: "success", + } + handleSnackInfo(snackInfo) + } + else { + snackInfo = { + open: true, + text: "Selecione os recursos que deseja baixar", + severity: "warning", + } + handleSnackInfo(snackInfo) + } + } + + const handleCloseSnack = () => { + const snackInfo = { + open: false, + text: "", + severity: "", + } + handleSnackInfo(snackInfo) } useEffect(() => { @@ -103,16 +138,14 @@ export default function ResourceList(props) { </Button> </Grid> <Grid item> - <UnstyledAnchor href={download_url}> - <Button - color="primary" - variant="outlined" - startIcon={<GetAppIcon fontSize="large" />} - onClick={handleDownloadSelection} - > - <PanelButtonText>baixar seleção</PanelButtonText> - </Button> - </UnstyledAnchor> + <Button + color="primary" + variant="outlined" + startIcon={<GetAppIcon fontSize="large" />} + onClick={handleDownloadSelection} + > + <PanelButtonText>baixar seleção</PanelButtonText> + </Button> </Grid> </Grid> <Grid container direction="row" justify="center" alignItems="center"> @@ -151,16 +184,18 @@ export default function ResourceList(props) { })} </Grid> <Snackbar - open={snackbar_open} + open={snackInfo.open} autoHideDuration={6000} - onClose={() => setSnackbarOpen(false)} + onClose={handleCloseSnack} anchorOrigin={{ vertical: 'top', horizontal: 'right' }} > - <Alert onClose={() => setSnackbarOpen(false)} severity="info"> - Os recursos serão baixados + <Alert onClose={handleCloseSnack} severity={snackInfo.severity}> + {snackInfo.text} </Alert> </Snackbar> - <FloatingDownloadButton url={download_url} empty={selected.indexOf(true) === -1} /> + <FloatingDownloadButton + handleDownloadSelection={handleDownloadSelection} + /> </ResourceListContainer> ); } @@ -182,7 +217,4 @@ const PanelButtonText = styled.span` ` const ResourceGrid = styled(Grid)` padding-right: 7px; -` -const UnstyledAnchor = styled.a` - text-decoration: none !important; -` +` \ No newline at end of file diff --git a/src/Components/ResourcePageComponents/CommentsArea.js b/src/Components/ResourcePageComponents/CommentsArea.js index 0ba2d12a188d45a5516b20611d2aee1679846fbd..578ab7d29d668fe3204a7ed77bcd61b7764e1ae5 100644 --- a/src/Components/ResourcePageComponents/CommentsArea.js +++ b/src/Components/ResourcePageComponents/CommentsArea.js @@ -32,6 +32,7 @@ import Snackbar from '@material-ui/core/Snackbar'; import SignUpModal from './../SignUpModal' import MuiAlert from '@material-ui/lab/Alert'; import CircularProgress from '@material-ui/core/CircularProgress'; +import noAvatar from '../../img/default_profile.png'; function Alert(props) { return <MuiAlert elevation={6} variant="filled" {...props} />; @@ -101,7 +102,7 @@ export default function CommentsArea(props) { <Grid container style={{ paddingTop: "20px" }} spacing={1}> <Grid item xs={12} sm={2} style={{ paddingLeft: "15px", paddingRight: "15px" }}> <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}> - <img src={apiDomain + state.currentUser.avatar} className="minha-imagem" alt="user avatar" /> + <img src={ state.currentUser.avatar ? apiDomain + state.currentUser.avatar : noAvatar} className="minha-imagem" alt="user avatar" /> </div> </Grid> <Grid item xs={12} sm={10}> diff --git a/src/Pages/CollectionPage.js b/src/Pages/CollectionPage.js index 52a26eb90d29edfd109b41dd6cf90604e130f449..51c8ba5ce7b7826b0e39a890ce9b5b03fb2e680f 100644 --- a/src/Pages/CollectionPage.js +++ b/src/Pages/CollectionPage.js @@ -22,7 +22,7 @@ import VerticalRuler from '../Components/VerticalRuler.js'; import CollectionDescription from '../Components/CollectionDescription.js'; import ResourceList from '../Components/ResourceList.js'; import CollectionCommentSection from '../Components/CollectionCommentSection.js'; -import { apiDomain } from '../env'; +import LoadingSpinner from '../Components/LoadingSpinner'; import styled from 'styled-components'; import DowloadButton from '../Components/CollectionDowloadButton.js'; import Breadcrumbs from "@material-ui/core/Breadcrumbs"; @@ -31,12 +31,14 @@ import FollowCollectionButton from '../Components/FollowCollectionButton'; import { Store } from '../Store.js'; import Button from '@material-ui/core/Button'; import { getRequest } from '../Components/HelperFunctions/getAxiosConfig.js'; +import noAvatar from '../img/default_profile.png'; +import { apiDomain } from '../env.js'; export default function CollectionPage(props) { const { state } = useContext(Store); const [error, setError] = useState(false) - + const [loading, setLoading] = useState(true) const [collection, setCollection] = useState({ name: '', id: 0, @@ -46,8 +48,7 @@ export default function CollectionPage(props) { useEffect(() => { const url = `/collections/${collection_id}` - - getRequest(url, (data) => { setCollection(Object.assign({}, data)) }, (error) => { setError(true) }) + getRequest(url, (data) => { setCollection(Object.assign({}, data)); setLoading(false) }, (error) => { setError(true); setLoading(false) }) }, [state.currentUser.id]); const handleScrollToComments = () => { @@ -74,6 +75,8 @@ export default function CollectionPage(props) { </Grid> </Grid> </CollectionNotFound> + if (loading) + return <LoadingSpinner text="Carregando coleção..." /> else return ( <> @@ -86,9 +89,9 @@ export default function CollectionPage(props) { <Grid container direction="row" justify="center" alignItems="center"> <Grid item md={3}> <CollectionAuthor - author_id={collection.owner ? collection.owner.id : 0} - name={collection.owner ? collection.owner.name : ""} - imgsrc={collection.owner ? apiDomain + collection.owner.avatar : ''} /> + author_id={collection.owner.id ? collection.owner.id : 0} + name={collection.owner.name ? collection.owner.name : ""} + imgsrc={collection.owner.avatar ? apiDomain + collection.owner.avatar : noAvatar} /> </Grid> @@ -99,12 +102,12 @@ export default function CollectionPage(props) { liked={collection.liked} scrollToComments={handleScrollToComments} title={collection.name ? collection.name : ""} - collection_id={collection.id ? collection.id : 0} /> + collection_id={collection_id} /> </Grid> <Grid item md={3}> <DowloadButton - id={collection.id ? collection.id : 0} + id={collection_id} /> <div style={{ height: 12 }}></div> <FollowCollectionButton @@ -143,10 +146,10 @@ export default function CollectionPage(props) { </Grid> <Grid container item xs={12} style={{ marginTop: 40, paddingBottom: 40 }} ref={comment_ref}> - <CollectionCommentSection - id={collection_id} - currentUserId={state.currentUser.id} - + <CollectionCommentSection + id={collection_id} + currentUserId={state.currentUser.id} + avatar={state.currentUser.avatar ? apiDomain + state.currentUser.avatar : noAvatar} /> </Grid> </Grid> diff --git a/src/Pages/Search.js b/src/Pages/Search.js index ae699d2abf1d545e7312911187499a512026d81d..5e5fcbd8c572eff3e3439dce20827644de52ce1d 100644 --- a/src/Pages/Search.js +++ b/src/Pages/Search.js @@ -50,11 +50,11 @@ export default function Search(props) { const [resultsCollection, setResultsCollection] = useState([]); const [resultsUser, setResultsUser] = useState([]); const [currOrder, setCurrOrder] = useState(order); - const [page] = useState(0); + const [page, setPage] = useState(0); const [isloading, setIsLoading] = useState(false); const [loadingMoreData, setLoadingMoreData] = useState(false); const [isFiltering, setIsFiltering] = useState(false); - const [resultsPerPage, setResultsPerPage] = useState(12); + const [resultsPerPage] = useState(12); const [showingResults, setShowingResults] = useState(0); const [totalResults, setTotalResults] = useState(0); const [options] = React.useState([ @@ -78,9 +78,15 @@ export default function Search(props) { currOption = option; function handleSuccessfulGet(data, headers) { - if (currOption === "LearningObject") setResultsResource(data); - else if (currOption === "Collection") setResultsCollection(data); - else if (currOption === "User") setResultsUser(data); + if (currOption === "LearningObject") { + setResultsResource((previousData) => previousData.concat(data)); + } + else if (currOption === "Collection") { + setResultsCollection((previousData) => previousData.concat(data)); + } + else if (currOption === "User") { + setResultsUser((previousData) => previousData.concat(data)); + } dispatch({ type: "SAVE_SEARCH", newSearch: { @@ -91,7 +97,7 @@ export default function Search(props) { if (headers.has('X-Total-Count')) { setTotalResults(headers.get('X-Total-Count')); } - setShowingResults(data.length) + setShowingResults((previousSize) => data.length + previousSize) setIsLoading(false); setIsFiltering(false); setLoadingMoreData(false); @@ -142,11 +148,11 @@ export default function Search(props) { }, [window.history.state === null ? true : window.history.state.key, state.currentUser.id]) useEffect(() => { - if (resultsPerPage > 12) { - //setIsLoading(true); + if (page > 0) { + setIsLoading(true); collectStuff(option); } - }, [resultsPerPage]); + }, [page]); return ( <div style={{ backgroundColor: "#f4f4f4" }}> @@ -170,6 +176,11 @@ export default function Search(props) { </div> <Dropdown options={options} value={optionResult} onChange={(e) => { + setResultsCollection([]); + setResultsResource([]); + setResultsUser([]); + setShowingResults(0); + setPage(0); setIsLoading(true); currOption = e.value; history.push(`/busca?query=${state.search.query}&search_class=${currOption}`) @@ -246,7 +257,7 @@ export default function Search(props) { }} onClick={() => { setLoadingMoreData(true); - setResultsPerPage(resultsPerPage + 12) + setPage(page + 1) }} > { @@ -318,7 +329,7 @@ export default function Search(props) { }} onClick={() => { setLoadingMoreData(true); - setResultsPerPage(resultsPerPage + 12) + setPage(page + 1) // collectStuff("LearningObject", ""); }} > @@ -391,7 +402,7 @@ export default function Search(props) { }} onClick={() => { setLoadingMoreData(true); - setResultsPerPage(resultsPerPage + 12) + setPage(page + 1) // collectStuff("User", ""); }} > diff --git a/src/env.js b/src/env.js index b7359157e1c0e9d260976bbe2b2545a2e0ca51b0..7284c42754e5c679138196c2bae2a2f128779c2b 100644 --- a/src/env.js +++ b/src/env.js @@ -17,15 +17,15 @@ You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ -var apiDomain = 'https://api.portalmec.c3sl.ufpr.br', - apiVersion = 'v1', - apiUrl = apiDomain + '/' + apiVersion; +var apiDomain = 'https://api.portalmectest.c3sl.ufpr.br', + apiVersion = 'v1', + apiUrl = apiDomain + '/' + apiVersion; var simcaqAPIDomain = 'https://www.simcaq.c3sl.ufpr.br/api', - apiSimcaqVersion = 'v1', - simcaqAPIurl = simcaqAPIDomain + '/' + apiSimcaqVersion + apiSimcaqVersion = 'v1', + simcaqAPIurl = simcaqAPIDomain + '/' + apiSimcaqVersion -export {apiUrl}; -export {apiDomain}; -export {simcaqAPIurl} +export { apiUrl }; +export { apiDomain }; +export { simcaqAPIurl }