Skip to content

Commit

Permalink
Added livekit setup for doctor connect
Browse files Browse the repository at this point in the history
  • Loading branch information
khavinshankar committed Sep 29, 2024
1 parent 5534762 commit 1159011
Show file tree
Hide file tree
Showing 11 changed files with 675 additions and 1,230 deletions.
1,635 changes: 447 additions & 1,188 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
"@googlemaps/react-wrapper": "^1.1.35",
"@googlemaps/typescript-guards": "^2.0.3",
"@headlessui/react": "^2.1.2",
"@livekit/components-react": "^0.4.1",
"@livekit/react-components": "^1.1.0",
"@loadable/component": "^5.16.4",
"@pnotify/core": "^5.2.0",
"@pnotify/mobile": "^5.2.0",
"@sentry/browser": "^8.29.0",
Expand All @@ -64,6 +67,7 @@
"hi-profiles": "^1.0.6",
"i18next": "^23.11.4",
"i18next-browser-languagedetector": "^7.2.1",
"livekit-client": "^1.6.8",
"lodash-es": "^4.17.21",
"postcss-loader": "^7.3.3",
"qrcode.react": "^3.1.0",
Expand Down Expand Up @@ -94,6 +98,7 @@
"@types/cypress": "^1.1.3",
"@types/events": "^3.0.3",
"@types/google.maps": "^3.55.8",
"@types/loadable__component": "^5.13.9",
"@types/lodash-es": "^4.17.12",
"@types/qrcode.react": "^1.0.5",
"@types/react": "18.3.2",
Expand Down Expand Up @@ -155,4 +160,4 @@
"node": ">=20.12.0"
},
"packageManager": "[email protected]"
}
}
10 changes: 0 additions & 10 deletions src/Common/hooks/useMergeState.js

This file was deleted.

10 changes: 10 additions & 0 deletions src/Common/hooks/useMergeState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useState } from "react";

export default function useMergeState(initialState: any) {
const [state, setState] = useState(initialState);

const setMergedState = (newState: any) =>
setState((prevState: any) => Object.assign({}, prevState, newState));

return [state, setMergedState];
}
74 changes: 74 additions & 0 deletions src/Components/Common/Livekit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import "@livekit/react-components/dist/index.css";

import { useEffect, useState } from "react";

import ButtonV2 from "./components/ButtonV2";
import { LiveKitRoom } from "@livekit/react-components";
import request from "../../Utils/request/request";
import routes from "../../Redux/api";

export const Livekit = (props: {
sourceUsername: string;
targetUsername: string;
}) => {
const [status, setStatus] = useState("Disconnected");
const [connect, setConnect] = useState(false);
const [token, setToken] = useState("");

const getToken = async () => {
const { res, data } = await request(routes.livekit.create_room, {
body: {
source: props.sourceUsername,
target: props.targetUsername,
},
});

if (res?.status === 201 && data) {
setToken(data.access);
}
};

useEffect(() => {
getToken();
}, []);

return (
<div className="roomContainer">
<p className="text-md">Welcome {props.sourceUsername} !</p>
<p className="font-semibold text-md my-4">
Status:{" "}
<span
className={status === "Connected" ? "text-green-600" : "text-red-500"}
>
{status}
</span>
</p>
<ButtonV2
onClick={() => {
setConnect(!connect);
if (status != "Connected") {
setStatus("Connecting...");
} else {
setStatus("Disconnected");
}
}}
variant={status === "Connected" ? "danger" : "primary"}
>
{status === "Connected" ? "Disconnect" : "Connect"}
</ButtonV2>
{connect && token && (
<LiveKitRoom
token={token}
url="wss://care-22km03y2.livekit.cloud"
onConnected={() => {
setStatus("Connected");
}}
onLeave={() => {
setStatus("Disconnected");
setConnect(false);
}}
/>
)}
</div>
);
};
57 changes: 57 additions & 0 deletions src/Components/Facility/DoctorLiveConnect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Livekit } from "../Common/Livekit";
import loadable from "@loadable/component";
import routes from "../../Redux/api";
import useAuthUser from "../../Common/hooks/useAuthUser";
import useQuery from "../../Utils/request/useQuery";
import { useState } from "react";

const PageTitle = loadable(() => import("../Common/PageTitle"));

const DoctorLiveConnect = (props: { facilityId: string; userId: string }) => {
const { facilityId, userId } = props;
const [user, setUser] = useState<any>(null);
const currentUser = useAuthUser();

useQuery(routes.getFacilityUsers, {
pathParams: { facility_id: facilityId },
query: { limit: 50 },
onResponse: (res) => {
setUser(res.data?.results.find((user: any) => user.id == userId));
},
});

return (
user && (
<div className="px-2 pb-2">
<PageTitle title="Doctor Live Connect" />
<div className="flex flex-col xl:flex-row gap-8">
<div className="bg-white rounded-lg md:rounded-xl w-full flex service-panel">
<div className="w-full md:p-8 md:pt-6 p-6 pt-4 flex flex-col justify-between gap-6">
<div>
<div className="flex flex-wrap items-center gap-2 justify-between w-full">
<div className="flex items-center gap-3">
<span className="text-2xl md:text-3xl font-bold break-words">
Connect to Doctor {user.first_name} {user.last_name}
</span>
</div>
</div>
<div className="mt-8">
<Livekit
sourceUsername={currentUser.username}
targetUsername={
user.username === currentUser.username
? "devdistrictadmin"
: user.username
}
/>
</div>
</div>
</div>
</div>
</div>
</div>
)
);
};

