From bfdf20fef56d3da3b02a9e7e2bdac7fd4a923923 Mon Sep 17 00:00:00 2001 From: talksik Date: Fri, 13 May 2022 12:49:19 -0500 Subject: [PATCH] getting list showing now! --- .vscode/settings.json | 2 +- packages/desktop/src/components/lineIcon.tsx | 64 +++++ .../src/providers/RealTimeRoomProvider.tsx | 30 ++- .../src/tree/protected/terminal/Terminal.tsx | 23 +- .../protected/terminal/panels/SidePanel.tsx | 224 ++++++++++++++++++ 5 files changed, 320 insertions(+), 23 deletions(-) create mode 100644 packages/desktop/src/components/lineIcon.tsx create mode 100644 packages/desktop/src/tree/protected/terminal/panels/SidePanel.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 8711611..abfc7e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "workbench.colorTheme": "Default Light+", "javascript.format.semicolons": "insert", - "window.zoomLevel": 2 + "window.zoomLevel": 1 // "eslint.workingDirectories": ["./packages/desktop"] } diff --git a/packages/desktop/src/components/lineIcon.tsx b/packages/desktop/src/components/lineIcon.tsx new file mode 100644 index 0000000..3514f4f --- /dev/null +++ b/packages/desktop/src/components/lineIcon.tsx @@ -0,0 +1,64 @@ +import { Avatar } from 'antd'; + +import React, { useMemo } from 'react'; + +function LineIcon({ + sourceImages, + grayscale = true, +}: { + sourceImages: string[]; + grayscale: boolean; +}) { + const isMultiple = useMemo(() => { + if (sourceImages?.length > 1) return true; + + return false; + }, [sourceImages]); + + return ( + <> + {isMultiple ? ( +
+ {sourceImages.map((avatarSrc, index) => { + if (index === 0) { + return ( + + ); + } else if (index === 1) { + return ( + + ); + } + + // TODO: add third in the frame if we want later + return <>; + })} +
+ ) : ( + sourceImages?.map((avatarSrc, index) => ( + + )) + )} + + ); +} + +export default React.memo(LineIcon); diff --git a/packages/desktop/src/providers/RealTimeRoomProvider.tsx b/packages/desktop/src/providers/RealTimeRoomProvider.tsx index 6c28590..95f7319 100644 --- a/packages/desktop/src/providers/RealTimeRoomProvider.tsx +++ b/packages/desktop/src/providers/RealTimeRoomProvider.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useContext } from 'react'; +import React, { useEffect, useState, useContext, useCallback } from 'react'; import useRooms from './RoomsProvider'; import useSockets from './SocketProvider'; @@ -19,6 +19,7 @@ import { UserStartedBroadcastingResponse, UserStoppedBroadcastingResponse, } from '@nirvana/core/sockets/channels'; +import toast from 'react-hot-toast'; type LineIdToMasterLine = { [lineId: string]: MasterLineData; @@ -26,17 +27,27 @@ type LineIdToMasterLine = { interface IRealTimeRoomProvider { roomsMap: LineIdToMasterLine; + + selectedLine?: MasterLineData; + handleSelectLine: (newLineId: string) => void; } -const RealTimeRoomContext = React.createContext({ roomsMap: {} }); +const RealTimeRoomContext = React.createContext({ + roomsMap: {}, + handleSelectLine: () => { + // + }, +}); // handles reads of new data -// does NOT handle sending socket emissions +// keeps listening to incoming socket events to make sure that the realtime rooms map is highly available export function RealTimeRoomProvider({ children }: { children: React.ReactChild }) { const { rooms } = useRooms(); const { $ws } = useSockets(); const [realTimeRoomMap, setRealTimeRoomMap] = useState({}); + const [selectedLine, setSelectedLine] = useState(); + useEffect(() => { /** * initiate listeners @@ -188,8 +199,19 @@ export function RealTimeRoomProvider({ children }: { children: React.ReactChild } }, [rooms.value, setRealTimeRoomMap]); + /** show user line details on click of one line */ + const handleSelectLine = useCallback( + (newLineIdToSelect: string) => { + toast('selecting line!! NOT IMPLEMENTED'); + setSelectedLine(realTimeRoomMap[newLineIdToSelect]); + }, + [setSelectedLine, realTimeRoomMap], + ); + return ( - + {children} ); diff --git a/packages/desktop/src/tree/protected/terminal/Terminal.tsx b/packages/desktop/src/tree/protected/terminal/Terminal.tsx index d412979..1305825 100644 --- a/packages/desktop/src/tree/protected/terminal/Terminal.tsx +++ b/packages/desktop/src/tree/protected/terminal/Terminal.tsx @@ -7,32 +7,19 @@ import MasterLineData from '@nirvana/core/models/masterLineData.model'; import useRooms from '../../../providers/RoomsProvider'; import toast from 'react-hot-toast'; +import SidePanel from './panels/SidePanel'; export default function Terminal() { - const { rooms: initialRoomsState } = useRooms(); const { roomsMap } = useRealTimeRooms(); const { desktopMode } = useElectron(); - const { user } = useAuth(); - - const [selectedLine, setSelectedLine] = useState(); - - /** show user line details on click of one line */ - const handleSelectLine = useCallback( - (newLineIdToSelect: string) => { - toast('selecting line!! NOT IMPLEMENTED'); - setSelectedLine(roomsMap[newLineIdToSelect]); - }, - [setSelectedLine], - ); - return (
+ + + + {/* */}
); } - -// have a socket view component to just connect on mount and disconnect from specific | tunes in and untunes similarly -// have a socket view component to just tune into tuned in lines -// have a stream view component to just make peer connections and have stream components available diff --git a/packages/desktop/src/tree/protected/terminal/panels/SidePanel.tsx b/packages/desktop/src/tree/protected/terminal/panels/SidePanel.tsx new file mode 100644 index 0000000..1e679f7 --- /dev/null +++ b/packages/desktop/src/tree/protected/terminal/panels/SidePanel.tsx @@ -0,0 +1,224 @@ +import { LineMemberState } from '@nirvana/core/models/line.model'; +import MasterLineData from '@nirvana/core/models/masterLineData.model'; +import { Avatar, Skeleton, Tooltip } from 'antd'; +import React, { useMemo } from 'react'; +import { FaPlus } from 'react-icons/fa'; +import { FiActivity, FiSun } from 'react-icons/fi'; +import useRealTimeRooms from '../../../../providers/RealTimeRoomProvider'; +import useRooms from '../../../../providers/RoomsProvider'; +import useAuth from '../../../../providers/AuthProvider'; +import LineIcon from '../../../../components/lineIcon'; +import moment from 'moment'; + +export default function SidePanel() { + // using merely for loading state...better to add to realtimeroom context? + const { rooms: initialRoomsFetch } = useRooms(); + + const { roomsMap, handleSelectLine, selectedLine } = useRealTimeRooms(); + + const [toggleTunedLines, allLines] = useMemo(() => { + const masterLines: MasterLineData[] = Object.values(roomsMap); + + const tunedLines: MasterLineData[] = []; + const restLines: MasterLineData[] = []; + + masterLines.forEach((masterLine) => { + if (masterLine.currentUserMember.state === LineMemberState.TUNED) restLines.push(masterLine); + else restLines.push(masterLine); + }); + + return [tunedLines, restLines]; + }, [roomsMap]); + + return ( +
+ {/* tuned in lines block */} +
+ {/* tuned in header + general controls */} + + +
+ + + +

Rooms

+ +

{`${toggleTunedLines?.length || 0}/3`}

+
+
+
+ + {/* list of toggle tuned lines */} +
+ {toggleTunedLines.map((masterLineData) => ( + handleSelectLine(masterLineData.lineDetails._id.toString())} + isSelected={ + selectedLine?.lineDetails._id.toString() === + masterLineData.lineDetails._id.toString() + } + /> + ))} +
+
+ + {!(allLines.length > 0) && !(toggleTunedLines.length > 0) && ( + + You have no lines!
Create one to connect to your team instantly. +
+ )} + + {/* rest of the lines */} +
+ {initialRoomsFetch.loading ? ( + + ) : ( + allLines.map((masterLineData) => ( + handleSelectLine(masterLineData.lineDetails._id.toString())} + isSelected={ + selectedLine?.lineDetails._id.toString() === + masterLineData.lineDetails._id.toString() + } + /> + )) + )} +
+ +
+ +
+
+ ); +} + +function LineRow({ + line, + onClick, + isSelected, +}: { + line: MasterLineData; + onClick: () => void; + isSelected: boolean; +}) { + const { user } = useAuth(); + + const isUserTunedIn = useMemo( + () => line.tunedInMemberIds?.includes(user._id.toString()), + [line.tunedInMemberIds, user], + ); + + /** + * TODO: slowly add to this and fix based on added features + * */ + const renderActivityIcon = useMemo(() => { + // if there is someone or me broadcasting here + if (line.currentBroadcastersUserIds?.length > 0) + return ; + + if (isUserTunedIn) return ; + + // if there is new activity blocks for me + if (line.currentUserMember.lastVisitDate) + return ; + + return ; + }, [line, isUserTunedIn]); + + const renderRightActivity = useMemo(() => { + // TODO: get the profile pictures of the broadcasters + if (line.currentBroadcastersUserIds?.length > 0) + return ( + + {line.otherUserObjects?.map((otherUser, index) => ( + + ))} + + ); + + // if there is new activity/black dot, then show relative time as little bolder? or too much? + + // TODO: compare last visit date to latest audio block + if (line.currentUserMember) + return ( + + {moment(line.currentUserMember.lastVisitDate).fromNow(true)} + + ); + + return ( + + {moment(line.currentUserMember.lastVisitDate).fromNow(true)} + + ); + }, [line]); + + const profilePictures = useMemo(() => { + const pictureSources: string[] = []; + + // ?don't add in my image as that's useless contextually? + // if (userData?.user?.picture) pictureSources.push(userData.user.picture); + + line.otherUserObjects?.forEach((otherUser) => { + if (otherUser.picture) pictureSources.push(otherUser.picture); + }); + + return pictureSources; + }, [line, user]); + + return ( +
onClick()} + role={'presentation'} + className={`flex flex-row items-center justify-start gap-2 p-2 px-4 h-14 hover:bg-gray-200 cursor-pointer transition-all +last:border-b-0 border-b border-b-gray-200 relative z-50 rounded ${ + isSelected && 'bg-gray-200 scale-110 shadow-2xl translate-x-3' + }`} + > + {/* status dot */} +
{renderActivityIcon}
+ + {profilePictures && } + +

+ {line.lineDetails.name || line.otherUserObjects[0].givenName} +

+ +
{renderRightActivity}
+
+ ); +}