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

adds: error page front-end concept #925

Merged
merged 5 commits into from
Apr 16, 2020
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
21 changes: 21 additions & 0 deletions src/backend/web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,25 @@ app.use(logger);
// Include our router with all endpoints
app.use('/', router);

/**
* Error Handler, Pass to front-end
*/
/* eslint-disable no-unused-vars */
app.use((err, req, res, next) => {
logger.logger.error({ error: err });
const status = err.status || 500;
res
.status(status)
.redirect(`/error?status=${status}${err.message ? `&message=${encodeURI(err.message)}` : ``}`);
});

/**
* 404 Handler, Pass to front-end
* Leverage .status because adding the `404` status in redirect causes "Not Found. Redirecting to /404?search=" to display.
*/
app.use((req, res) => {
logger.logger.warn(`Attempted to access the following unknown URL: ${req.url}`);
res.status(404).redirect(`/error?status=404`);
});

humphd marked this conversation as resolved.
Show resolved Hide resolved
module.exports = app;
23 changes: 16 additions & 7 deletions src/backend/web/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,21 +132,30 @@ function protectWithRedirect(req, res, next) {
passport.authenticate('saml')(req, res, next);
}

function throwCustomError(message, status) {
const err = Error(message);
err.status = status;

throw err;
}

// If user is not authenticated, return an appropriate 400 error type
function forbidden(req, res) {
/* eslint-disable no-unused-vars */
function forbidden(req, res, next) {
if (req.accepts('json')) {
humphd marked this conversation as resolved.
Show resolved Hide resolved
res.status(403).json({
humphd marked this conversation as resolved.
Show resolved Hide resolved
message: 'Forbidden',
});
} else {
// TODO: https://github.com/Seneca-CDOT/telescope/issues/890
res.status(403).send('Forbidden');

return;
}

throwCustomError('Forbidden: Access is not allowed for the requested page!', 403);
}

// If we aren't redirecting, we're going to forbid this request
function protectWithoutRedirect(req, res) {
forbidden(req, res);
function protectWithoutRedirect(req, res, next) {
forbidden(req, res, next);
}

/**
Expand All @@ -166,7 +175,7 @@ function checkUser(requireAdmin, redirect, req, res, next) {
}
// Not an admin, so fail this now using best response type
else {
forbidden(req, res);
forbidden(req, res, next);
}
} else {
// We don't need an admin, and this is a regular authenticated user, let it pass
Expand Down
127 changes: 30 additions & 97 deletions src/frontend/src/components/Banner/Banner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Fab from '@material-ui/core/Fab';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import CssBaseline from '@material-ui/core/CssBaseline';
import useSiteMetadata from '../../hooks/use-site-metadata';
import DynamicBackgroundContainer from '../DynamicBackgroundContainer';

const useStyles = makeStyles((theme) => ({
h1: {
Expand All @@ -32,18 +33,6 @@ const useStyles = makeStyles((theme) => ({
fontSize: '12rem',
},
},
bannerImg: {
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundAttachment: 'scroll',
backgroundSize: 'cover',
height: '100vh',
opacity: 0.4,
'-webkit-transition': 'opacity 1s ease-in-out',
'-moz-transition': 'opacity 1s ease-in-out',
'-o-transition': 'opacity 1s ease-in-out',
transition: 'opacity 1s ease-in-out',
},
heroBanner: {
height: '100vh',
},
Expand Down Expand Up @@ -101,6 +90,7 @@ const useStyles = makeStyles((theme) => ({
position: 'relative',
left: '49.5%',
bottom: theme.spacing(20),
zIndex: 300,
[theme.breakpoints.between('xs', 'sm')]: {
right: theme.spacing(4),
left: '80%',
Expand All @@ -127,100 +117,43 @@ function ScrollDown(props) {
);
}

async function getDynamicAsset(url, success, failure) {
try {
const response = await fetch(url);

if (response.status !== 200) {
throw new Error(response.statusText);
}

if (response.headers.get('content-type').includes('application/json')) {
success(await response.json());
return;
}

success(response);
} catch (error) {
console.error('Error getting dynamic asset', error);

if (failure) {
failure();
}
}
}

function RetrieveBannerDynamicAssets() {
const [backgroundImgSrc, setBackgroundImgSrc] = useState('');
const [transitionBackground, setTransitionBackground] = useState(true);
const [stats, setStats] = useState({ stats: { posts: 0, authors: 0, words: 0 } });

const classes = useStyles();
const { telescopeUrl } = useSiteMetadata();

useEffect(() => {
async function getBackgroundImgSrc() {
// Uses https://unsplash.com/collections/894/earth-%26-planets collection
/* Other Options:
- https://unsplash.com/collections/2411320/trend%3A-extreme-neon
- https://unsplash.com/collections/1538150/milkyway
- https://unsplash.com/collections/291422/night-lights
*/

// Ensure we are using an image which fits correctly to user's viewspace
const dimensions = `${window.innerWidth}x${window.innerHeight}`;
async function getStats() {
const response = await fetch(`${telescopeUrl}/stats/year`);

async function getStats() {
await getDynamicAsset(`${telescopeUrl}/stats/year`, (response) => {
const localeStats = {
posts: response.posts.toLocaleString(),
authors: response.authors.toLocaleString(),
words: response.words.toLocaleString(),
};
setStats(localeStats);

// Ease in Background
setTransitionBackground(false);
});
if (response.status !== 200) {
throw new Error(response.statusText);
}

await getDynamicAsset(
`https://source.unsplash.com/collection/894/${dimensions}/`,
(response) => {
setBackgroundImgSrc(response.url);
getStats();
},
() => {
// Fallback to default image
setBackgroundImgSrc('../../images/hero-banner.png');
}
);
const data = await response.json();

const localeStats = {
posts: data.posts.toLocaleString(),
authors: data.authors.toLocaleString(),
words: data.words.toLocaleString(),
};
setStats(localeStats);
}

getBackgroundImgSrc();
getStats();
}, [telescopeUrl]);

return (
<div>
<div
className={classes.bannerImg}
style={{
backgroundImage:
backgroundImgSrc === '../../images/hero-banner.png'
? backgroundImgSrc
: `url(${backgroundImgSrc})`,
opacity: transitionBackground ? 0 : 0.4,
}}
></div>
<Typography
variant="caption"
className={classes.stats}
style={{
opacity: transitionBackground ? 0 : 0.85,
}}
>
This year {stats.authors} of us have written {stats.words} words and counting. Add yours!
</Typography>
<DynamicBackgroundContainer>
{stats.authors !== 0 && (
<Typography variant="caption" className={classes.stats}>
This year {stats.authors} of us have written {stats.words} words and counting. Add
yours!
</Typography>
)}
</DynamicBackgroundContainer>
</div>
);
}
Expand Down Expand Up @@ -271,13 +204,13 @@ export default function Banner() {
>
v {gitInfo.version}
</a>
<div className={classes.icon}>
<ScrollDown>
<Fab color="primary" aria-label="scroll-down">
<KeyboardArrowDownIcon fontSize="large" />
</Fab>
</ScrollDown>
</div>
</div>
<div className={classes.icon}>
<ScrollDown>
<Fab color="primary" aria-label="scroll-down">
<KeyboardArrowDownIcon fontSize="large" />
</Fab>
</ScrollDown>
</div>
</>
);
Expand Down
73 changes: 73 additions & 0 deletions src/frontend/src/components/DynamicBackgroundContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useEffect, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
background: {
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundAttachment: 'scroll',
backgroundSize: 'cover',
transition: 'opacity 1s ease-in-out',
position: 'absolute',
height: '100vh',
width: '100%',
top: 0,
},
child: {
transition: 'opacity 1s ease-in-out',
},
}));