export default DoctorLiveConnect;
55 changes: 43 additions & 12 deletions src/Components/Facility/DoctorVideoSlideover.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import React, { useState } from "react";
import SlideOver from "../../CAREUI/interactive/SlideOver";
import { UserAssignedModel } from "../Users/models";
import { SkillObjectModel } from "../Users/models";
import CareIcon, { IconName } from "../../CAREUI/icons/CareIcon";
import React, { useState } from "react";
import {
classNames,
formatName,
isUserOnline,
relativeTime,
} from "../../Utils/utils";
import useAuthUser from "../../Common/hooks/useAuthUser";
import { triggerGoal } from "../../Integrations/Plausible";
import { Warn } from "../../Utils/Notifications";

import { Link } from "raviger";
import Loading from "../Common/Loading";
import { SkillObjectModel } from "../Users/models";
import SlideOver from "../../CAREUI/interactive/SlideOver";
import Switch from "../../CAREUI/interactive/Switch";
import useQuery from "../../Utils/request/useQuery";
import { UserAssignedModel } from "../Users/models";
import { Warn } from "../../Utils/Notifications";
import routes from "../../Redux/api";
import Loading from "../Common/Loading";
import { triggerGoal } from "../../Integrations/Plausible";
import useAuthUser from "../../Common/hooks/useAuthUser";
import useQuery from "../../Utils/request/useQuery";

const UserGroups = {
ALL: "All",
Expand Down Expand Up @@ -97,24 +99,31 @@ export default function DoctorVideoSlideover(props: {
<UserGroupList
group="DOCTOR"
users={annotatedUsers}
facilityId={facilityId}
showGroupHeading
/>

<UserGroupList
group="NURSE"
users={annotatedUsers}
facilityId={facilityId}
showGroupHeading
/>

<UserGroupList
group="TELEICU"
users={annotatedUsers}
facilityId={facilityId}
showGroupHeading
/>
</div>
) : (
<div className="py-6">
<UserGroupList group={filter} users={annotatedUsers} />
<UserGroupList
group={filter}
users={annotatedUsers}
facilityId={facilityId}
/>
</div>
)}
</SlideOver>
Expand All @@ -124,6 +133,7 @@ export default function DoctorVideoSlideover(props: {
const UserGroupList = (props: {
users: UserAnnotatedWithGroup[];
group: UserGroup;
facilityId: string;
showGroupHeading?: boolean;
}) => {
const users = props.users.filter((user) => user.group === props.group);
Expand Down Expand Up @@ -155,7 +165,7 @@ const UserGroupList = (props: {
user.group !== "TELEICU" ? "home" : "remote"
}-${user.user_type.toLowerCase()}`}
>
<UserListItem user={user} />
<UserListItem user={user} facilityId={props.facilityId} />
</li>
))}
</ul>
Expand All @@ -170,7 +180,13 @@ type MSLaunchURI = (
noHandlerCB?: null | (() => void),
) => void;

function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
function UserListItem({
user,
facilityId,
}: {
user: UserAnnotatedWithGroup;
facilityId: string;
}) {
const icon: IconName =
user.user_type === "Doctor" ? "l-user-md" : "l-user-nurse";

Expand Down Expand Up @@ -267,6 +283,7 @@ function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
<DoctorConnectButtons
user={user}
connectOnWhatsApp={connectOnWhatsApp}
facilityId={facilityId}
/>
</div>
{!!user.skills.length && (
Expand Down Expand Up @@ -317,6 +334,7 @@ function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
function DoctorConnectButtons(props: {
user: UserAssignedModel;
connectOnWhatsApp: (e: React.MouseEvent<HTMLAnchorElement>) => void;
facilityId: string;
}) {
const user = props.user;
const authUser = useAuthUser();
Expand Down Expand Up @@ -350,6 +368,19 @@ function DoctorConnectButtons(props: {
<CareIcon icon="l-whatsapp" className="h-5 w-5" />
</div>
</a>
<Link
onClick={(e) => {
e.stopPropagation();
}}
href={`/facility/${props.facilityId}/live_connect/${user.id}/`}
target="_blank"
rel="noopener noreferrer"
>
<div className="tooltip">
<span className="tooltip-text tooltip-left">Connect on Care</span>
<CareIcon icon="l-video" className="w-5 h-5" />
</div>
</Link>
<a
href={user.alt_phone_number ? `tel:${user.alt_phone_number}` : "#"}
onClick={(e) => {
Expand Down
9 changes: 9 additions & 0 deletions src/Redux/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ export interface LoginCredentials {
}

const routes = {
livekit: {
create_room: {
path: "/api/livekit/create_room/",
method: "POST",
TBody: Type<{ source: string; target: string }>(),
TRes: Type<{ room_code: string; access: string }>(),
},
},

createScribe: {
path: "/api/care_scribe/scribe/",
method: "POST",
Expand Down
Loading

0 comments on commit 1159011

Please sign in to comment.