Skip to content

Commit

Permalink
feat: chapter 1-1, 1-2 기본과제 통과
Browse files Browse the repository at this point in the history
  • Loading branch information
suzzyso committed Dec 26, 2024
1 parent c816674 commit 7c04ac7
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 27 deletions.
45 changes: 45 additions & 0 deletions src/components/posts/Post.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,58 @@
/** @jsx createVNode */
import { createVNode } from "../../lib";
import { globalStore } from "../../stores/globalStore.js";
import { toTimeFormat } from "../../utils/index.js";

export const Post = ({
id,
author,
time,
content,
likeUsers,
activationLike = false,
}) => {
const { getState, setState } = globalStore;
const { currentUser, loggedIn, posts } = getState();

const handleLike = () => {
if (!loggedIn) {
window.alert("로그인 후 이용해주세요.");
return;
}

const targetPost = posts.find((post) => post.id === id);
const username = currentUser.username;

// 좋아요 상태 업데이트
const updatedLikeUsers = targetPost.likeUsers.includes(username)
? targetPost.likeUsers.filter((user) => user !== username)
: [...targetPost.likeUsers, username];

// 새 포스트 업데이트
const updatedPost = { ...targetPost, likeUsers: updatedLikeUsers };
const updatedPosts = posts.map((post) =>
post.id === id ? updatedPost : post,
);

setState({ posts: updatedPosts });

// const targetPost = posts.find((post) => post.id === id);
// const prevLikeUsers = targetPost.likeUsers;
// const username = currentUser.username;
// const likeUsers = prevLikeUsers.includes(username)
// ? prevLikeUsers.filter((user) => user !== username)
// : [...prevLikeUsers, username];
// const newPost = { ...targetPost, likeUsers };

// updatePost(newPost);
};

// const updatePost = (newPost) => {
// const { id } = newPost;
// const newPosts = posts.map((post) => (post.id === id ? newPost : post));
// setState({ posts: newPosts });
// };

return (
<div className="bg-white rounded-lg shadow p-4 mb-4">
<div className="flex items-center mb-2">
Expand All @@ -20,6 +64,7 @@ export const Post = ({
<p>{content}</p>
<div className="mt-2 flex justify-between text-gray-500">
<span
onClick={handleLike}
className={`like-button cursor-pointer${activationLike ? " text-blue-500" : ""}`}
>
좋아요 {likeUsers.length}
Expand Down
24 changes: 24 additions & 0 deletions src/components/posts/PostForm.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
/** @jsx createVNode */
import { createVNode } from "../../lib";
import { globalStore } from "../../stores/globalStore";

export const PostForm = () => {
const { getState, setState } = globalStore;
const { currentUser, posts } = getState();

const submitPost = () => {
const contentEl = document.querySelector("#post-content");
const content = contentEl.value.trim();

if (!currentUser || !content) return;

const newPosts = [
...posts,
{
id: posts.length + 1,
author: currentUser?.username,
time: new Date().getTime(),
content,
likeUsers: [],
},
];

setState({ posts: newPosts });
};
return (
<div className="mb-4 bg-white rounded-lg shadow p-4">
<textarea
Expand All @@ -12,6 +35,7 @@ export const PostForm = () => {
<button
id="post-submit"
className="mt-2 bg-blue-600 text-white px-4 py-2 rounded"
onClick={submitPost}
>
게시
</button>
Expand Down
65 changes: 53 additions & 12 deletions src/lib/eventManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function handleEvent(e) {
}

export function setupEventListeners(root) {
if ($root) return;
if (!root) return;
$root = root;

eventListeners.forEach((handlers, eventType) => {
Expand Down Expand Up @@ -47,23 +47,64 @@ export function addEvent(element, eventType, handler) {
}

export function removeEvent(element, eventType, handler) {
const handlers = eventListeners.get(eventType);
const handlersMap = eventListeners.get(eventType);
if (!handlersMap) return;

const handlers = handlersMap.get(element);
if (!handlers) return;

const handlerList = handlers.get(element);
if (handlerList) {
handlerList.delete(handler);
if (handlerList.size === 0) {
handlers.delete(element);
if (handler) {
handlers.delete(handler);

if (handlers.size === 0) {
handlersMap.delete(element);
}
} else {
handlersMap.delete(element);
}

if (eventListeners.size === 0) {
if (handlersMap.size === 0) {
eventListeners.delete(eventType);
if ($root && $root._listeners?.has(eventType)) {
$root.removeEventListener(eventType, handleEvent, true);
$root._listeners.delete(eventType);
}
}
}

// export function removeEvent(element, eventType, handler) {
// const elementEvents = eventListeners.get(element);
// if (!elementEvents) return;

// if (handler) {
// const handlers = elementEvents.get(eventType);
// if (handlers) {
// handlers.delete(handler);
// if (handlers.size === 0) {
// elementEvents.delete(eventType);
// }
// }
// } else {
// elementEvents.delete(eventType);
// }

// if (elementEvents.size === 0) {
// eventListeners.delete(element);
// }

// // const handlers = eventListeners.get(eventType);

// // if (!handlers) return;

// // const handlerList = handlers.get(element);
// // if (handlerList) {
// // handlerList.delete(handler);
// // if (handlerList.size === 0) {
// // handlers.delete(element);
// // }
// // }

// // if (eventListeners.size === 0) {
// // eventListeners.delete(eventType);
// // if ($root && $root._listeners?.has(eventType)) {
// // $root.removeEventListener(eventType, handleEvent, true);
// // $root._listeners.delete(eventType);
// // }
// // }
// }
14 changes: 10 additions & 4 deletions src/lib/normalizeVNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ export function normalizeVNode(vNode) {
);
}

if (Array.isArray(vNode)) {
return vNode.map(normalizeVNode).join("");
}
// if (Array.isArray(vNode)) {
// return vNode.map(normalizeVNode).join("");
// }

const children = Array.isArray(vNode.children)
? vNode.children
: vNode.children
? [vNode.children]
: [];

return {
...vNode,
children: vNode.children.map(normalizeVNode).filter(Boolean),
children: children.map(normalizeVNode).filter(Boolean),
};
}
2 changes: 1 addition & 1 deletion src/lib/renderElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function renderElement(vNode, container) {
// 최초 렌더링시에는 createElement로 DOM을 생성하고
// 이후에는 updateElement로 기존 DOM을 업데이트한다.
// 렌더링이 완료되면 container에 이벤트를 등록한다.
console.log(`renderElement ${JSON.stringify(vNode)}, ${container}`);
// console.log(`renderElement ${JSON.stringify(vNode)}, ${container}`);

const oldNode = OldNodeMap.get(container);
const newNode = normalizeVNode(vNode);
Expand Down
7 changes: 4 additions & 3 deletions src/lib/updateElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function updateAttributes(target, originNewProps, originOldProps) {

for (const attr in oldProps) {
if (!(attr in newProps)) {
if (attr.startsWith("on") && typeof newProps[attr] === "function") {
if (attr.startsWith("on") && typeof oldProps[attr] === "function") {
const eventType = attr.slice(2).toLowerCase();
removeEvent(target, eventType, oldProps[attr]);
} else {
Expand All @@ -17,7 +17,7 @@ function updateAttributes(target, originNewProps, originOldProps) {
}

for (const attr in newProps) {
if (oldProps[attr] !== newProps[attr]) {
if (!(attr in newProps)) {
if (attr.startsWith("on") && typeof newProps[attr] === "function") {
const eventType = attr.slice(2).toLowerCase();
if (typeof oldProps[attr] === "function") {
Expand All @@ -28,10 +28,11 @@ function updateAttributes(target, originNewProps, originOldProps) {
target.className = newProps[attr];
} else if (attr === "style" && typeof newProps[attr] === "object") {
Object.entries(newProps[attr]).forEach(([key, value]) => {
target.style[key] = value;
target.style[key] = value || "";
});
} else {
target.setAttribute(attr, newProps[attr]);
// target.removeAttribute(attr);
}
}
}
Expand Down
27 changes: 20 additions & 7 deletions src/pages/HomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import { globalStore } from "../stores";
* - 로그인하지 않은 사용자가 게시물에 좋아요를 누를 경우, "로그인 후 이용해주세요"를 alert로 띄운다.
*/
export const HomePage = () => {
const { posts } = globalStore.getState();
const { posts, loggedIn, currentUser } = globalStore.getState();

const isChecked = (likeUsers) => {
return likeUsers.find((postLike) =>
postLike.includes(currentUser.username),
);
};

return (
<div className="bg-gray-100 min-h-screen flex justify-center">
Expand All @@ -20,13 +26,20 @@ export const HomePage = () => {
<Navigation />

<main className="p-4">
<PostForm />
{loggedIn && <PostForm />}
<div id="posts-container" className="space-y-4">
{[...posts]
.sort((a, b) => b.time - a.time)
.map((props) => {
return <Post {...props} activationLike={false} />;
})}
{posts &&
[...posts]
.sort((a, b) => b.time - a.time)
.map((props) => {
return (
<Post
key={props.id}
{...props}
activationLike={!!isChecked(props.likeUsers)}
/>
);
})}
</div>
</main>

Expand Down

0 comments on commit 7c04ac7

Please sign in to comment.