boilerplate for new channel creation
This commit is contained in:
@@ -55,10 +55,15 @@ interface IRealTimeRoomProvider {
|
||||
handleSelectLine?: (newLineId: string) => void;
|
||||
|
||||
handleUpdateLineMemberState?: (lineId: string, newState: LineMemberState) => void;
|
||||
|
||||
showNewChannelForm: boolean;
|
||||
handleShowNewChannelForm?: (showOrHide: 'show' | 'hide') => void;
|
||||
}
|
||||
|
||||
const RealTimeRoomContext = React.createContext<IRealTimeRoomProvider>({
|
||||
roomsMap: {},
|
||||
|
||||
showNewChannelForm: false,
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -109,6 +114,7 @@ export function RealTimeRoomProvider({ children }: { children: React.ReactChild
|
||||
const [selectedLineId, setSelectedLineId] = useState<string>();
|
||||
|
||||
const [moveLineState, moveLine] = useAsyncFn(updateLineMemberState);
|
||||
const [showNewChannelForm, setShowNewChannelForm] = useState<boolean>(false);
|
||||
|
||||
/** All line listeners */
|
||||
useEffect(() => {
|
||||
@@ -309,9 +315,24 @@ export function RealTimeRoomProvider({ children }: { children: React.ReactChild
|
||||
[setSelectedLineId, handleUntuneFromLine, roomMap, user, handleTuneIntoLine],
|
||||
);
|
||||
|
||||
const handleShowNewChannelForm = useCallback(
|
||||
(showOrHide: 'show' | 'hide' = 'show') => {
|
||||
setSelectedLineId(undefined);
|
||||
setShowNewChannelForm(showOrHide === 'show');
|
||||
},
|
||||
[setShowNewChannelForm, setSelectedLineId],
|
||||
);
|
||||
|
||||
return (
|
||||
<RealTimeRoomContext.Provider
|
||||
value={{ roomsMap: roomMap, handleSelectLine, selectedLineId, handleUpdateLineMemberState }}
|
||||
value={{
|
||||
roomsMap: roomMap,
|
||||
handleSelectLine,
|
||||
selectedLineId,
|
||||
handleUpdateLineMemberState,
|
||||
showNewChannelForm,
|
||||
handleShowNewChannelForm,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RealTimeRoomContext.Provider>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { FiUsers, FiXSquare } from 'react-icons/fi';
|
||||
import { useAsyncFn, useDebounce } from 'react-use';
|
||||
import { userSearch } from '../../../../api/NirvanaApi';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Avatar, Skeleton } from 'antd';
|
||||
import { User } from '@nirvana/core/models/user.model';
|
||||
|
||||
export default function NewChannelForm() {
|
||||
const [peopleSearchQuery, setPeopleSearchQuery] = useState<string>('');
|
||||
|
||||
const [userSearchRes, fetchUsers] = useAsyncFn(userSearch);
|
||||
|
||||
const [isReady, cancel] = useDebounce(
|
||||
async () => {
|
||||
try {
|
||||
await fetchUsers(peopleSearchQuery);
|
||||
} catch (error) {
|
||||
toast.error('Problem in searching users!');
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
2000,
|
||||
[peopleSearchQuery],
|
||||
);
|
||||
|
||||
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
|
||||
|
||||
const addUser = useCallback(
|
||||
(newUser: User) => {
|
||||
setSelectedUsers((prevUsers) => [...prevUsers, newUser]);
|
||||
},
|
||||
[setSelectedUsers],
|
||||
);
|
||||
|
||||
const removeUser = useCallback((userIdToRemove: string) => {
|
||||
setSelectedUsers((prevUsers) =>
|
||||
prevUsers.filter((currentUser) => !currentUser._id.equals(userIdToRemove)),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
async (e) => {
|
||||
setPeopleSearchQuery(e.target.value);
|
||||
},
|
||||
[setPeopleSearchQuery],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1 items-center pt-10 bg-white">
|
||||
<div className="flex flex-col gap-2 max-w-lg w-full">
|
||||
{/* people search */}
|
||||
<span className="text-gray-500">People</span>
|
||||
<div
|
||||
className="flex flex-row items-center space-x-2 bg-transparent
|
||||
bg-gray-200 p-3 rounded"
|
||||
>
|
||||
<FiUsers className="text-lg text-gray-400" />
|
||||
<input
|
||||
placeholder="Search by name or email"
|
||||
className="flex-1 text-lg bg-transparent placeholder-gray-300 focus:outline-none"
|
||||
onChange={handleSearchChange}
|
||||
value={peopleSearchQuery}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* dropdown search results */}
|
||||
<div className="flex flex-col shadow-lg max-h-[500px] overflow-auto">
|
||||
{(userSearchRes.loading || !isReady) && <Skeleton />}
|
||||
|
||||
{userSearchRes.value?.users.map((searchedUser) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => addUser(searchedUser)}
|
||||
role={'presentation'}
|
||||
key={searchedUser.email}
|
||||
className="flex flex-row gap-2 items-center p-2 border border-gray-200
|
||||
hover:bg-gray-200 cursor-pointer"
|
||||
>
|
||||
<Avatar src={searchedUser.picture} size={'large'} shape={'square'} />
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="text-md font-semibold text-gray-600">{searchedUser.name}</span>
|
||||
<span className="text-sm text-gray-400">{searchedUser.email}</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* selected people */}
|
||||
<span className="flex flex-row justify-between mt-5">
|
||||
<span className="text-gray-500 ">{`Selected`}</span>
|
||||
|
||||
<span className="text-gray-400">{selectedUsers.length}/7</span>
|
||||
</span>
|
||||
|
||||
{selectedUsers.map((selectedUser) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => removeUser(selectedUser._id.toString())}
|
||||
role={'presentation'}
|
||||
key={selectedUser.email}
|
||||
className="flex flex-row gap-2 items-center p-2 border border-gray-200
|
||||
hover:bg-gray-200 cursor-pointer group"
|
||||
>
|
||||
<Avatar src={selectedUser.picture} size={'large'} shape={'square'} />
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="text-md font-semibold text-gray-600">{selectedUser.name}</span>
|
||||
<span className="text-sm text-gray-400">{selectedUser.email}</span>
|
||||
</span>
|
||||
|
||||
<FiXSquare className="ml-auto text-gray-400 cursor-pointer group-hover:scale-105" />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -18,16 +18,19 @@ import {
|
||||
import { Avatar, Tooltip } from 'antd';
|
||||
import useStreams from '../../../../providers/StreamProvider';
|
||||
import Peer from 'simple-peer';
|
||||
import NewChannelForm from '../compose/NewChannelForm';
|
||||
|
||||
export default function MainPanel() {
|
||||
const { user } = useAuth();
|
||||
|
||||
const { selectedLineId } = useRealTimeRooms();
|
||||
const { selectedLineId, showNewChannelForm } = useRealTimeRooms();
|
||||
|
||||
// already won't see stuff for overlay only mode as per parent configuration
|
||||
|
||||
// TODO: if there is stuff in search, show that first
|
||||
|
||||
if (showNewChannelForm) return <NewChannelForm />;
|
||||
|
||||
// if selected line, show line details
|
||||
if (selectedLineId) return <LineDetails />;
|
||||
|
||||
@@ -107,89 +110,6 @@ function LineDetails() {
|
||||
return pictureSources;
|
||||
}, [selectedLine]);
|
||||
|
||||
const [peopleSearch, setPeopleSearch] = useState<string>('');
|
||||
|
||||
const showCreateChannel = false;
|
||||
if (showCreateChannel)
|
||||
return (
|
||||
<div className="flex flex-col flex-1 items-center pt-10 bg-white">
|
||||
<div className="flex flex-col gap-2 max-w-lg w-full">
|
||||
{/* people search */}
|
||||
<span className="text-gray-500">People</span>
|
||||
<div
|
||||
className="flex flex-row items-center space-x-2 bg-transparent
|
||||
bg-gray-200 p-3 rounded"
|
||||
>
|
||||
<FiUsers className="text-lg text-gray-400" />
|
||||
<input
|
||||
placeholder="Search by name or email"
|
||||
className="flex-1 text-lg bg-transparent placeholder-gray-300 focus:outline-none"
|
||||
onChange={(e) => setPeopleSearch(e.target.value)}
|
||||
value={peopleSearch}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* dropdown search results */}
|
||||
<div className="flex flex-col shadow-lg max-h-[500px] overflow-auto">
|
||||
{selectedLine.otherUserObjects.map((otherUser) => {
|
||||
return (
|
||||
<div
|
||||
key={otherUser.email}
|
||||
className="flex flex-row gap-2 items-center p-2 border border-gray-200
|
||||
hover:bg-gray-200 cursor-pointer"
|
||||
>
|
||||
<Avatar src={otherUser.picture} size={'large'} shape={'square'} />
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="text-md font-semibold text-gray-600">{otherUser.name}</span>
|
||||
<span className="text-sm text-gray-400">{otherUser.email}</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{selectedLine.otherUserObjects.map((otherUser) => {
|
||||
return (
|
||||
<div
|
||||
key={otherUser.email}
|
||||
className="flex flex-row gap-2 items-center p-2 border border-gray-200
|
||||
hover:bg-gray-200 cursor-pointer"
|
||||
>
|
||||
<Avatar src={otherUser.picture} size={'large'} shape={'square'} />
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="text-md font-semibold text-gray-600">{otherUser.name}</span>
|
||||
<span className="text-sm text-gray-400">{otherUser.email}</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* selected people */}
|
||||
<span className="flex flex-row justify-between mt-5">
|
||||
<span className="text-gray-500 ">{`Selected`}</span>
|
||||
|
||||
<span className="text-gray-400">{selectedLine.otherMembers.length}/7</span>
|
||||
</span>
|
||||
|
||||
{selectedLine.otherUserObjects.map((otherUser) => {
|
||||
return (
|
||||
<div
|
||||
key={otherUser.email}
|
||||
className="flex flex-row gap-2 items-center p-2 border border-gray-200
|
||||
hover:bg-gray-200 cursor-pointer"
|
||||
>
|
||||
<Avatar src={otherUser.picture} size={'large'} shape={'square'} />
|
||||
<span className="flex flex-col gap-1">
|
||||
<span className="text-md font-semibold text-gray-600">{otherUser.name}</span>
|
||||
<span className="text-sm text-gray-400">{otherUser.email}</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1 bg-white relative">
|
||||
{/* line details */}
|
||||
|
||||
@@ -3,7 +3,7 @@ import MasterLineData from '@nirvana/core/models/masterLineData.model';
|
||||
import { Avatar, Skeleton, Tooltip } from 'antd';
|
||||
import React, { useCallback, useMemo, useEffect, useState } from 'react';
|
||||
import { FaPlus, FaSearch } from 'react-icons/fa';
|
||||
import { FiActivity, FiSearch, FiSun } from 'react-icons/fi';
|
||||
import { FiActivity, FiPlus, FiSearch, FiSun } from 'react-icons/fi';
|
||||
import useRealTimeRooms from '../../../../providers/RealTimeRoomProvider';
|
||||
import useRooms from '../../../../providers/RoomsProvider';
|
||||
import useAuth from '../../../../providers/AuthProvider';
|
||||
@@ -16,7 +16,8 @@ export default function SidePanel() {
|
||||
// using merely for loading state...better to add to realtimeroom context?
|
||||
const { rooms: initialRoomsFetch } = useRooms();
|
||||
|
||||
const { roomsMap, handleSelectLine, selectedLineId } = useRealTimeRooms();
|
||||
const { roomsMap, handleSelectLine, selectedLineId, handleShowNewChannelForm } =
|
||||
useRealTimeRooms();
|
||||
|
||||
const [toggleTunedLines, allLines] = useMemo(() => {
|
||||
const masterLines: MasterLineData[] = Object.values(roomsMap);
|
||||
@@ -34,6 +35,10 @@ export default function SidePanel() {
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
|
||||
const handleCreateNewChannel = useCallback(() => {
|
||||
handleShowNewChannelForm('show');
|
||||
}, [handleShowNewChannelForm]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-[350px] relative group border-r border-r-gray-200 shadow-xl bg-white z-20">
|
||||
<NavBar />
|
||||
@@ -49,12 +54,15 @@ export default function SidePanel() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="ml-auto flex flex-row items-center justify-evenly
|
||||
<Tooltip title={'New channel'}>
|
||||
<button
|
||||
onClick={handleCreateNewChannel}
|
||||
className="ml-auto flex flex-row items-center justify-evenly
|
||||
shadow-xl bg-gray-800 p-2 text-white text-xs"
|
||||
>
|
||||
<FaPlus />
|
||||
</button>
|
||||
>
|
||||
<FiPlus />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/* tuned in lines block */}
|
||||
|
||||
Reference in New Issue
Block a user