diff --git a/package-lock.json b/package-lock.json index 7cecefe..161b28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "ajv": "^8.12.0", "classnames": "^2.3.2", "decentraland-dapps": "^15.5.0", - "decentraland-ui": "^4.0.0", + "decentraland-ui": "^4.6.0", "ethers": "^6.6.5", "livekit-client": "^1.12.3", "livekit-server-sdk": "^1.2.5", @@ -8346,11 +8346,11 @@ "integrity": "sha512-L4/bPD2fOeEdtFx+OnO3N81+/gsOkdensIuV9uFGYSN1mSTFaxHkWkhG8DOZ/8jlD0H2Qjkj6yDcWFaK+qu1Dg==" }, "node_modules/decentraland-ui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/decentraland-ui/-/decentraland-ui-4.1.0.tgz", - "integrity": "sha512-lK96yfPhQMusmaiIyhcL4O4nlubRcEq9kem59NBT3O7/gP4O9QLMLoNItZ8gG1U+4Z3FdMLMZ1+/vxq/UMdQTA==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/decentraland-ui/-/decentraland-ui-4.6.0.tgz", + "integrity": "sha512-dGa0aXpIGUaHTD9T9y7JRXT9JpQtgpwHWmpJWJWeG8A3yUUlfJwdIayLEATmtUpYeCGDJGNZhx4ctgUjoV7Y/Q==", "dependencies": { - "@dcl/schemas": "^8.1.0", + "@dcl/schemas": "^9.2.0", "balloon-css": "^0.5.0", "classnames": "^2.3.2", "deep-equal": "^2.0.5", @@ -8358,8 +8358,8 @@ "events": "^3.3.0", "fp-future": "^1.0.1", "parallax-js": "^3.1.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0", "react-responsive": "^9.0.0-beta.3", "react-semantic-ui-datepickers": "^2.17.2", "react-tile-map": "^0.4.1", @@ -8381,9 +8381,9 @@ } }, "node_modules/decentraland-ui/node_modules/@dcl/schemas": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-8.1.0.tgz", - "integrity": "sha512-1A7st/fESASmss+T1Vxc9lo3LHqSvgfDTLtLD2uhdgtOS0RombNES3KG/2zDfBpg9v3DCEchN6i7mWiCEw7/6g==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-9.2.0.tgz", + "integrity": "sha512-OGjjfbL+JsTqphvc6Msl4a5kYCGJlq2LmAMBY9WDlYRE+9DZBE2Sin+ebekYWBmSUA75mJHO+qUpg0/FC8z+oQ==", "dependencies": { "ajv": "^8.11.0", "ajv-errors": "^3.0.0", @@ -26176,11 +26176,11 @@ "integrity": "sha512-L4/bPD2fOeEdtFx+OnO3N81+/gsOkdensIuV9uFGYSN1mSTFaxHkWkhG8DOZ/8jlD0H2Qjkj6yDcWFaK+qu1Dg==" }, "decentraland-ui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/decentraland-ui/-/decentraland-ui-4.1.0.tgz", - "integrity": "sha512-lK96yfPhQMusmaiIyhcL4O4nlubRcEq9kem59NBT3O7/gP4O9QLMLoNItZ8gG1U+4Z3FdMLMZ1+/vxq/UMdQTA==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/decentraland-ui/-/decentraland-ui-4.6.0.tgz", + "integrity": "sha512-dGa0aXpIGUaHTD9T9y7JRXT9JpQtgpwHWmpJWJWeG8A3yUUlfJwdIayLEATmtUpYeCGDJGNZhx4ctgUjoV7Y/Q==", "requires": { - "@dcl/schemas": "^8.1.0", + "@dcl/schemas": "^9.2.0", "balloon-css": "^0.5.0", "classnames": "^2.3.2", "deep-equal": "^2.0.5", @@ -26188,8 +26188,8 @@ "events": "^3.3.0", "fp-future": "^1.0.1", "parallax-js": "^3.1.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0", "react-responsive": "^9.0.0-beta.3", "react-semantic-ui-datepickers": "^2.17.2", "react-tile-map": "^0.4.1", @@ -26199,9 +26199,9 @@ }, "dependencies": { "@dcl/schemas": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-8.1.0.tgz", - "integrity": "sha512-1A7st/fESASmss+T1Vxc9lo3LHqSvgfDTLtLD2uhdgtOS0RombNES3KG/2zDfBpg9v3DCEchN6i7mWiCEw7/6g==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-9.2.0.tgz", + "integrity": "sha512-OGjjfbL+JsTqphvc6Msl4a5kYCGJlq2LmAMBY9WDlYRE+9DZBE2Sin+ebekYWBmSUA75mJHO+qUpg0/FC8z+oQ==", "requires": { "ajv": "^8.11.0", "ajv-errors": "^3.0.0", diff --git a/package.json b/package.json index eee79a7..1ebfc0a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "ajv": "^8.12.0", "classnames": "^2.3.2", "decentraland-dapps": "^15.5.0", - "decentraland-ui": "^4.0.0", + "decentraland-ui": "^4.6.0", "ethers": "^6.6.5", "livekit-client": "^1.12.3", "livekit-server-sdk": "^1.2.5", diff --git a/src/assets/icons/PeopleIcon.tsx b/src/assets/icons/PeopleIcon.tsx index 7740a55..5a89b8c 100644 --- a/src/assets/icons/PeopleIcon.tsx +++ b/src/assets/icons/PeopleIcon.tsx @@ -4,8 +4,8 @@ import type { SVGProps } from 'react' const PeopleIcon = (props: SVGProps) => ( diff --git a/src/components/Pages/Conference/Conference.container.ts b/src/components/Pages/Conference/Conference.container.ts index 0be1522..315d7f8 100644 --- a/src/components/Pages/Conference/Conference.container.ts +++ b/src/components/Pages/Conference/Conference.container.ts @@ -1,24 +1,19 @@ import { connect } from 'react-redux' import { getAddress, isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' -import { getServer, getToken } from '../../../modules/conference/selector' +import { getServer, getToken, getWorldContentServerUrl, getWorldName } from '../../../modules/conference/selector' import { isLoggingIn } from '../../../modules/identity/selector' import { RootState } from '../../../modules/reducer' import withRouter from '../../../utils/WithRouter' import Conference from './Conference' -import { MapDispatch, MapStateProps, OwnProps } from './Conference.types' +import { MapStateProps } from './Conference.types' -const mapStateToProps = (state: RootState, ownProps: OwnProps): MapStateProps => { - const addressFromPath = ownProps.router.params.profileAddress +const mapStateToProps = (state: RootState): MapStateProps => ({ + isLoading: isLoggingIn(state) || isConnecting(state), + loggedInAddress: getAddress(state)?.toLowerCase(), + server: getServer(state), + token: getToken(state), + worldName: getWorldName(state), + worldContentServerUrl: getWorldContentServerUrl(state) +}) - return { - profileAddress: addressFromPath?.toLowerCase(), - isLoading: isLoggingIn(state) || isConnecting(state), - loggedInAddress: getAddress(state)?.toLowerCase(), - server: getServer(state), - token: getToken(state) - } -} - -const mapDispatch = (_dispatch: MapDispatch): any => ({}) - -export default withRouter(connect(mapStateToProps, mapDispatch)(Conference)) +export default withRouter(connect(mapStateToProps)(Conference)) diff --git a/src/components/Pages/Conference/Conference.tsx b/src/components/Pages/Conference/Conference.tsx index 51e6134..5a12d7f 100644 --- a/src/components/Pages/Conference/Conference.tsx +++ b/src/components/Pages/Conference/Conference.tsx @@ -1,16 +1,58 @@ -import React from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { LiveKitRoom } from '@livekit/components-react' import '@livekit/components-styles' +import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils' +import { Events } from '../../../modules/analytics/types' import { VideoConference } from '../../VideoConference/' import { Props } from './Conference.types' import './Conference.css' export default function Conference(props: Props) { - const { token, server } = props + const { token, server, worldName, worldContentServerUrl } = props + const [alreadyDisconnected, setAlreadyDisconnected] = useState(false) + const analytics = getAnalytics() + + const track = useCallback( + (event: Events) => { + if (!worldName || !worldContentServerUrl) return + + analytics.track(event, { + worldName, + worldContentServerUrl + }) + }, + [worldName, worldContentServerUrl] + ) + + const handleConnect = useCallback(() => track(Events.CONNECT), [track]) + const handleDisconnect = useCallback(() => { + // This is to avoid tracking the disconnect event twice + if (!alreadyDisconnected) { + track(Events.DISCONNECT) + setAlreadyDisconnected(true) + } + }, [track, alreadyDisconnected]) + + useEffect(() => { + window.onbeforeunload = () => { + handleDisconnect() + } + + return () => { + window.onbeforeunload = null + } + }, []) return ( <> - + diff --git a/src/components/Pages/Conference/Conference.types.ts b/src/components/Pages/Conference/Conference.types.ts index 6482362..6b28bbb 100644 --- a/src/components/Pages/Conference/Conference.types.ts +++ b/src/components/Pages/Conference/Conference.types.ts @@ -1,19 +1,13 @@ import { Dispatch } from 'redux' -import { RouterProps } from '../../../utils/WithRouter' export type Props = { loggedInAddress?: string - profileAddress?: string isLoading: boolean server?: string token?: string + worldContentServerUrl: string + worldName: string } -export type MapStateProps = Pick +export type MapStateProps = Pick export type MapDispatch = Dispatch -type Params = { - profileAddress?: string -} -export type OwnProps = { - router: RouterProps -} diff --git a/src/components/Pages/ConnectToWorld/ConnectToWorld.container.ts b/src/components/Pages/ConnectToWorld/ConnectToWorld.container.ts index 2f66c16..c1c4692 100644 --- a/src/components/Pages/ConnectToWorld/ConnectToWorld.container.ts +++ b/src/components/Pages/ConnectToWorld/ConnectToWorld.container.ts @@ -1,6 +1,6 @@ import { connect } from 'react-redux' import { getAddress, isConnecting } from 'decentraland-dapps/dist/modules/wallet/selectors' -import { setServer, setToken } from '../../../modules/conference/action' +import { setServer, setToken, setWorldRelatedInformation } from '../../../modules/conference/action' import { config } from '../../../modules/config' import { getCurrentIdentity, isLoggingIn } from '../../../modules/identity/selector' import { RootState } from '../../../modules/reducer' @@ -22,9 +22,10 @@ const mapStateToProps = (state: RootState, ownProps: OwnProps): MapStateProps => } const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onSubmitConnectForm: (server: string, token: string) => { + onSubmitConnectForm: (server: string, token: string, worldsContentServerUrl: string, selectedServer: string) => { dispatch(setServer({ server })) dispatch(setToken({ token })) + dispatch(setWorldRelatedInformation({ contentServerUrl: worldsContentServerUrl, name: selectedServer })) } }) diff --git a/src/components/Pages/ConnectToWorld/ConnectToWorld.tsx b/src/components/Pages/ConnectToWorld/ConnectToWorld.tsx index 463f944..5520dd3 100644 --- a/src/components/Pages/ConnectToWorld/ConnectToWorld.tsx +++ b/src/components/Pages/ConnectToWorld/ConnectToWorld.tsx @@ -90,7 +90,7 @@ function ConnectToWorld(props: Props) { if (!identity) return const response: { url: string; token: string } = await livekitConnect(identity, worldsContentServerUrl, selectedServer) - onSubmitConnectForm(response.url, response.token) + onSubmitConnectForm(response.url, response.token, worldsContentServerUrl, selectedServer) addServerToPreviouslyLoaded(selectedServer) navigate(`/meet/${encodeURIComponent(response.url)}?token=${encodeURIComponent(response.token)}`) } catch (error) { diff --git a/src/components/Pages/ConnectToWorld/ConnectToWorld.types.ts b/src/components/Pages/ConnectToWorld/ConnectToWorld.types.ts index b8e5a7e..4e05dc1 100644 --- a/src/components/Pages/ConnectToWorld/ConnectToWorld.types.ts +++ b/src/components/Pages/ConnectToWorld/ConnectToWorld.types.ts @@ -8,7 +8,7 @@ export type Props = { previouslyLoadedServers: string[] | null identity: AuthIdentity | null worldsContentServerUrl: string - onSubmitConnectForm: (server: string, token: string) => void + onSubmitConnectForm: (server: string, token: string, worldsContentServerUrl: string, selectedServer: string) => void } export type MapStateProps = Pick diff --git a/src/modules/analytics/types.ts b/src/modules/analytics/types.ts new file mode 100644 index 0000000..d4a4155 --- /dev/null +++ b/src/modules/analytics/types.ts @@ -0,0 +1,4 @@ +export enum Events { + CONNECT = 'Connect', + DISCONNECT = 'Disconnect' +} diff --git a/src/modules/conference/action.ts b/src/modules/conference/action.ts index 034e6ff..29c3dbf 100644 --- a/src/modules/conference/action.ts +++ b/src/modules/conference/action.ts @@ -1,9 +1,10 @@ import { createAction } from '@reduxjs/toolkit' export const setServer = createAction<{ server: string }>('Set Server') - export type SetServerAction = ReturnType export const setToken = createAction<{ token: string }>('Set Token') - export type SetTokenAction = ReturnType + +export const setWorldRelatedInformation = createAction<{ contentServerUrl: string; name: string }>('Set World Related Information') +export type SetWorldRelatedInformationAction = ReturnType diff --git a/src/modules/conference/reducer.ts b/src/modules/conference/reducer.ts index c644e52..0625f00 100644 --- a/src/modules/conference/reducer.ts +++ b/src/modules/conference/reducer.ts @@ -1,14 +1,22 @@ import { createReducer } from '@reduxjs/toolkit' -import { setServer, setToken } from './action' +import { setServer, setToken, setWorldRelatedInformation } from './action' export type ConferenceState = { token: string server: string + worlds: { + contentServerUrl: string + name: string + } } export const INITIAL_STATE: ConferenceState = { token: '', - server: '' + server: '', + worlds: { + contentServerUrl: '', + name: '' + } } export const conferenceReducer = createReducer(INITIAL_STATE, builder => @@ -19,4 +27,12 @@ export const conferenceReducer = createReducer(INITIAL_STATE, b .addCase(setToken, (state, action) => { state.token = action.payload.token }) + .addCase(setWorldRelatedInformation, (state, action) => { + const { contentServerUrl, name } = action.payload + + state.worlds = { + contentServerUrl, + name + } + }) ) diff --git a/src/modules/conference/selector.ts b/src/modules/conference/selector.ts index aa447d6..6f2e97c 100644 --- a/src/modules/conference/selector.ts +++ b/src/modules/conference/selector.ts @@ -6,4 +6,8 @@ const getState = (state: RootState) => state.conference export const getToken = (state: RootState) => getState(state).token export const getServer = (state: RootState) => getState(state).server +const getWorlds = (state: RootState) => getState(state).worlds +export const getWorldName = (state: RootState) => getWorlds(state).name +export const getWorldContentServerUrl = (state: RootState) => getWorlds(state).contentServerUrl + export const isLoading = createSelector([getToken], token => !!token) diff --git a/src/modules/config/env/dev.json b/src/modules/config/env/dev.json index 4564368..83ba899 100644 --- a/src/modules/config/env/dev.json +++ b/src/modules/config/env/dev.json @@ -1,5 +1,6 @@ { "CHAIN_ID": "11155111", "PEER_URL": "https://peer.decentraland.zone", - "WORLDS_CONTENT_SERVER_URL": "https://worlds-content-server.decentraland.zone" + "WORLDS_CONTENT_SERVER_URL": "https://worlds-content-server.decentraland.zone", + "SEGMENT_API_KEY": "XiymjAbQV5OJZXaT1doKqHgvgoUGHprP" } diff --git a/src/modules/config/env/prod.json b/src/modules/config/env/prod.json index 164b59b..2302cc9 100644 --- a/src/modules/config/env/prod.json +++ b/src/modules/config/env/prod.json @@ -2,5 +2,6 @@ "CHAIN_ID": "1", "EXPLORER_URL": "https://play.decentraland.org", "PEER_URL": "https://peer.decentraland.org", - "WORLDS_CONTENT_SERVER_URL": "https://worlds-content-server.decentraland.org" + "WORLDS_CONTENT_SERVER_URL": "https://worlds-content-server.decentraland.org", + "SEGMENT_API_KEY": "FLpPrzJ8NlZLoRDldyQz4VdRnFGtxCAb" } diff --git a/src/modules/config/env/stg.json b/src/modules/config/env/stg.json index 1b898f4..370ef8b 100644 --- a/src/modules/config/env/stg.json +++ b/src/modules/config/env/stg.json @@ -1,5 +1,6 @@ { "CHAIN_ID": "1", "PEER_URL": "https://peer.decentraland.org", - "WORLDS_CONTENT_SERVER_URL": "https://worlds-content-server.decentraland.org" + "WORLDS_CONTENT_SERVER_URL": "https://worlds-content-server.decentraland.org", + "SEGMENT_API_KEY": "FLpPrzJ8NlZLoRDldyQz4VdRnFGtxCAb" } diff --git a/tsconfig.json b/tsconfig.json index 289c719..0adb603 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,12 +21,6 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": [ - "src", - ".eslintrc.js", - "vite.config.ts", - "jest.config.ts", - "scripts" - ], + "include": ["src", ".eslintrc.js", "vite.config.ts", "jest.config.ts", "scripts"], "references": [{ "path": "./tsconfig.node.json" }] }