Skip to content

Commit

Permalink
ADD Writing a post functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
dolf321 committed Jul 17, 2023
1 parent 07d0d1f commit e0a1e58
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 26 deletions.
87 changes: 64 additions & 23 deletions web/src/components/forums/CreateDialog/CreateDialog.jsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,102 @@
import React, {useState} from 'react';
import React, {useState, useRef} from 'react';

import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import Box from '@mui/material/Box';
import Input from '@mui/material/Input';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Switch from '@mui/material/Switch';
import EditorToolbar from './Toolbar/EditorToolbar';
import {Editor, EditorState, RichUtils, convertToRaw} from 'draft-js';
import CloseIcon from '@mui/icons-material/Close';
import IconButton from '@mui/material/IconButton';

import {Editor, EditorState} from 'draft-js';

import 'draft-js/dist/Draft.css';
import './TextEditor.css';
import {useStyles} from './CreateDialog.styles';

export default function CreateDialog({
mode = 'post',
isOpen = false,
open = false,
setOpen,
handleCreatePost,
}) {
// MUI theme-based css styles
const classes = useStyles();

// Form Data
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [isVisibleToStudents, setIsVisisbleToStudents] = useState(true);
const [isAnonymous, setIsAnonymous] = useState(false);
const [error, setError] = useState('');
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const editor = useRef(null);

const [error, setError] = useState('');
const validatePost = () => {
if (title && content) {
handleCreatePost({
title: title,
content: content,
content: JSON.stringify(convertToRaw(editorState.getCurrentContent())),
visible_to_students: isVisibleToStudents,
anonymous: isAnonymous,
});
};
};

const handleKeyCommand = (command) => {
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
setEditorState(newState);
return true;
}
return false;
};

return (
<Dialog
isFullScreen
open={isOpen}
open={open}
classes={{paper: classes.root}}
>
{error}
<Typography>
{mode === 'post' ? 'Create a new post' : 'Create a new comment'}
</Typography>
<Input value={title} onChange={(e) => setTitle(e.target.value)} placeholder={'Post Title'} />
<TextField value={content} onChange={(e) => setContent(e.target.value)}/>
<div className={classes.switchContainer}>
<p> Visibile to Students ? </p>
<Switch checked={isVisibleToStudents} onChange={() => setIsVisisbleToStudents(!isVisibleToStudents)}/>
</div>
<div className={classes.switchContainer}>
<p>Anonymous ? </p>
<Switch checked={isAnonymous} onChange={() => setIsAnonymous(!isAnonymous)}/>
</div>
<Button onClick={validatePost}> Post </Button>
<DialogTitle className={classes.titleContainer}>
<Box display="flex" alignItems="center">
<Box flexGrow={1} >{mode === 'post' ? 'Create A New Post' : 'Create A New Comment'}</Box>
<Box>
<IconButton onClick={(e) => setOpen(false)}>
<CloseIcon />
</IconButton>
</Box>
</Box>
</DialogTitle>
<DialogContent sx={{padding: 0}}>
<Input inputProps={{className: classes.inputTitle}} fullWidth
value={title} onChange={(e) => setTitle(e.target.value)} placeholder={'Put Title Here'} />
<div className={classes.toolbarContainer}>
<EditorToolbar editorState={editorState} setEditorState={setEditorState}/>
</div>
<Editor
ref={editor}
handleKeyCommand={handleKeyCommand}
editorState={editorState}
onChange={(editorState) => {
setEditorState(editorState);
}}
/>
<Box display="flex" alignItems="center" justifyContent="flex-start" gap="20px" padding="10px">
<div className={classes.switchContainer}>
<p> Visibile to Students?</p>
<Switch checked={isVisibleToStudents} onChange={() => setIsVisisbleToStudents(!isVisibleToStudents)}/>
</div>
<div className={classes.switchContainer}>
<p>Anonymous?</p>
<Switch checked={isAnonymous} onChange={() => setIsAnonymous(!isAnonymous)}/>
</div>
</Box>
</DialogContent>
<Button className={classes.submit} onClick={validatePost}> Post </Button>
</Dialog>
);
}
Expand Down
29 changes: 28 additions & 1 deletion web/src/components/forums/CreateDialog/CreateDialog.styles.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
import makeStyles from '@mui/styles/makeStyles';

