PJH's live chat - Chat List

λ°•μ •ν˜ΈΒ·2023λ…„ 2μ›” 5일
0

Live Chat Project

λͺ©λ‘ 보기
7/7
post-thumbnail

πŸš€ Start

이제 chatboxλ₯Ό λ§Œλ“€μ—ˆμœΌλ‹ˆ μ‹€μ œλ‘œ λ©”μ‹œμ§€κ°€ 좜λ ₯λ˜λŠ” 창을 λ§Œλ“€μ–΄λ³΄μž.

πŸ‘ μ‚¬μš©λ˜λŠ” κΈ°λŠ₯λ“€

  • chatList : λ©”μ‹œμ§€λ“€μ΄ 화면에 좜λ ₯λ˜λŠ” κΈ°λŠ₯을 담은 μ»΄ν¬λ„ŒνŠΈ
  • custom scrollbar : μ±„νŒ…μ°½ λ‚΄λΆ€μ—μ„œ λ™μž‘ν•˜λŠ” μŠ€ν¬λ‘€λ°”
  • dayjs : λ©”μ‹œμ§€ 생성 λ‚ μ§œλ₯Ό μ›ν•˜λŠ” ν˜•νƒœλ‘œ 좜λ ₯ν•΄μ£ΌλŠ” κΈ°λŠ₯
  • image drag & drop : 이미지λ₯Ό λ“œλž˜κ·Έν•˜μ—¬ μ±„νŒ…μ°½μ— λ“œλžν•˜μ—¬ κ°„νŽΈν•˜κ²Œ μ—…λ‘œλ“œν•΄μ£ΌλŠ” κΈ°λŠ₯
  • λ‚ μ§œλ³„ κ·Έλ£Ή: λ‚ μ§œλ³„λ‘œ λ©”μ‹œμ§€ κ·Έλ£Ήν™”μ‹œμΌœ ν‘œμ‹œ
⭐️ client
|
└── πŸ—‚ pages
|	  β”œβ”€β”€ πŸ—‚ Channel
|     └── πŸ—‚ DirectMessage
|
└── πŸ—‚ components
	 β”œβ”€β”€ πŸ—‚ ChatList
     └── πŸ—‚ Chat



πŸ’» ChatList


βœ… Channel / Direct Message μ»΄ν¬λ„ŒνŠΈ

  <ChatList ... chatData={chatData} // μž…λ ₯ν•œ λ©”μ‹œμ§€ 데이터  ... />

βœ… ChatList μ»΄ν¬λ„ŒνŠΈ

const ChatList: FC<Props> = ({chatData}) => {
  
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  	...
  return (
    <ChatZone>
    	{chatData.map(chat => (
         		// μœ μ € μ•„μ΄μ½˜, 이름, λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•΄μ£ΌλŠ” μ»΄ν¬λ„ŒνŠΈ
                <Chat key={chat.id} data={chat} /> 
              ))}
    </ChatZone>
  );
};



πŸ–± custom scrollbar

λΈŒλΌμš°μ € μžμ²΄μ—μ„œ μƒμ„±λ˜λŠ” μŠ€ν¬λ‘€λ°”κ°€ μ•„λ‹Œ μ±„νŒ…μ°½μ—μ„œ μƒμ„±λ˜λŠ” μŠ€ν¬λ‘€λ°”λ‘œ, λ™μž‘ν•˜μ§€ μ•Šμ„ λ•ŒλŠ” 보이지 μ•Šκ²Œ ν•˜λŠ” λ“±μ˜ μ»€μŠ€ν…€μ΄ κ°€λŠ₯ν•œ μŠ€ν¬λ‘€λ°”μ΄λ‹€.(μ°Έκ³ )


βœ… Channel / Direct Message μ»΄ν¬λ„ŒνŠΈ

  <ChatList
        scrollbarRef={scrollbarRef}
        isReachingEnd={isReachingEnd}
   />

βœ… ChatList μ»΄ν¬λ„ŒνŠΈ

import { positionValues, Scrollbars } from 'react-custom-scrollbars-2';

const ChatList: FC<Props> = ({scrollbarRef, isReachingEnd}) => {


return <Scrollbars autoHide ref={scrollbarRef} onScrollFrame={onScroll}
  			...
       </Scrollbars>
}


⌚️ dayjs

βœ… Chat μ»΄ν¬λ„ŒνŠΈ

πŸ‘Ž 2022.02-07T14:57:47.000Z

