Skip to content

Commit

Permalink
[what] implement comment details, restruct provider to enable context…
Browse files Browse the repository at this point in the history
… api

[how]
- implement CommentThread, CommentStarter
- adopt moment.js to display timestamp
- complete remove comment and update comment thread actions
- extract canvas component
- replace prop drilling with context

[reference]
According to the following discussion, react-konva <Stage> will influence the Context API:
konvajs/react-konva#188
Redux is based on Context API, so it is influenced too
konvajs/react-konva#311
  • Loading branch information
lynda0214 committed May 20, 2022
1 parent ddc25bc commit 9d5576e
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 77 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@testing-library/user-event": "^13.5.0",
"classnames": "^2.3.1",
"konva": "^8.3.8",
"moment": "^2.29.3",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons": "^4.3.1",
Expand Down
63 changes: 23 additions & 40 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import {useContext, useState} from 'react';
import Konva from 'konva';
import {Stage, Layer, Rect} from 'react-konva';
import {Stage} from 'react-konva';
import {ModeContext} from './contexts/ModeProvider';
import {CanvasStatusContext} from './contexts/CanvasStatusProvider';
import CanvasStatusProvider from './contexts/CanvasStatusProvider';
import Toolbar from './components/toolbar/Toolbar';
import Picture from './components/picture/Picture';
import Canvas from './components/canvas/Canvas';
import {MODE, ID_PREFIX} from './constants';
import './App.css';
import Comment from "./components/comment/Comment";

const GET_CURSOR_PATH = {
[MODE.COMMENT]: () => `url('cursor/comment.png')`,
[MODE.POINTER]: () => `url('cursor/pointer.png')`,
[MODE.HAND]: (isClicking) => isClicking ? `url('cursor/hand-rock.png')` : `url('cursor/hand-paper.png')`,
[MODE.MAGNIFIER]: () => `url('cursor/magnifier.png')`,
[MODE.COMMENT]: () => `url('cursor/comment.png') 0 14,auto`,
[MODE.POINTER]: () => `url('cursor/pointer.png'),auto`,
[MODE.HAND]: (isClicking) => isClicking ? `url('cursor/hand-rock.png'),auto` : `url('cursor/hand-paper.png'),auto`,
[MODE.MAGNIFIER]: () => `url('cursor/magnifier.png'),auto`,
};

