diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..37f8642
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @cpsiaki @LinaBell @liebeskind
\ No newline at end of file
diff --git a/client/src/components/DottedLoader.tsx b/client/src/components/DottedLoader.tsx
index dcda45e..b2a4ce0 100644
--- a/client/src/components/DottedLoader.tsx
+++ b/client/src/components/DottedLoader.tsx
@@ -1,7 +1,11 @@
export const DottedLoader: React.FC = () => {
return (
-
-

+
+
);
};
diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx
index 9f56fea..914bb34 100644
--- a/client/src/components/Header.tsx
+++ b/client/src/components/Header.tsx
@@ -1,5 +1,3 @@
-import React from "react";
-
const Header = () => {
return (
<>
diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx
index cc6155b..d6e120e 100644
--- a/client/src/pages/Home.tsx
+++ b/client/src/pages/Home.tsx
@@ -12,7 +12,7 @@ import Header from "@/components/Header";
const Home: React.FC = () => {
const dispatch = useContext(GlobalDispatchContext);
- const { hasInteractiveParams, isAdmin, backendAPI, initLoading, sessionData } = useContext(GlobalStateContext);
+ const { isAdmin, backendAPI, initLoading, sessionData } = useContext(GlobalStateContext);
const [endLoading, setEndLoading] = useState(false);
diff --git a/client/src/pages/Instructions.tsx b/client/src/pages/Instructions.tsx
index 0792424..965b0d0 100644
--- a/client/src/pages/Instructions.tsx
+++ b/client/src/pages/Instructions.tsx
@@ -1,5 +1,4 @@
import Header from "@/components/Header";
-import React from "react";
import { Link } from "react-router-dom";
const Instructions = () => {
diff --git a/client/src/utils/backendAPI.ts b/client/src/utils/backendAPI.ts
index 5e3b4e3..3262f74 100644
--- a/client/src/utils/backendAPI.ts
+++ b/client/src/utils/backendAPI.ts
@@ -1,5 +1,5 @@
-import axios from 'axios';
-import { InteractiveParams } from '../context/types';
+import axios, { InternalAxiosRequestConfig } from "axios";
+import { InteractiveParams } from "../context/types";
const setupBackendAPI = async (interactiveParams: InteractiveParams) => {
const backendAPI = axios.create({
@@ -11,7 +11,7 @@ const setupBackendAPI = async (interactiveParams: InteractiveParams) => {
// Only do this if have interactive nonce.
if (interactiveParams.assetId) {
- backendAPI.interceptors.request.use((config: any) => {
+ backendAPI.interceptors.request.use((config: InternalAxiosRequestConfig) => {
if (!config?.params) config.params = {};
config.params = { ...config.params };
config.params["assetId"] = interactiveParams.assetId;
diff --git a/package-lock.json b/package-lock.json
index 0563151..c3be1e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1057,9 +1057,9 @@
]
},
"node_modules/@rtsdk/topia": {
- "version": "0.12.6",
- "resolved": "https://registry.npmjs.org/@rtsdk/topia/-/topia-0.12.6.tgz",
- "integrity": "sha512-YNMoz0Y/xZ1vZwFs+C+aXg8Qd7Z/QR32i5tIdYzbCUSxC173WgcyMmTW6cAy1RDFNxZQgPmH2H4ehieIpUxlJA=="
+ "version": "0.15.7",
+ "resolved": "https://registry.npmjs.org/@rtsdk/topia/-/topia-0.15.7.tgz",
+ "integrity": "sha512-n63tuxROfbP8Y53IipAshbE9iytNAwRphCylP4GjauyJkmOfzBlorvWJIwbqAjXqxEyd2t70DrzYPtMcOdzz4A=="
},
"node_modules/@swc/core": {
"version": "1.4.11",
@@ -5610,7 +5610,7 @@
"name": "@breakout/server",
"version": "0.0.0",
"dependencies": {
- "@rtsdk/topia": "^0.12.6",
+ "@rtsdk/topia": "^0.15.7",
"axios": "^1.6.8",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
diff --git a/server/controllers/session/handleGetDataObject.ts b/server/controllers/session/handleGetDataObject.ts
index e328fa0..a8c9d89 100644
--- a/server/controllers/session/handleGetDataObject.ts
+++ b/server/controllers/session/handleGetDataObject.ts
@@ -1,4 +1,4 @@
-import { World, WorldActivity, errorHandler, getCredentials, getDroppedAsset } from "../../utils/index.js";
+import { WorldActivity, errorHandler, getCredentials, getDroppedAsset } from "../../utils/index.js";
import { Request, Response } from "express";
export default async function handleGetDataObject(req: Request, res: Response) {
@@ -10,13 +10,7 @@ export default async function handleGetDataObject(req: Request, res: Response) {
return res.status(404).json({ message: "Asset not found" });
}
- const worldActivity = WorldActivity.create(credentials.urlSlug, {
- credentials: {
- interactiveNonce: credentials.interactiveNonce,
- interactivePublicKey: credentials.interactivePublicKey,
- visitorId: credentials.visitorId,
- },
- });
+ const worldActivity = WorldActivity.create(credentials.urlSlug, { credentials });
const visitors = await worldActivity.fetchVisitorsInZone({ droppedAssetId: keyAsset.dataObject.landmarkZoneId });
const visitorProfileIds = Object.values(visitors).map((visitor) => visitor.profileId);
diff --git a/server/controllers/session/handleGetParticipantsInZone.ts b/server/controllers/session/handleGetParticipantsInZone.ts
index 4031111..dfd15f0 100644
--- a/server/controllers/session/handleGetParticipantsInZone.ts
+++ b/server/controllers/session/handleGetParticipantsInZone.ts
@@ -9,13 +9,7 @@ export default async function handleGetParticipantsInZone(req: Request, res: Res
return res.status(404).json({ message: "Asset not found" });
}
- const worldActivity = WorldActivity.create(credentials.urlSlug, {
- credentials: {
- interactiveNonce: credentials.interactiveNonce,
- interactivePublicKey: credentials.interactivePublicKey,
- visitorId: credentials.visitorId,
- },
- });
+ const worldActivity = WorldActivity.create(credentials.urlSlug, { credentials });
const visitors = await worldActivity.fetchVisitorsInZone({ droppedAssetId: keyAsset.dataObject.landmarkZoneId });
const participants = Object.values(visitors).map(({ profileId, username }) => {
return {
diff --git a/server/controllers/session/handleResetSession.ts b/server/controllers/session/handleResetSession.ts
index 72b7b3c..a0a0ac8 100644
--- a/server/controllers/session/handleResetSession.ts
+++ b/server/controllers/session/handleResetSession.ts
@@ -1,4 +1,3 @@
-import { Credentials } from "../../types/index.js";
import { WorldActivity, defaultDataObject, errorHandler, getCredentials, getDroppedAsset } from "../../utils/index.js";
import { Request, Response } from "express";
import { endBreakout } from "./handleSetBreakoutConfig.js";
@@ -7,13 +6,7 @@ import closeIframeForVisitors from "../../utils/session/closeIframeForVisitors.j
export default async function handleResetSession(req: Request, res: Response) {
try {
const credentials = getCredentials(req.query);
- const worldActivity = WorldActivity.create(credentials.urlSlug, {
- credentials: {
- interactiveNonce: credentials.interactiveNonce,
- interactivePublicKey: credentials.interactivePublicKey,
- visitorId: credentials.visitorId,
- },
- });
+ const worldActivity = WorldActivity.create(credentials.urlSlug, { credentials });
const keyAsset = await getDroppedAsset(credentials);
const visitors = await worldActivity.fetchVisitorsInZone({ droppedAssetId: keyAsset.dataObject.landmarkZoneId });
diff --git a/server/controllers/session/handleSetBreakoutConfig.ts b/server/controllers/session/handleSetBreakoutConfig.ts
index 8b991f8..f3662f6 100644
--- a/server/controllers/session/handleSetBreakoutConfig.ts
+++ b/server/controllers/session/handleSetBreakoutConfig.ts
@@ -1,4 +1,4 @@
-import { DroppedAsset, WorldActivity as IWorldActivity, Visitor } from "@rtsdk/topia";
+import { DroppedAsset, DroppedAssetInterface, Visitor, VisitorInterface, WorldActivityType } from "@rtsdk/topia";
import { AnalyticType, Credentials } from "../../types/index.js";
import { getDroppedAssetsBySceneDropId } from "../../utils/droppedAssets/getDroppedAssetsBySceneDropId.js";
import { World, WorldActivity, errorHandler, getCredentials, getDroppedAsset } from "../../utils/index.js";
@@ -41,13 +41,14 @@ export const endBreakout = (key: string) => {
};
export const updateAdminCredentials = (credentials: Credentials) => {
- const session = Object.entries(breakouts).find(([_, data]) => data.landmarkZoneId === credentials.assetId);
- if (session && session[1].adminProfileId === credentials.profileId) {
+ const { assetId, profileId, interactiveNonce } = credentials;
+ const session = Object.entries(breakouts).find(([_, data]) => data.landmarkZoneId === assetId);
+ if (session && session[1].adminProfileId === profileId) {
const [key, _] = session as [string, Breakouts[string]];
if (
- breakouts[key].adminProfileId === credentials.profileId &&
- breakouts[key].adminOriginalInteractiveNonce !== credentials.interactiveNonce
+ breakouts[key].adminProfileId === profileId &&
+ breakouts[key].adminOriginalInteractiveNonce !== interactiveNonce
) {
breakouts[key].adminCredentials = { ...credentials, assetId: key };
}
@@ -77,7 +78,7 @@ const getAnalytics = (includedVisitors: Visitor[], matches: string[][], urlSlug:
uniqueKey: visitor.profileId as string,
};
});
-
+
const groupSizeAnalytics: { [key: string]: AnalyticType } = {};
matches.forEach((match) => {
const analyticName = `groupsOf${match.length}`;
@@ -98,6 +99,7 @@ const getAnalytics = (includedVisitors: Visitor[], matches: string[][], urlSlug:
export default async function handleSetBreakoutConfig(req: Request, res: Response) {
try {
const credentials = getCredentials(req.query);
+ const { assetId, profileId, interactiveNonce, sceneDropId, urlSlug } = credentials;
const numOfGroups = Math.min(parseInt(req.body.numOfGroups), 16);
const numOfRounds = Math.min(parseInt(req.body.numOfRounds), 25);
@@ -115,28 +117,22 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
numOfGroups < 1 ||
numOfRounds < 1
) {
- console.log(`Invalid configuration for ${credentials.assetId}`);
+ console.log(`Invalid configuration for ${assetId}`);
return res.status(400).json({ message: "Invalid configuration" });
}
const [keyAsset, breakoutScene]: [IDroppedAsset, DroppedAsset[]] = await Promise.all([
getDroppedAsset(credentials),
- getDroppedAssetsBySceneDropId(credentials, credentials.sceneDropId),
+ getDroppedAssetsBySceneDropId(credentials, sceneDropId),
]);
const privateZonesAtStart = breakoutScene.filter(
- (droppedAsset: DroppedAsset) => droppedAsset.isPrivateZone,
+ (droppedAsset: DroppedAssetInterface) => droppedAsset.isPrivateZone,
) as DroppedAsset[];
const landmarkZone = breakoutScene.find(
- (droppedAsset: DroppedAsset) => droppedAsset.isLandmarkZoneEnabled,
+ (droppedAsset: DroppedAssetInterface) => droppedAsset.isLandmarkZoneEnabled,
) as DroppedAsset;
- const worldActivityAtStart = WorldActivity.create(credentials.urlSlug, {
- credentials: {
- interactiveNonce: credentials.interactiveNonce,
- interactivePublicKey: credentials.interactivePublicKey,
- visitorId: credentials.visitorId,
- },
- });
+ const worldActivityAtStart = WorldActivity.create(urlSlug, { credentials });
const timeFactor = new Date(Math.round(new Date().getTime() / 10000) * 10000);
const lockId = `${keyAsset.id!}_${timeFactor}`;
@@ -146,10 +142,8 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
droppedAssetId: keyAsset.dataObject!.landmarkZoneId,
shouldIncludeAdminPermissions: true,
});
- const includedVisitors = Object.values(visitorsObj).filter((visitor) => {
- if (!includeAdmins) {
- return !visitor.isAdmin;
- }
+ const includedVisitors = Object.values(visitorsObj).filter((visitor: VisitorInterface) => {
+ if (!includeAdmins) return !visitor.isAdmin;
return true;
});
const participants = includedVisitors.map((visitor) => visitor.profileId) as string[];
@@ -173,11 +167,11 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
analytics: [
{
analyticName: "starts",
- urlSlug: credentials.urlSlug,
+ urlSlug: urlSlug,
},
{
analyticName: `groupConfigOf${numOfGroups}`,
- urlSlug: credentials.urlSlug,
+ urlSlug: urlSlug,
},
{
analyticName: "rounds",
@@ -203,19 +197,13 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
breakouts[keyAsset.id!].adminOriginalInteractiveNonce !==
breakouts[keyAsset.id!].adminCredentials.interactiveNonce
) {
- worldActivity = WorldActivity.create(credentials.urlSlug, {
- credentials: {
- interactiveNonce: breakouts[keyAsset.id!].adminCredentials.interactiveNonce,
- interactivePublicKey: credentials.interactivePublicKey,
- visitorId: breakouts[keyAsset.id!].adminCredentials.visitorId,
- },
- });
+ worldActivity = WorldActivity.create(urlSlug, { credentials });
const breakoutScene: DroppedAsset[] = await getDroppedAssetsBySceneDropId(
breakouts[keyAsset.id!].adminCredentials,
- credentials.sceneDropId,
+ sceneDropId,
);
privateZones = breakoutScene.filter(
- (droppedAsset: DroppedAsset) => droppedAsset.isPrivateZone,
+ (droppedAsset: DroppedAssetInterface) => droppedAsset.isPrivateZone,
) as DroppedAsset[];
}
@@ -224,10 +212,8 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
shouldIncludeAdminPermissions: true,
});
- const includedVisitors = Object.values(visitorsObj).filter((visitor) => {
- if (!includeAdmins) {
- return !visitor.isAdmin;
- }
+ const includedVisitors = Object.values(visitorsObj).filter((visitor: VisitorInterface) => {
+ if (!includeAdmins) return !visitor.isAdmin;
return true;
});
const participants = includedVisitors.map((visitor) => visitor.profileId) as string[];
@@ -242,7 +228,7 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
);
const timeout = setTimeout(() => {
- const world = World.create(credentials.urlSlug, { credentials });
+ const world = World.create(urlSlug, { credentials });
world
.triggerParticle({
name: "pastelConfetti_fall",
@@ -257,11 +243,7 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
breakouts[keyAsset.id!].timeouts.push(timeout);
- const { participantsAnalytics, groupSizeAnalytics } = getAnalytics(
- includedVisitors,
- matches,
- credentials.urlSlug,
- );
+ const { participantsAnalytics, groupSizeAnalytics } = getAnalytics(includedVisitors, matches, urlSlug);
keyAsset
.updateDataObject(
@@ -300,13 +282,7 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
breakouts[keyAsset.id!].adminOriginalInteractiveNonce !==
breakouts[keyAsset.id!].adminCredentials.interactiveNonce
) {
- worldActivity = WorldActivity.create(credentials.urlSlug, {
- credentials: {
- interactiveNonce: breakouts[keyAsset.id!].adminCredentials.interactiveNonce,
- interactivePublicKey: credentials.interactivePublicKey,
- visitorId: breakouts[keyAsset.id!].adminCredentials.visitorId,
- },
- });
+ worldActivity = WorldActivity.create(urlSlug, { credentials });
}
const visitorsObj = await worldActivity.fetchVisitorsInZone({
@@ -314,8 +290,9 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
shouldIncludeAdminPermissions: true,
});
if (!includeAdmins) {
- Object.values(visitorsObj).forEach((visitor) => {
+ Object.values(visitorsObj).forEach((visitor: VisitorInterface) => {
if (visitor.isAdmin) {
+ // @ts-ignore
delete visitorsObj[visitor.visitorId];
}
});
@@ -346,8 +323,8 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
breakouts[keyAsset.id!] = {
interval: interval,
timeouts: [],
- adminProfileId: credentials.profileId,
- adminOriginalInteractiveNonce: credentials.interactiveNonce,
+ adminProfileId: profileId,
+ adminOriginalInteractiveNonce: interactiveNonce,
adminCredentials: credentials,
landmarkZoneId: keyAsset.dataObject!.landmarkZoneId,
data: {
@@ -363,7 +340,7 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
const matches = getMatches(true, keyAsset.id!, participants, breakouts);
const timeout = setTimeout(() => {
- const world = World.create(credentials.urlSlug, { credentials });
+ const world = World.create(urlSlug, { credentials });
world
.triggerParticle({
@@ -374,12 +351,14 @@ export default async function handleSetBreakoutConfig(req: Request, res: Respons
.then()
.catch(() => console.error("Error: Cannot trigger particle"));
+ world.triggerActivity({ type: WorldActivityType.GAME_ON, assetId });
+
placeVisitors(matches, visitorsObj, participants, keyAsset.id!, breakouts, privateZonesAtStart);
}, countdown * 1000);
breakouts[keyAsset.id!].timeouts.push(timeout);
- const { participantsAnalytics, groupSizeAnalytics } = getAnalytics(includedVisitors, matches, credentials.urlSlug);
+ const { participantsAnalytics, groupSizeAnalytics } = getAnalytics(includedVisitors, matches, urlSlug);
keyAsset
.updateDataObject(
diff --git a/server/package.json b/server/package.json
index de85057..c8aab6d 100644
--- a/server/package.json
+++ b/server/package.json
@@ -11,7 +11,7 @@
"ts-check": "tsc --noEmit"
},
"dependencies": {
- "@rtsdk/topia": "^0.12.6",
+ "@rtsdk/topia": "^0.15.7",
"axios": "^1.6.8",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
diff --git a/server/utils/droppedAssets/getDroppedAssetsBySceneDropId.ts b/server/utils/droppedAssets/getDroppedAssetsBySceneDropId.ts
index 8ef9d6b..baa8cd5 100644
--- a/server/utils/droppedAssets/getDroppedAssetsBySceneDropId.ts
+++ b/server/utils/droppedAssets/getDroppedAssetsBySceneDropId.ts
@@ -2,24 +2,15 @@ import { Credentials } from "../../types/Credentials.js";
import { IDroppedAsset } from "../../types/DroppedAssetInterface.js";
import { World, errorHandler } from "../index.js";
-export const getDroppedAssetsBySceneDropId = async (
- credentials: Credentials,
- sceneDropId: string,
-) => {
+export const getDroppedAssetsBySceneDropId = async (credentials: Credentials, sceneDropId: string) => {
try {
- const { interactivePublicKey, interactiveNonce, urlSlug, visitorId } = credentials;
+ const { urlSlug } = credentials;
- const world = World.create(urlSlug, {
- credentials: {
- interactiveNonce,
- interactivePublicKey,
- visitorId,
- },
- });
+ const world = World.create(urlSlug, { credentials });
- const droppedAssets = await world.fetchDroppedAssetsBySceneDropId({
+ const droppedAssets = (await world.fetchDroppedAssetsBySceneDropId({
sceneDropId,
- }) as IDroppedAsset[];
+ })) as IDroppedAsset[];
return droppedAssets;
} catch (error) {
diff --git a/server/utils/topiaInit.ts b/server/utils/topiaInit.ts
index 0baa21a..c895509 100644
--- a/server/utils/topiaInit.ts
+++ b/server/utils/topiaInit.ts
@@ -1,11 +1,18 @@
import dotenv from "dotenv";
dotenv.config({ path: "../.env" });
-import { Topia, AssetFactory, DroppedAssetFactory, UserFactory, VisitorFactory, WorldFactory, WorldActivityFactory } from "@rtsdk/topia";
+import {
+ Topia,
+ AssetFactory,
+ DroppedAssetFactory,
+ UserFactory,
+ VisitorFactory,
+ WorldFactory,
+ WorldActivityFactory,
+} from "@rtsdk/topia";
const config = {
apiDomain: process.env.INSTANCE_DOMAIN || "api.topia.io",
- // apiKey: process.env.API_KEY,
apiProtocol: process.env.INSTANCE_PROTOCOL || "https",
interactiveKey: process.env.INTERACTIVE_KEY,
interactiveSecret: process.env.INTERACTIVE_SECRET,
diff --git a/server/utils/visitors/getVisitor.ts b/server/utils/visitors/getVisitor.ts
index 0e03221..5add1d1 100644
--- a/server/utils/visitors/getVisitor.ts
+++ b/server/utils/visitors/getVisitor.ts
@@ -1,18 +1,13 @@
-import { Visitor } from "../topiaInit.js"
-import { errorHandler } from "../errorHandler.js"
+import { Visitor } from "../topiaInit.js";
+import { errorHandler } from "../errorHandler.js";
import { Credentials } from "../../types/Credentials.js";
+import { VisitorInterface } from "@rtsdk/topia";
export const getVisitor = async (credentials: Credentials) => {
try {
- const { interactivePublicKey, interactiveNonce, urlSlug, visitorId } = credentials;
+ const { urlSlug, visitorId } = credentials;
- const visitor = await Visitor.get(visitorId, urlSlug, {
- credentials: {
- interactiveNonce,
- interactivePublicKey,
- visitorId,
- },
- });
+ const visitor: VisitorInterface = await Visitor.get(visitorId, urlSlug, { credentials });
if (!visitor || !visitor.username) throw "Not in world";