fix: mixing clientId & socketId in UserList (#7461)

This commit is contained in:
David Luzar 2023-12-18 18:21:57 +01:00 committed by GitHub
parent 0808532b49
commit 57ea4e61d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 46 additions and 39 deletions

View file

@ -12,6 +12,7 @@ export const actionGoToCollaborator = register({
trackEvent: { category: "collab" },
perform: (_elements, appState, collaborator: Collaborator) => {
if (
!collaborator.socketId ||
appState.userToFollow?.socketId === collaborator.socketId ||
collaborator.isCurrentUser
) {
@ -28,7 +29,7 @@ export const actionGoToCollaborator = register({
appState: {
...appState,
userToFollow: {
socketId: collaborator.socketId!,
socketId: collaborator.socketId,
username: collaborator.username || "",
},
// Close mobile menu

View file

@ -3472,7 +3472,6 @@ class App extends React.Component<AppProps, AppState> {
};
private maybeUnfollowRemoteUser = () => {
console.warn("maybeUnfollowRemoteUser");
if (this.state.userToFollow) {
this.setState({ userToFollow: null });
}

View file

@ -3,6 +3,7 @@ import { LaserPointer } from "@excalidraw/laser-pointer";
import { sceneCoordsToViewportCoords } from "../../utils";
import App from "../App";
import { getClientColor } from "../../clients";
import { SocketId } from "../../types";
// decay time in milliseconds
const DECAY_TIME = 1000;
@ -88,7 +89,7 @@ type CollabolatorState = {
export class LaserPathManager {
private ownState: CollabolatorState;
private collaboratorsState: Map<string, CollabolatorState> = new Map();
private collaboratorsState: Map<SocketId, CollabolatorState> = new Map();
private rafId: number | undefined;
private isDrawing = false;

View file

@ -14,51 +14,54 @@ import { t } from "../i18n";
import { isShallowEqual } from "../utils";
export type GoToCollaboratorComponentProps = [
SocketId,
ClientId,
Collaborator,
boolean,
boolean,
];
/** collaborator user id or socket id (fallback) */
type ClientId = string & { _brand: "UserId" };
const FIRST_N_AVATARS = 3;
const SHOW_COLLABORATORS_FILTER_AT = 8;
const ConditionalTooltipWrapper = ({
shouldWrap,
children,
socketId,
clientId,
username,
}: {
shouldWrap: boolean;
children: React.ReactNode;
username?: string | null;
socketId: string;
clientId: ClientId;
}) =>
shouldWrap ? (
<Tooltip label={username || "Unknown user"} key={socketId}>
<Tooltip label={username || "Unknown user"} key={clientId}>
{children}
</Tooltip>
) : (
<React.Fragment key={socketId}>{children}</React.Fragment>
<React.Fragment key={clientId}>{children}</React.Fragment>
);
const renderCollaborator = ({
actionManager,
collaborator,
socketId,
clientId,
withName = false,
shouldWrapWithTooltip = false,
isBeingFollowed,
}: {
actionManager: ActionManager;
collaborator: Collaborator;
socketId: string;
clientId: ClientId;
withName?: boolean;
shouldWrapWithTooltip?: boolean;
isBeingFollowed: boolean;
}) => {
const data: GoToCollaboratorComponentProps = [
socketId,
clientId,
collaborator,
withName,
isBeingFollowed,
@ -67,8 +70,8 @@ const renderCollaborator = ({
return (
<ConditionalTooltipWrapper
key={socketId}
socketId={socketId}
key={clientId}
clientId={clientId}
username={collaborator.username}
shouldWrap={shouldWrapWithTooltip}
>
@ -100,12 +103,13 @@ export const UserList = React.memo(
({ className, mobile, collaborators, userToFollow }: UserListProps) => {
const actionManager = useExcalidrawActionManager();
const uniqueCollaboratorsMap = new Map<string, Collaborator>();
const uniqueCollaboratorsMap = new Map<ClientId, Collaborator>();
collaborators.forEach((collaborator, socketId) => {
const userId = (collaborator.id || socketId) as ClientId;
uniqueCollaboratorsMap.set(
// filter on user id, else fall back on unique socketId
collaborator.id || socketId,
userId,
{ ...collaborator, socketId },
);
});
@ -134,25 +138,25 @@ export const UserList = React.memo(
);
const firstNAvatarsJSX = firstNCollaborators.map(
([socketId, collaborator]) =>
([clientId, collaborator]) =>
renderCollaborator({
actionManager,
collaborator,
socketId,
clientId,
shouldWrapWithTooltip: true,
isBeingFollowed: socketId === userToFollow,
isBeingFollowed: collaborator.socketId === userToFollow,
}),
);
return mobile ? (
<div className={clsx("UserList UserList_mobile", className)}>
{uniqueCollaboratorsArray.map(([socketId, collaborator]) =>
{uniqueCollaboratorsArray.map(([clientId, collaborator]) =>
renderCollaborator({
actionManager,
collaborator,
socketId,
clientId,
shouldWrapWithTooltip: true,
isBeingFollowed: socketId === userToFollow,
isBeingFollowed: collaborator.socketId === userToFollow,
}),
)}
</div>
@ -205,13 +209,13 @@ export const UserList = React.memo(
<div className="UserList__hint">
{t("userList.hint.text")}
</div>
{filteredCollaborators.map(([socketId, collaborator]) =>
{filteredCollaborators.map(([clientId, collaborator]) =>
renderCollaborator({
actionManager,
collaborator,
socketId,
clientId,
withName: true,
isBeingFollowed: socketId === userToFollow,
isBeingFollowed: collaborator.socketId === userToFollow,
}),
)}
</div>

View file

@ -41,7 +41,7 @@ import { Merge, ValueOf } from "./utility-types";
export type Point = Readonly<RoughPoint>;
export type SocketId = string;
export type SocketId = string & { _brand: "SocketId" };
export type Collaborator = Readonly<{
pointer?: CollaboratorPointer;
@ -128,7 +128,7 @@ export type SidebarName = string;
export type SidebarTabName = string;
export type UserToFollow = {
socketId: string;
socketId: SocketId;
username: string;
};
@ -296,7 +296,7 @@ export interface AppState {
offsetLeft: number;
fileHandle: FileSystemHandle | null;
collaborators: Map<string, Collaborator>;
collaborators: Map<SocketId, Collaborator>;
showStats: boolean;
currentChartType: ChartType;
pasteDialog:
@ -321,7 +321,7 @@ export interface AppState {
/** the user's clientId & username who is being followed on the canvas */
userToFollow: UserToFollow | null;
/** the clientIds of the users following the current user */
followedBy: Set<string>;
followedBy: Set<SocketId>;
}
export type UIAppState = Omit<
@ -474,7 +474,7 @@ export interface ExcalidrawProps {
export type SceneData = {
elements?: ImportedDataState["elements"];
appState?: ImportedDataState["appState"];
collaborators?: Map<string, Collaborator>;
collaborators?: Map<SocketId, Collaborator>;
commitToHistory?: boolean;
};