πŸ‘ 2:57 PM
const Chat: FC<Props> = memo(({ data }) => {
	...
  return <span>{dayjs(data.createdAt).format('h:mm A')}</span>
      


🏞 image drag & drop

HTML5μ—μ„œ μ œκ³΅ν•˜λŠ” onDragOver, onDrop을 ν†΅ν•΄μ„œ μš”μ†Œλ₯Ό λ“œλž˜κ·Έν•˜μ—¬ λ–¨κ΅΄ 수 μžˆλ‹€.

βœ… onDragOver : μš”μ†Œκ°€ λ“œλ‘­μ‘΄ μœ„μ— μžˆμ„ λ•Œ λ°˜μ‘ 즉, drop이 μ™Όλ£Œλ  λ•ŒκΉŒμ§€ κ³„μ†ν•΄μ„œ μ΄λ²€νŠΈκ°€ λ°œμƒλœλ‹€.

  • λ“œλ‘­ μ „κΉŒμ§€ κ³„μ†ν•΄μ„œ μ‹€ν–‰λ˜λŠ” λͺ¨μŠ΅

βœ… onDrop :μš”μ†Œκ°€ λ“œλ‘­μ‘΄μ— 놓일 λ•Œ λ°˜μ‘ 즉, drop이 μ‹€ν–‰λ˜μ—ˆμ„ λ•Œ μ‹€ν–‰λ˜λŠ” μ΄λ²€νŠΈμ΄λ‹€.

  • λ“œλ‘­μ΄ 되면 일반적으둜 이미지λ₯Ό μ—…λ‘œλ“œν•˜λŠ” 과정을 μ‹€ν–‰ν•˜κ²Œ λœλ‹€.

πŸ’‘ ν•΄λ‹Ή λ¬Έμ„œμ— μ ‘μ†ν•˜λ©΄ λ“œλ‘­μ΄λ²€νŠΈλ‘œ μ‹€ν–‰λ˜λŠ” 이미지 μ—…λ‘œλ“œ 과정을 κ°–λŠ” μ½”λ“œλ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.
πŸ‘‰ ondrop Event - W3Schools

const onDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    console.log(e);
    setDragOver(true);
  }, []);

const onDrop = useCallback(
    (e: any) => {
      e.preventDefault();
      console.log(e);
      const formData = new FormData();
      // dataTransfer :  λ“œλž˜κ·Έ μ•€ λ“œλ‘­ μž‘μ—… 쀑에 λ“œλž˜κ·Έλ˜λŠ” 데이터λ₯Ό λ³΄μœ ν•˜λŠ” 데 μ‚¬μš©
      if (e.dataTransfer.items) {
        // DataTransferItemList μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ νŒŒμΌμ— μ•‘μ„ΈμŠ€
        for (let i = 0; i < e.dataTransfer.items.length; i++) {
          // λ–¨μ–΄λœ¨λ¦° ν•­λͺ©μ΄ 파일이 μ•„λ‹Œ 경우 κ±°λΆ€
          if (e.dataTransfer.items[i].kind === 'file') {
            const file = e.dataTransfer.items[i].getAsFile();
            console.log('... file[' + i + '].name = ' + file.name);
            formData.append('image', file);
          }
        }
      } else {
        // DataTransfer μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ νŒŒμΌμ— μ•‘μ„ΈμŠ€
        for (let i = 0; i < e.dataTransfer.files.length; i++) {
          console.log(
            '... file[' + i + '].name = ' + e.dataTransfer.files[i].name
          );
          formData.append('image', e.dataTransfer.files[i]);
        }
      }
      axios // μ„œλ²„μ— 이미지 μ—…λ‘œλ“œ 및 μ €μž₯
        .post(`/api/workspaces/${workspace}/dms/${id}/images`, formData)
        .then(() => {
          setDragOver(false);
          localStorage.setItem( // λ©”μ‹œμ§€ μˆ˜μ‹  ν‘œμ‹œλ₯Ό μœ„ν•œ λ‘œμ»¬μŠ€ν† λ¦¬μ§€ μ‚¬μš©
            `${workspace}-${id}`,
            new Date().getTime().toString()
          );
          mutateChat();
        });
    },
    [workspace, id, mutateChat]
  );

return (
  	<Container onDrop={onDrop} onDragOver={onDragOver}>
    	... 	
   	 {dragOver && <DragOver>μ—…λ‘œλ“œ!</DragOver>}
    </Container>
      )

profile
κΈ°λ‘ν•˜μ—¬ κΈ°μ–΅ν•˜κ³ , κ³„νšν•˜μ—¬ μ‹€μ²œν•˜μž. will be a FE developer (HOMEλ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ Notion으둜 λ†€λŸ¬μ˜€μ„Έμš”!)

0개의 λŒ“κΈ€