mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add live users avatar indicators
This commit is contained in:
parent
54718d7111
commit
6a47202c78
5 changed files with 100 additions and 15 deletions
67
apps/web/components/Objects/Editor/ActiveAvatars.tsx
Normal file
67
apps/web/components/Objects/Editor/ActiveAvatars.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import UserAvatar from '../UserAvatar'
|
||||||
|
import { useSession } from '@components/Contexts/SessionContext'
|
||||||
|
import { getUserAvatarMediaDirectory } from '@services/media/media';
|
||||||
|
import { getCollaborationServerUrl } from '@services/config/config';
|
||||||
|
import { useOrg } from '@components/Contexts/OrgContext';
|
||||||
|
|
||||||
|
type ActiveAvatarsProps = {
|
||||||
|
mouseMovements: any;
|
||||||
|
userRandomColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActiveAvatars(props: ActiveAvatarsProps) {
|
||||||
|
const session = useSession() as any;
|
||||||
|
const org = useOrg() as any;
|
||||||
|
const [activeUsers, setActiveUsers] = useState({} as any);
|
||||||
|
|
||||||
|
/* Collaboration Features */
|
||||||
|
const collab = getCollaborationServerUrl()
|
||||||
|
const isCollabEnabledOnThisOrg = org?.config.config.GeneralConfig.collaboration && collab
|
||||||
|
|
||||||
|
// Get users from the mouseMovements object
|
||||||
|
useEffect(() => {
|
||||||
|
const users: any = {};
|
||||||
|
Object.keys(props.mouseMovements).forEach((key) => {
|
||||||
|
users[props.mouseMovements[key].user.user_uuid] = props.mouseMovements[key].user;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the current user from the list
|
||||||
|
delete users[session.user.user_uuid];
|
||||||
|
|
||||||
|
setActiveUsers(users);
|
||||||
|
}
|
||||||
|
, [props.mouseMovements, session.user, org]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className=''>
|
||||||
|
|
||||||
|
<div className='flex -space-x-2 transition-all ease-linear'>
|
||||||
|
{isCollabEnabledOnThisOrg && Object.keys(activeUsers).map((key) => (
|
||||||
|
<div className='flex' style={{ position: 'relative' }} key={key}>
|
||||||
|
<UserAvatar
|
||||||
|
key={key}
|
||||||
|
width={40}
|
||||||
|
border="border-4"
|
||||||
|
rounded="rounded-full"
|
||||||
|
avatar_url={getUserAvatarMediaDirectory(activeUsers[key].user_uuid, activeUsers[key].avatar_image) as string}
|
||||||
|
/>
|
||||||
|
<div className="h-2 w-2 rounded-full" style={{ position: 'absolute', bottom: -5, right: 16, backgroundColor: props.mouseMovements[key].color }} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{session.isAuthenticated && (
|
||||||
|
<div className='z-50'>
|
||||||
|
<UserAvatar
|
||||||
|
width={40}
|
||||||
|
border="border-4"
|
||||||
|
rounded="rounded-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActiveAvatars
|
||||||
|
|
@ -48,6 +48,7 @@ import UserAvatar from '../UserAvatar'
|
||||||
import randomColor from 'randomcolor'
|
import randomColor from 'randomcolor'
|
||||||
import Collaboration from '@tiptap/extension-collaboration'
|
import Collaboration from '@tiptap/extension-collaboration'
|
||||||
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
|
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
|
||||||
|
import ActiveAvatars from './ActiveAvatars'
|
||||||
|
|
||||||
interface Editor {
|
interface Editor {
|
||||||
content: string
|
content: string
|
||||||
|
|
@ -59,6 +60,7 @@ interface Editor {
|
||||||
hocuspocusProvider: any,
|
hocuspocusProvider: any,
|
||||||
isCollabEnabledOnThisOrg: boolean
|
isCollabEnabledOnThisOrg: boolean
|
||||||
userRandomColor: string
|
userRandomColor: string
|
||||||
|
mouseMovements: any
|
||||||
setContent: (content: string) => void
|
setContent: (content: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,13 +139,14 @@ function Editor(props: Editor) {
|
||||||
...(props.isCollabEnabledOnThisOrg ? [
|
...(props.isCollabEnabledOnThisOrg ? [
|
||||||
Collaboration.configure({
|
Collaboration.configure({
|
||||||
document: props.hocuspocusProvider?.document,
|
document: props.hocuspocusProvider?.document,
|
||||||
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
CollaborationCursor.configure({
|
CollaborationCursor.configure({
|
||||||
provider: props.hocuspocusProvider,
|
provider: props.hocuspocusProvider,
|
||||||
user: {
|
user: {
|
||||||
name: props.session.user.first_name + ' ' + props.session.user.last_name,
|
name: props.session.user.first_name + ' ' + props.session.user.last_name,
|
||||||
color: props.userRandomColor ,
|
color: props.userRandomColor,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
] : []),
|
] : []),
|
||||||
|
|
@ -267,14 +270,7 @@ function Editor(props: Editor) {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<EditorUserProfileWrapper>
|
<EditorUserProfileWrapper>
|
||||||
{!session.isAuthenticated && <span>Loading</span>}
|
<ActiveAvatars userRandomColor={props.userRandomColor} mouseMovements={props.mouseMovements} />
|
||||||
{session.isAuthenticated && (
|
|
||||||
<UserAvatar
|
|
||||||
width={40}
|
|
||||||
border="border-4"
|
|
||||||
rounded="rounded-full"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</EditorUserProfileWrapper>
|
</EditorUserProfileWrapper>
|
||||||
</EditorUsersSection>
|
</EditorUsersSection>
|
||||||
</EditorTop>
|
</EditorTop>
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,14 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
document: doc,
|
document: doc,
|
||||||
|
|
||||||
// TODO(alpha code): This whole block of code should be improved to something more efficient and less hacky
|
// TODO(alpha code): This whole block of code should be improved to something more efficient and less hacky
|
||||||
|
onConnect: () => {
|
||||||
|
// Set the online page instance ID
|
||||||
|
setOnlinePageInstanceID(uuidv4());
|
||||||
|
|
||||||
|
// Set the user color
|
||||||
|
setThisPageColor(randomColor({ luminosity: 'light' }) as string);
|
||||||
|
},
|
||||||
|
|
||||||
onAwarenessUpdate: ({ states }) => {
|
onAwarenessUpdate: ({ states }) => {
|
||||||
const usersStates = states;
|
const usersStates = states;
|
||||||
/* Showing user mouse movement */
|
/* Showing user mouse movement */
|
||||||
|
|
@ -121,6 +129,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -162,7 +171,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toast></Toast>
|
<Toast></Toast>
|
||||||
<MouseMovements movements={mouseMovements} onlinePageInstanceID={onlinePageInstanceID} />
|
<MouseMovements org={props.org} movements={mouseMovements} onlinePageInstanceID={onlinePageInstanceID} />
|
||||||
<OrgProvider orgslug={props.org.slug}>
|
<OrgProvider orgslug={props.org.slug}>
|
||||||
{!session.isLoading && (<Editor
|
{!session.isLoading && (<Editor
|
||||||
org={props.org}
|
org={props.org}
|
||||||
|
|
@ -175,6 +184,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
hocuspocusProvider={provider}
|
hocuspocusProvider={provider}
|
||||||
isCollabEnabledOnThisOrg={isCollabEnabledOnThisOrg}
|
isCollabEnabledOnThisOrg={isCollabEnabledOnThisOrg}
|
||||||
userRandomColor={thisPageColor}
|
userRandomColor={thisPageColor}
|
||||||
|
mouseMovements={mouseMovements}
|
||||||
></Editor>)}
|
></Editor>)}
|
||||||
</OrgProvider>
|
</OrgProvider>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { m, motion } from "framer-motion";
|
import { getCollaborationServerUrl } from "@services/config/config";
|
||||||
import React from 'react'
|
import { motion } from "framer-motion";
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
user_uuid: string;
|
user_uuid: string;
|
||||||
|
|
@ -18,12 +19,24 @@ interface Movement {
|
||||||
interface MouseMovementsProps {
|
interface MouseMovementsProps {
|
||||||
movements: Record<string, Movement>;
|
movements: Record<string, Movement>;
|
||||||
onlinePageInstanceID: string;
|
onlinePageInstanceID: string;
|
||||||
|
org ?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MouseMovements({ movements, onlinePageInstanceID }: MouseMovementsProps): JSX.Element {
|
function MouseMovements({ movements, onlinePageInstanceID, org }: MouseMovementsProps): JSX.Element {
|
||||||
|
|
||||||
|
|
||||||
|
/* Collaboration config */
|
||||||
|
const collab = getCollaborationServerUrl()
|
||||||
|
const isCollabEnabledOnThisOrg = org?.config.config.GeneralConfig.collaboration && collab
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
}
|
||||||
|
, [movements, org]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{Object.keys(movements).map((key) => (
|
{isCollabEnabledOnThisOrg && Object.keys(movements).map((key) => (
|
||||||
movements[key].onlinePageInstanceID !== onlinePageInstanceID && (<motion.div
|
movements[key].onlinePageInstanceID !== onlinePageInstanceID && (<motion.div
|
||||||
key={key}
|
key={key}
|
||||||
className="flex -space-x-2"
|
className="flex -space-x-2"
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ function UserAvatar(props: UserAvatarProps) {
|
||||||
return predefinedAvatar
|
return predefinedAvatar
|
||||||
} else {
|
} else {
|
||||||
if (props.avatar_url) {
|
if (props.avatar_url) {
|
||||||
console.log('avatar_url', props.avatar_url)
|
|
||||||
return props.avatar_url
|
return props.avatar_url
|
||||||
} else {
|
} else {
|
||||||
if (session.user.avatar_image) {
|
if (session.user.avatar_image) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue