Skip to content

Commit

Permalink
update chat-list UI and dynamic content
Browse files Browse the repository at this point in the history
  • Loading branch information
pth-1641 committed Jun 3, 2022
1 parent 20c161d commit b5b7703
Show file tree
Hide file tree
Showing 23 changed files with 10,656 additions and 2,533 deletions.
20 changes: 20 additions & 0 deletions components/ChatContent/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function Header({ detail }) {
const { roomName, chatAvatar, avatarBgColor } = detail;

return (
<header className='w-full flex items-center gap-3 pb-2 absolute top-0'>
<div
className={`rounded-full w-14 aspect-square overflow-hidden flex-center ${avatarBgColor}`}
>
{chatAvatar ? (
<img src={chatAvatar} alt='' />
) : (
<span className='text-white text-3xl'>{roomName[0]}</span>
)}
</div>
<h4 className='text-white font-medium'>{roomName}</h4>
</header>
);
}

export default Header;
26 changes: 18 additions & 8 deletions components/ChatContent/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { useState, useEffect } from 'react';
import Header from './Header';
import Input from './Input';
import { useRouter } from 'next/router';
import { getRoom } from '../../firebase/dbInteract';

function ChatContent() {
const router = useRouter();
const { roomId } = router.query;

const [detail, setDetail] = useState({});

useEffect(() => {
async function fetchRoomDetail() {
const res = await getRoom(roomId);
res.forEach((doc) => setDetail(doc.data()));
}
fetchRoomDetail();
}, [roomId]);

return (
<div className='h-full ml-10 relative'>
<header className='w-full flex items-center gap-3 pb-2 absolute top-0'>
<img
src='https://scontent.fhan5-3.fna.fbcdn.net/v/t39.30808-6/282206190_1130470397512027_1785203650365955963_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=dfmEKP7N0YwAX_VuB9z&_nc_oc=AQnCVKrsVK92MJsnnag4cjgj5ja0ceQdKnc0SrKVaUa-UbLP8woRMy-ZArW-7R6gooQem3vK7xAjK9Q4frLaJUbZ&_nc_ht=scontent.fhan5-3.fna&oh=00_AT_sC-vH65LIYRslbyspNuRHe9VCAiJHm4cOvTIcYCExeQ&oe=62950428'
alt=''
className='avatar h-14 w-14'
/>
<h4 className='text-white font-medium'>Guy Hawkins</h4>
</header>
<Header detail={detail} />
<div className='w-full pr-2 absolute top-16 bottom-16 overflow-y-scroll'>
<div className='flex gap-3 mt-5'>
<img
Expand Down
48 changes: 48 additions & 0 deletions components/ChatList/ChatItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Link from 'next/link';
import { useRouter } from 'next/router';

function ChatItem({ roomData }) {
const { roomName, id, chatAvatar, avatarBgColor } = roomData;

const router = useRouter();
const { roomId } = router.query;

return (
<Link href={'/' + id}>
<li
className={`py-2 px-3 rounded-xl hover:bg-lightDark duration-200 cursor-pointer ${
roomId === id && 'active'
}`}
>
<a className='flex items-center gap-3'>
<div
className={`rounded-full w-16 aspect-square overflow-hidden flex-center ${avatarBgColor}`}
>
{chatAvatar ? (
<img src={chatAvatar} alt='' />
) : (
<span className='text-white text-3xl'>
{roomName[0]}
</span>
)}
</div>
<div className='flex-between flex-1'>
<div>
<h4 className='text-white font-medium'>
{roomName}
</h4>
<p className='text-gray-400'>
Let{"'"}s meet todays?
</p>
</div>
<div className='text-gray-400'>
<time className='text-sm'>11:25</time>
</div>
</div>
</a>
</li>
</Link>
);
}

export default ChatItem;
20 changes: 2 additions & 18 deletions components/ListFriend/Header.js → components/ChatList/Header.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,16 @@
import { useState, useEffect } from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { IoLogOutOutline } from 'react-icons/io5';
import { MdDarkMode, MdLightMode } from 'react-icons/md';
import { FaUserCircle } from 'react-icons/fa';
import ModalInfo from './ModalInfo';
import ModalSignOut from './ModalSignOut';
import { auth } from '../../firebase/config';

function Header() {
const router = useRouter();

const [user, setUser] = useState(null);
function Header({ user }) {
const [displaySetting, setDisplaySetting] = useState(false);
const [displayModalInfo, setDisplayModalInfo] = useState(false);
const [displayModalSignOut, setDisplayModalSignOut] = useState(false);
const [darkMode, setDarkMode] = useState(true);

useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
} else {
router.push('/login');
}
});
}, []);

const toggleTheme = (e) => {
e.stopPropagation();
setDarkMode(!darkMode);
Expand Down
198 changes: 198 additions & 0 deletions components/ChatList/ModalConversation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import Modal from '../Modal';
import { useState, useEffect } from 'react';
import { IoClose } from 'react-icons/io5';
import { getUser, addRoom, updateRoomToUser } from '../../firebase/dbInteract';
import { useStore } from '../../store';

function ModalConversation({ setDisplayNewConversation }) {
const { displayName, photoURL, uid } = useStore((state) => state.user);

const [roomName, setRoomName] = useState('');
const [friendID, setFriendID] = useState('');
const [members, setMembers] = useState([]);
const [message, setMessage] = useState('');
const [chatType, setChatType] = useState('friend');

useEffect(() => {
setMembers([
{ displayName, photoURL, uid, isAdmin: true, nickname: '' },
]);
}, []);

const handleCreate = async (e) => {
e.preventDefault();
if (members.length < 2) {
setMessage('Need at least 2 members to create conversation!');
return;
}
let roomID = null;
if (chatType === 'friend') {
const { id } = await addRoom(
roomName,
members,
chatType,
members[1].photoURL
);
roomID = id;
} else {
const { id } = await addRoom(roomName, members, chatType, '');
roomID = id;
}
console.log(roomID);
members.forEach(async (member) => {
const { uid } = member;
const user = await getUser(uid);
user.forEach((doc) => updateRoomToUser(doc.id, roomID, 'add'));
});
setDisplayNewConversation(false);
};

const handleAddMember = async () => {
if (chatType === 'friend' && members.length === 2 && friendID) {
setMessage('If you want more than 2 members, please select group!');
return;
}
if (!(await getUser(friendID)).size && friendID) {
setMessage('This ID does not exist!');
return;
}
if (members.length > 30) {
setMessage('The maximum number of members has been reached!');
return;
}
const isAdd = members.some((member) => member.uid === friendID);
if (isAdd) {
setMessage('This member has been added!');
return;
} else {
const member = await getUser(friendID);
member.forEach((doc) => {
setMembers((prev) => [
...prev,
{
displayName: doc.data().displayName,
photoURL: doc.data().photoURL,
uid: doc.data().uid,
isAdmin: false,
nickname: '',
},
]);
if (chatType === 'friend') {
setRoomName(doc.data().displayName);
}
});
setFriendID('');
setMessage('');
}
};

const handleDeleteMember = (uid) => {
const currentMembers = members.filter((member) => member.uid !== uid);
setMembers(currentMembers);
if (chatType === 'friend') {
setRoomName('');
}
};

const handleFriendType = () => {
setChatType('friend');
members.length = 2;
};

return (
<Modal setDisplayNewConversation={setDisplayNewConversation}>
<div className='flex-center justify-around border-b border-gray-500 mb-3'>
<button
type='button'
className={`w-full py-2 ${
chatType === 'friend' && 'bg-lightDark'
}`}
onClick={handleFriendType}
>
Friend
</button>
<button
type='button'
className={`w-full py-2 ${
chatType === 'group' && 'bg-lightDark'
}`}
onClick={() => setChatType('group')}
>
Group
</button>
</div>
<form onSubmit={handleCreate}>
<label>Name</label>
<input
type='text'
className='input-dark mt-1 mb-3'
required
placeholder='Room Name'
value={roomName}
onChange={(e) => setRoomName(e.target.value)}
readOnly={chatType === 'friend'}
/>
<label>
{chatType === 'friend' ? "Friend's ID" : 'Members'}
</label>
<div className='flex-center gap-2 mt-1 mb-3'>
<input
type='text'
placeholder="Friend's ID"
className='input-dark'
value={friendID}
onChange={(e) => setFriendID(e.target.value)}
/>
<button
type='button'
className='modal-btn bg-emerald-500 hover:bg-emerald-600'
onClick={handleAddMember}
>
Add
</button>
</div>
<p className='text-red-600 text-sm mb-3'>{message}</p>
<ul className='flex items-center flex-wrap gap-2'>
{members.map((member) => (
<li
className='w-12 aspect-square relative'
key={member.uid}
>
<img
src={member.photoURL}
className='rounded-full'
/>
{!member.isAdmin && (
<span
className='absolute -top-1 -right-1 p-0.5 bg-gray-500 text-sm rounded-full cursor-pointer first:hidden'
onClick={() =>
handleDeleteMember(member.uid)
}
>
<IoClose />
</span>
)}
</li>
))}
</ul>
<div className='flex-center justify-end gap-3 mt-6'>
<button
className='modal-btn bg-red-500 hover:bg-red-600'
type='button'
onClick={() => setDisplayNewConversation(false)}
>
Cancel
</button>
<button
className='modal-btn bg-blue-500 hover:bg-blue-600'
type='submit'
>
Create
</button>
</div>
</form>
</Modal>
);
}

export default ModalConversation;
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useState } from 'react';
import { useState, memo } from 'react';
import { FiSearch } from 'react-icons/fi';
import { BsChat } from 'react-icons/bs';
import ModalConversation from './ModalConversation';

function Seach() {
function SearchInput() {
const [displayNewConversation, setDisplayNewConversation] = useState(false);
const handleAddNewChat = () => {
setDisplayNewConversation(true);
Expand Down Expand Up @@ -36,4 +36,4 @@ function Seach() {
);
}

export default Seach;
export default memo(SearchInput);
Loading

0 comments on commit b5b7703

Please sign in to comment.