const App = () => {
const {mode, user} = useContext(ModeContext);
const {pictures, comments, updatePicturePosition, removePicture, addComment} = useContext(CanvasStatusContext);
const [selectID, setSelectID] = useState(ID_PREFIX.CANVAS);
const [isClicking, setIsClicking] = useState(false);
const [selectID, setSelectID] = useState(ID_PREFIX.CANVAS);
const [newComment, setNewComment] = useState(null);

const handleClick = (event) => {
setIsClicking(true);
Expand All @@ -45,46 +44,30 @@ const App = () => {
fill: Konva.Util.getRandomColor(),
x: x,
y: y,
starter: user
starter: user,
thread: [],
}
addComment(newAnchor);
setNewComment(newAnchor);
}

const handleUnclick = () => {
setIsClicking(false);
}

return (
<div style={{cursor: `${GET_CURSOR_PATH[mode](isClicking)},auto`}}>
<div style={{cursor: `${GET_CURSOR_PATH[mode](isClicking)}`}}>
<Toolbar/>
<Stage id={ID_PREFIX.CANVAS}
width={window.innerWidth}
height={window.innerHeight}
draggable={mode === 'hand'}
onMouseDown={handleClick}
onMouseUp={handleUnclick}
<Stage
id={ID_PREFIX.CANVAS}
width={window.innerWidth}
height={window.innerHeight}
draggable={mode === 'hand'}
onMouseDown={handleClick}
onMouseUp={handleUnclick}
>
<Layer>
{pictures.map((picture) =>
<Picture key={picture.id}
mode={mode}
isSelecting={picture.id === selectID}
comments={comments.filter((comment) => comment.parentId === picture.id)}
updatePicturePosition={updatePicturePosition}
removePicture={removePicture}
{...picture}
/>
)}
{comments
.filter((comment) => comment.parentId === ID_PREFIX.CANVAS)
.map((comment) =>
<Comment
key={comment.id}
comment={comment}
mode={mode}
/>
)}
</Layer>
<CanvasStatusProvider>
<Canvas selectID={selectID} mode={mode} currentUser={user} newComment={newComment}/>
</CanvasStatusProvider>
</Stage>
</div>
);
Expand Down
45 changes: 45 additions & 0 deletions src/components/canvas/Canvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {useContext, useEffect} from 'react';
import {Layer} from 'react-konva';
import {CanvasStatusContext} from '../../contexts/CanvasStatusProvider';
import Picture from '../picture/Picture';
import Comment from '../comment/Comment';
import {ID_PREFIX} from '../../constants';

const Canvas = ({mode, currentUser, selectID, newComment}) => {
const {
pictures,
comments,
addComment,
} = useContext(CanvasStatusContext);

useEffect(() => {
if (!newComment) return;
addComment(newComment);
}, [newComment]);

return (
<Layer>
{pictures.map((picture) =>
<Picture key={picture.id}
mode={mode}
currentUser={currentUser}
isSelecting={picture.id === selectID}
comments={comments.filter((comment) => comment.parentId === picture.id)}
{...picture}
/>
)}
{comments
.filter((comment) => comment.parentId === ID_PREFIX.CANVAS)
.map((comment) =>
<Comment
key={comment.id}
comment={comment}
mode={mode}
currentUser={currentUser}
/>
)}
</Layer>
);
}

export default Canvas;
147 changes: 145 additions & 2 deletions src/components/comment/Comment.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.comment-collapsed__avatar {
.comment__avatar {
background-color: #0D99FF;
color: white;
display: flex;
Expand All @@ -12,8 +12,151 @@
margin-right: 5px;
border: white 5px solid;
box-shadow: 5px 5px 20px rgba(0, 0, 0, 0.25);
cursor: auto;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}

.comment-collapsed__avatar:hover {
.comment__avatar--collapsed:hover {
background-color: #0c77c5;
}

.comment__avatar--message {
background-color: #626262;
box-shadow: none;
border: 0;
margin-left: 5px;
padding: 10px;
font-size: 12px;
box-sizing: border-box;
}

.comment-starter {
display: flex;
align-items: center;
justify-content: center;
}

.comment-starter__input {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
background-color: white;
border-radius: 50px;
box-shadow: 5px 5px 20px rgb(0 0 0 / 15%);
cursor: auto;
}

.comment-starter__input input {
border: 0;
outline: 0;
font-size: 14px;
}

.comment-starter__input input::placeholder {
color: #cccccc;
}

.comment-starter__input svg {
color: #cccccc;
}

.comment-starter__input svg:hover {
color: #626262;
}

.comment-thread {
display: flex;
justify-content: flex-start;
}

.comment-thread__panel {
background-color: white;
box-shadow: 5px 5px 20px rgb(0 0 0 / 15%);
cursor: auto;
width: 230px;
border-radius: 5px;
}

.comment-thread__panel__header {
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
color: #cccccc;
padding: 3px 5px 0px;
border-bottom: #cccccc solid 1px;
box-sizing: border-box;
}

.comment-thread__panel__header__button {
margin-left: 5px;
}

.resolve-button:hover {
color: #7dd559;
}

.close-button:hover {
color: #cb3838;
}

.message {
display: flex;
padding: 3px;
}

.message__main {
width: 100%;
}

.message__header {
width: 100%;
display: flex;
justify-content: space-between;
}

.message__header__name {
font-size: 13px;
}

.message__header__time {
font-size: 12px;
color: #cccccc;
}

.message__content {
font-size: 13px;
color: #626262;
}

.new-message__input {
display: flex;
align-items: center;
margin-bottom: 3px;
}

.new-message__input input {
border: 0;
outline: 0;
background-color: #e0e0e0;
border-radius: 5px;
margin-right: 5px;
padding: 2px 5px;
font-size: 13px;
}

.new-message__input input::placeholder {
color: #cccccc;
}

.new-message__input svg {
color: #cccccc;
}

.new-message__input svg:hover {
color: #626262;
}
Loading

0 comments on commit 9d5576e

Please sign in to comment.