export const useStyles = makeStyles(() => ({
export const useStyles = makeStyles((theme) => ({
root: {
width: '800px',
},
switchContainer: {
display: 'flex',
alignItems: 'center',
},
editorContainer: {
display: 'block',
height: '400px',
margin: theme.spacing(10),
padding: theme.spacing(1),
},
buttonIcon: {
color: theme.palette.primary.main,
},
toolbarContainer: {
borderColor: theme.palette.white,
borderTop: theme.spacing(0.1) + ' solid',
borderBottom: theme.spacing(0.1) + ' solid',
},
submit: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
},
titleContainer: {
backgroundColor: `${theme.palette.primary.main}80`,
padding: theme.spacing(1.5),
},
inputTitle: {
padding: theme.spacing(1.5, 1, 1.5, 1),
fontSize: '1rem',
borderBottom: '0', // Not working
},
}));

22 changes: 22 additions & 0 deletions web/src/components/forums/CreateDialog/TextEditor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.DraftEditor-editorContainer {
height: 15rem;
padding: 0.8rem;
margin-bottom: 10px;
overflow-y: auto;
overflow-x: hidden;
}

::-webkit-scrollbar {
height: 4px;
width: 6px;
scrollbar-color: white white;
}

::-webkit-scrollbar-thumb {
background: white;
border-radius: 10px;
}

::-webkit-scrollbar-track {
background: gray;
}
154 changes: 154 additions & 0 deletions web/src/components/forums/CreateDialog/Toolbar/EditorToolbar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React, {useState} from 'react';
import Box from '@mui/material/Box';
import {makeStyles} from '@mui/styles';
import {CssBaseline} from '@mui/material';
import FormatBoldIcon from '@mui/icons-material/FormatBold';
import FormatItalicIcon from '@mui/icons-material/FormatItalic';
import FormatUnderlinedIcon from '@mui/icons-material/FormatUnderlined';
import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
import CodeIcon from '@mui/icons-material/Code';
import ImageIcon from '@mui/icons-material/Image'; // Need draft-js plugin
import IconButton from '@mui/material/IconButton';


import {RichUtils} from 'draft-js';

const useStyles = makeStyles((theme) => ({
root: {
flex: 1,
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
padding: theme.spacing(0),
margin: theme.spacing(0.5, 0.25),
},
p: {
margin: theme.spacing(0),
},
buttonIcon: {
// margin: theme.spacing(1, 0, 0, 0),
},
IconLogo: {
// margin: theme.spacing(0, 0, 0, 0),
translate: 'transform(-50%, -50%)',
padding: theme.spacing(0.1),
},
// inline: {
// display: 'inline',
// },
// datePicker: {
// width: 300,
// marginRight: theme.spacing(2),
// },
// button: {
// marginRight: theme.spacing(1),
// },
}));

export default function EditorToolbar({editorState, setEditorState}) {
const classes = useStyles();
// Create JSON array of the icon and their functions FIX THIS
const tools = [
{label: 'H1', style: 'header-one', method: 'block'},
{label: 'H2', style: 'header-two', method: 'block'},
{label: 'H3', style: 'header-three', method: 'block'},
{
label: 'bold',
style: 'BOLD',
icon: <FormatBoldIcon className={classes.IconLogo} />,
method: 'inline', // Convert to enum?
},
{
label: 'italic',
style: 'ITALIC',
icon: <FormatItalicIcon className={classes.IconLogo} />,
method: 'inline',
},
{
label: 'underline',
style: 'UNDERLINE',
icon: <FormatUnderlinedIcon className={classes.IconLogo} />,
method: 'inline',
},
// {
// label: 'Blockquote',
// style: 'blockQuote',
// icon: <FormatQuoteIcon className={classes.IconLogo} />,
// method: 'block',
// },
{
label: 'Unordered-List',
style: 'unordered-list-item',
method: 'block',
icon: <FormatListBulletedIcon className={classes.IconLogo} />,
},
{
label: 'Ordered-List',
style: 'ordered-list-item',
method: 'block',
icon: <FormatListNumberedIcon className={classes.IconLogo} />,
},
// {
// label: 'Code Block',
// style: 'CODEBLOCK',
// icon: <CodeIcon className={classes.IconLogo} />,
// method: 'inline',
// },
// {
// label: 'Uppercase',
// style: 'UPPERCASE',
// method: 'inline',
// },
// {
// label: 'lowercase',
// style: 'LOWERCASE',
// method: 'inline',
// },
];
const applyStyle = (e, style, method) => {
e.preventDefault();
(method === 'block') ? setEditorState(RichUtils.toggleBlockType(editorState, style)) :
setEditorState(RichUtils.toggleInlineStyle(editorState, style));
};

const isActive = (style, method) => {
if (method === 'block') {
const selection = editorState.getSelection();
const blockType = editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType();
return blockType === style;
} else {
const currentStyle = editorState.getCurrentInlineStyle();
return currentStyle.has(style);
}
};

return (
<>
<CssBaseline />
<Box className={classes.root}>
{tools.map((tool, id) => (
<Box
key={tool.label}
>
<IconButton
color= {isActive(tool.style, tool.method) ? 'primary' : 'inherit'}
className={classes.buttonIcon}
key={id}
size="small"
onClick={(e) => applyStyle(e, tool.style, tool.method)}
onMouseDown={(e) => e.preventDefault()}
>
{/* If the tool has an icon, render it if not render the label */}
{tool.icon || tool.label}
</IconButton>
</Box>
))}
</Box>
</>
);
};
5 changes: 3 additions & 2 deletions web/src/pages/forums/Forum/Forum.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default function Forum({user}) {
const [selectedPost, setSelectedPost] = useState(undefined);
const [selectedContent, setSelectedContent] = useState(undefined);

const [isDialogOpen, setIsDialogOpen] = useState(undefined);
const [isDialogOpen, setIsDialogOpen] = useState(true);
const [dialogMode, setDialogMode] = useState('post');

const [refreshPosts, setRefreshPosts] = useState(0);
Expand Down Expand Up @@ -108,7 +108,8 @@ export default function Forum({user}) {
return (
<StandardLayout>
<CreateDialog
isOpen={isDialogOpen}
open={isDialogOpen}
setOpen={setIsDialogOpen}
mode={dialogMode}
handleCreatePost={handleCreatePost}
/>
Expand Down

0 comments on commit e0a1e58

Please sign in to comment.