export default function DynamicBackgroundContainer(props) {
const [backgroundImgSrc, setBackgroundImgSrc] = useState('');
const [transitionBackground, setTransitionBackground] = useState(true);
const classes = useStyles();

useEffect(() => {
async function getBackgroundImgSrc() {
try {
// Uses https://unsplash.com/collections/1538150/milkyway collection
/* Other Options:
- https://unsplash.com/collections/291422/night-lights
*/

// Ensure we are using an image which fits correctly to user's viewspace
const dimensions = `${window.innerWidth}x${window.innerHeight}`;
const response = await fetch(`https://source.unsplash.com/collection/894/${dimensions}`);

if (response.status !== 200) {
throw new Error(response.statusText);
}

setBackgroundImgSrc(response.url);
} catch (error) {
setBackgroundImgSrc('../../images/hero-banner.png');
console.error(error);
} finally {
setTransitionBackground(false);
}
}

getBackgroundImgSrc();
}, []);

return (
<div>
<div
className={classes.background}
style={{
backgroundImage: `url(${backgroundImgSrc})`,
opacity: transitionBackground ? 0 : 0.45,
}}
></div>
<div
className={classes.child}
style={{
opacity: transitionBackground ? 0 : 0.85,
}}
>
{props.children}
</div>
</div>
);
}
Loading