Skip to content
This repository has been archived by the owner on Nov 2, 2024. It is now read-only.

Commit

Permalink
🚧 Add nolla news feed
Browse files Browse the repository at this point in the history
  • Loading branch information
danieladugyan committed Aug 17, 2023
1 parent ab74572 commit 3f44403
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 1 deletion.
6 changes: 5 additions & 1 deletion frontend/components/News/articleSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type NewsPageProps = {
loading?: boolean;
setLoading?: (loading: boolean) => void;
small?: boolean
nollning?: boolean
};

export default function ArticleSet({
Expand All @@ -20,11 +21,14 @@ export default function ArticleSet({
loading,
setLoading,
small,
nollning = false,
}: NewsPageProps) {
const {
error, data, refetch,
} = useNewsPageQuery({
variables: { page_number: pageIndex, per_page: articlesPerPage, tagIds },
variables: {
page_number: pageIndex, per_page: articlesPerPage, tagIds, nollning,
},
onCompleted: () => {
if (setLoading) setLoading(false);
},
Expand Down
133 changes: 133 additions & 0 deletions frontend/components/Nolla/Article.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import {
Avatar,
Box,
Button,
IconButton,
Paper,
Stack,
Typography,
useMediaQuery,
} from '@mui/material';
import { DateTime } from 'luxon';
import { useTranslation } from 'next-i18next';
import { useRef, useState } from 'react';
import Markdown from '~/components/Markdown';
import {
authorIsUser,
getAuthor,
getSignature,
} from '~/functions/authorFunctions';
import { timeAgo } from '~/functions/datetimeFunctions';
import selectTranslation from '~/functions/selectTranslation';
import {
ArticleQuery,
ArticleRequestStatus,
Author,
} from '~/generated/graphql';
import { hasAccess, useApiAccess } from '~/providers/ApiAccessProvider';
import { useUser } from '~/providers/UserProvider';
import routes from '~/routes';
import Link from '../Link';
import articleStyles from '~/components/News/articleStyles';

type ArticleProps = {
article: ArticleQuery['article'];
};

const getNollaNewsAuthor = (i18n: any, author: Partial<Author>) => {
if (author.__typename === 'Member') {
return getSignature(author);
}
const { name, nameEn } = author.position;
return selectTranslation(i18n, name, nameEn);
};

export default function Article({ article }: ArticleProps) {
const classes = articleStyles();
const { t, i18n } = useTranslation('common');
const date = DateTime.fromISO(
article.status === ArticleRequestStatus.Approved
? article.publishedDatetime
: article.createdDatetime,
).setLocale(i18n.language);
const apiContext = useApiAccess();
const { user } = useUser();
const [expanded, setExpanded] = useState(false);
const markdownRef = useRef<HTMLDivElement>(null);

const markdown = selectTranslation(i18n, article.body, article.bodyEn);
const isScreenLarge = useMediaQuery((theme: any) =>
theme.breakpoints.up('md'));

const topPart = (
<Stack direction="row" spacing={1}>
{/* Avatar and name */}
<Stack direction="row" spacing={1}>
<Avatar
src={
getAuthor(article.author)?.picture_path
|| '/images/nolla/nollning_logo_small.png'
}
style={{
width: 50,
height: 50,
}}
/>
<Stack spacing={0.5} alignItems="flex-start">
{getNollaNewsAuthor(i18n, article.author)}
<Typography>{timeAgo(date)}</Typography>
</Stack>
</Stack>

{/* Edit button */}
{(hasAccess(apiContext, 'news:article:update')
|| authorIsUser(article.author, user)) && (
<Link href={routes.editArticle(article.id)}>
<IconButton color="primary">
<EditOutlinedIcon />
</IconButton>
</Link>
)}
</Stack>
);

return (
<Paper className={classes.article} component="article">
<Stack>
<Stack
direction={isScreenLarge ? 'row' : 'column'}
justifyContent="space-between"
spacing={1}
>
{topPart}
</Stack>
<Stack direction="row" gap={1}>
{/* Body */}
<Box
sx={{
flexGrow: 1,
maxWidth: '75ch',
maxHeight: expanded ? undefined : '180px',
position: 'relative',
maskImage: expanded
? undefined
: 'linear-gradient(to bottom, black 130px, transparent 175px, transparent)',
}}
ref={markdownRef}
>
<Markdown content={markdown} />
</Box>
</Stack>

{/* Read more button */}
<Button
sx={{ alignSelf: 'flex-start' }}
onClick={() => setExpanded((state) => !state)}
>
{expanded ? t('minimize') : t('read_more')}
</Button>
</Stack>
</Paper>
);
}
78 changes: 78 additions & 0 deletions frontend/pages/nolla/news.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import AddIcon from '@mui/icons-material/Add';
import {
Alert, Box, Button, Pagination, Typography,
} from '@mui/material';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import Article from '~/components/Nolla/Article';
import NollaLayout from '~/components/Nolla/layout';
import theme from '~/components/Nolla/theme';
import genGetProps from '~/functions/genGetServerSideProps';
import { useNewsPageQuery } from '~/generated/graphql';
import { hasAccess, useApiAccess } from '~/providers/ApiAccessProvider';
import routes from '~/routes';

const ARTICLES_PER_PAGE = 10;

export default function News() {
const apiContext = useApiAccess();
const router = useRouter();
const { t } = useTranslation('common');
const currentPage = parseInt(router.query.page as string, 10) || 1;
const { data } = useNewsPageQuery({
variables: {
page_number: currentPage,
per_page: ARTICLES_PER_PAGE,
nollning: true,
},
});
const totalPages = data?.news?.pageInfo?.totalPages || 1;
const articles = data?.news?.articles || [];

// restrict access during development
useEffect(() => {
if (!apiContext.apisLoading && !apiContext.hasAccess('nolla:news')) {
router.push(routes.root);
}
}, [apiContext, router]);

return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<Alert severity="warning">
<Typography>This page is under construction!</Typography>
</Alert>

{hasAccess(apiContext, 'news:article:create') && (
<Button
onClick={() => router.push(routes.createArticle)}
variant="outlined"
>
{t('news:new_article')}
<AddIcon style={{ marginLeft: '0.25rem' }} />
</Button>
)}

{articles.map((article) =>
(article ? (
<Article key={article.id} article={article} />
) : (
<div>{t('news:articleError.missing')}</div>
)))}

<Pagination
page={currentPage}
count={totalPages}
onChange={(_, page) => router.push(`?page=${page}`)}
/>
</Box>
);
}

export const getServerSideProps = genGetProps(['nolla', 'news']);

News.getLayout = function getLayout({ children }) {
return <NollaLayout>{children}</NollaLayout>;
};

News.theme = theme;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"next": "Next",
"back": "Back",
"read_more": "Read more",
"minimize": "Minimize",
"sign in": "Sign in",
"sign out": "Sign out",
"register": "Register",
Expand Down
1 change: 1 addition & 0 deletions frontend/public/locales/sv/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"next": "Nästa",
"back": "Tillbaka",
"read_more": "Läs mer",
"minimize": "Minimera",
"sign in": "Logga in",
"sign out": "Logga ut",
"register": "Registrera dig",
Expand Down

0 comments on commit 3f44403

Please sign in to comment.