Skip to content

Commit

Permalink
Merge pull request #388 from cofacts/tutorial
Browse files Browse the repository at this point in the history
Implement tutorial page
  • Loading branch information
MrOrz authored Feb 17, 2021
2 parents 3234b5a + 12f2388 commit 65042bb
Show file tree
Hide file tree
Showing 64 changed files with 3,963 additions and 393 deletions.
2 changes: 1 addition & 1 deletion .storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@300;400;500;700&display=swap" rel="stylesheet">
2 changes: 2 additions & 0 deletions Storyshots.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,5 @@ initStoryshots({
},
snapshotSerializers: [createSerializer({ map: removeMaterialUIInternals })],
});

Element.prototype.scrollTo = () => {};
1 change: 1 addition & 0 deletions __mocks__/fileMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
4 changes: 1 addition & 3 deletions components/AppLayout/AppFooter.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ function AppFooter() {
</div>
<div className={classes.column}>
<h3>{t`About`}</h3>
<CustomLink external href="">
{t`About Cofacts`}
</CustomLink>
<CustomLink href="/about">{t`About Cofacts`}</CustomLink>
<CustomLink external href={USER_AGREEMENT_URL}>
{t`User Agreement`}
</CustomLink>
Expand Down
16 changes: 8 additions & 8 deletions components/LandingPage/SectionCanDo.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { makeStyles } from '@material-ui/core/styles';
import { useRouter } from 'next/router';
import { withDarkTheme } from 'lib/theme';
import Button from '@material-ui/core/Button';
import Link from 'next/link';

import InputBox from 'components/LandingPage/InputBox';

import lineQrcode from './images/line-qrcode.png';
Expand Down Expand Up @@ -245,14 +247,12 @@ const SectionCanDo = ({ className }) => {
LINE account. Forward suspicious text to it, the chatbot will help you
check the credibility of the text!`}
</div>
<Button
className={cx(classes.button, classes.text)}
href="https://g0v.hackmd.io/s/rkVVQDmqQ"
target="_blank"
rel="noopener noreferrer"
>
{t`Tutorial`}
</Button>

<Link href="/tutorial?tab=check-rumors" passHref>
<Button className={cx(classes.button, classes.text)}>
{t`Tutorial`}
</Button>
</Link>
</div>
</div>
</section>
Expand Down
66 changes: 40 additions & 26 deletions components/Ribbon.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,53 @@ import { forwardRef } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import cx from 'clsx';

const useStyles = makeStyles(theme => ({
root: {
position: 'relative',
left: -8,
const useStyles = makeStyles(theme => {
const RibbonColorMap = {
yellow: {
main: theme.palette.primary[500],
shadow: theme.palette.primary[700],
},
blue: {
main: theme.palette.common.blue3,
shadow: theme.palette.common.blue1,
},
};

backgroundColor: theme.palette.primary[500],
color: theme.palette.secondary.main,
width: 'fit-content',
return {
root: {
position: 'relative',
left: -8,

'&:before': {
content: '""',
position: 'absolute',
bottom: -8,
left: 0,
borderTop: `8px solid ${theme.palette.primary[700]}`,
borderLeft: '8px solid transparent',
backgroundColor: ({ ribbonTheme }) => RibbonColorMap[ribbonTheme].main,
color: theme.palette.secondary.main,
width: 'fit-content',

'&:before': {
content: '""',
position: 'absolute',
bottom: -8,
left: 0,
borderTop: ({ ribbonTheme }) =>
`8px solid ${RibbonColorMap[ribbonTheme].shadow}`,
borderLeft: '8px solid transparent',
},
},
},

tail: {
position: 'absolute',
left: '100%',
top: 0,
height: '100%',
tail: {
position: 'absolute',
left: '100%',
top: 0,
height: '100%',

'& > path': {
fill: theme.palette.primary[500],
'& > path': {
fill: ({ ribbonTheme }) => RibbonColorMap[ribbonTheme].main,
},
},
},
}));
};
});

function Ribbon({ className, children, ...props }, ref) {
const classes = useStyles();
function Ribbon({ className, theme = 'yellow', children, ...props }, ref) {
const classes = useStyles({ ribbonTheme: theme });
return (
<aside className={cx(classes.root, className)} ref={ref} {...props}>
{children}
Expand Down
6 changes: 4 additions & 2 deletions components/Ribbon.stories.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { withKnobs, text } from '@storybook/addon-knobs';
import { withKnobs, text, select } from '@storybook/addon-knobs';
import Ribbon from './Ribbon';

export default {
Expand All @@ -11,7 +11,9 @@ export default {
export const Default = () => (
<div style={{ margin: 40, background: '#ccc' }}>
Text content
<Ribbon>{text('ribbon children', 'ribbon children')}</Ribbon>
<Ribbon theme={select('theme', ['yellow', 'blue'], 'yellow')}>
{text('ribbon children', 'ribbon children')}
</Ribbon>
Text content
</div>
);
193 changes: 193 additions & 0 deletions components/Slider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import React, {
useRef,
useState,
useEffect,
useCallback,
forwardRef,
useImperativeHandle,
} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import cx from 'clsx';

const useStyles = makeStyles(() => ({
slider: {
display: 'flex',
position: 'relative',
scrollSnapType: 'x mandatory',
overflowX: 'scroll',
width: '100%',
scrollbarWidth: 'none',
MsOverflowStyle: 'none',

'&::-webkit-scrollbar': {
display: 'none',
},
},
slideWrapper: {
scrollSnapAlign: 'start',
flexShrink: 0,
width: '100%',
},
}));

const Slider = (
{
className,
children,
autoplay = false,
interval = 3000,
slideWrapperClassName,
initIndex = 0,
activeIndex,
onSlideChange = () => {},
},
ref
) => {
const classes = useStyles();

const [activeSlide, setActiveSlide] = useState(activeIndex || initIndex);

const timeoutId = useRef(null);
const sliderRef = useRef(null);

const slides = React.Children.toArray(children);
const slidesAmount = slides.length;

const slideTo = useCallback(
(index, { smooth = true } = {}) => {
const slider = sliderRef.current;

if (slider) {
slider.scrollTo({
left: slider.scrollWidth * (index / slidesAmount),
behavior: smooth ? 'smooth' : 'auto',
});
}
},
[slidesAmount]
);

const autoplayNext = useCallback(() => {
if (timeoutId.current) {
clearTimeout(timeoutId.current);
timeoutId.current = null;
}

const id = setTimeout(() => {
const nowActiveIndex = activeIndex || activeSlide;
const nextIndex =
nowActiveIndex === slidesAmount - 1 ? 0 : nowActiveIndex + 1;

if (activeIndex || activeIndex === 0) {
onSlideChange(nextIndex);
} else {
slideTo(nextIndex);
}

if (autoplay) {
autoplayNext();
} else {
clearTimeout(timeoutId.current);
}
}, interval);

timeoutId.current = id;
}, [
slidesAmount,
activeIndex,
activeSlide,
interval,
timeoutId,
onSlideChange,
slideTo,
autoplay,
]);

useImperativeHandle(
ref,
() => ({
activeIndex: activeIndex || activeSlide,
reset: (resetIndex = 0) => {
if (activeIndex || activeIndex === 0) {
onSlideChange(resetIndex);
} else {
slideTo(resetIndex, { smooth: false });
}

if (autoplay) {
autoplayNext();
}
},
}),
[activeIndex, activeSlide, slideTo, onSlideChange, autoplay, autoplayNext]
);

useEffect(() => {
const slider = sliderRef.current;

const onScroll = () => {
const slider = sliderRef.current;

if (slider) {
const nextIndex = Math.round(
(slider.scrollLeft / slider.scrollWidth) * slidesAmount
);

setActiveSlide(nextIndex);
}
};

if (slider) {
slider.addEventListener('scroll', onScroll);
}

return () => {
if (slider) {
slider.removeEventListener('scroll', onScroll);
}
};
}, [sliderRef, slidesAmount, activeIndex, onSlideChange]);

useEffect(() => {
if (autoplay) {
autoplayNext();
} else {
clearTimeout(timeoutId.current);
}

return () => {
if (timeoutId.current) {
clearTimeout(timeoutId.current);
}
};
}, [autoplay, autoplayNext, interval]);

useEffect(() => {
if ((activeIndex || activeIndex === 0) && activeIndex !== activeSlide) {
slideTo(activeIndex);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeIndex, slideTo]);

useEffect(() => {
onSlideChange(activeSlide);

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeSlide]);

return (
<div ref={sliderRef} className={cx(classes.slider, className)}>
{slides.map((slide, index) => (
<div
key={index}
className={cx(classes.slideWrapper, slideWrapperClassName)}
>
{slide}
</div>
))}
</div>
);
};

export default forwardRef(Slider);
Loading

0 comments on commit 65042bb

Please sign in to comment.