Skip to content

Commit f65ef8d

Browse files
authored
feat: add multiplayer feature flag (#6888)
* updated feature flag to type * added feature flag dependency for pointer sharing and editor listing features
1 parent 49909d2 commit f65ef8d

File tree

5 files changed

+51
-14
lines changed

5 files changed

+51
-14
lines changed

app/client/src/entities/FeatureFlag.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
type FeatureFlag = {
22
COMMENT: boolean;
33
JS_EDITOR: boolean;
4+
MULTIPLAYER: boolean;
45
LINTING: boolean;
56
SNIPPET: boolean;
67
};

app/client/src/pages/Editor/Canvas.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { memo, useCallback } from "react";
2-
import store from "store";
2+
import store, { useSelector } from "store";
33
import WidgetFactory from "utils/WidgetFactory";
44
import { RenderModes } from "constants/WidgetConstants";
55
import { ContainerWidgetProps } from "widgets/ContainerWidget";
@@ -17,6 +17,7 @@ import {
1717
APP_COLLAB_EVENTS,
1818
NAMESPACE_COLLAB_PAGE_EDIT,
1919
} from "constants/AppCollabConstants";
20+
import { isMultiplayerEnabledForUser as isMultiplayerEnabledForUserSelector } from "selectors/appCollabSelectors";
2021

2122
interface CanvasProps {
2223
dsl: ContainerWidgetProps<WidgetProps>;
@@ -46,12 +47,16 @@ const shareMousePointer = (e: any, pageId: string) => {
4647
// TODO(abhinav): get the render mode from context
4748
const Canvas = memo((props: CanvasProps) => {
4849
const { pageId } = props;
50+
const isMultiplayerEnabledForUser = useSelector(
51+
isMultiplayerEnabledForUserSelector,
52+
);
4953
const delayedShareMousePointer = useCallback(
5054
throttle((e) => shareMousePointer(e, pageId), 50, {
5155
trailing: false,
5256
}),
5357
[shareMousePointer, pageId],
5458
);
59+
5560
try {
5661
return (
5762
<>
@@ -62,16 +67,19 @@ const Canvas = memo((props: CanvasProps) => {
6267
id="art-board"
6368
onMouseMove={(e) => {
6469
e.persist();
70+
if (!isMultiplayerEnabledForUser) return;
6571
delayedShareMousePointer(e);
6672
}}
6773
width={props.dsl.rightColumn}
6874
>
6975
{props.dsl.widgetId &&
7076
WidgetFactory.createWidget(props.dsl, RenderModes.CANVAS)}
71-
<CanvasMultiPointerArena
72-
pageEditSocket={pageEditSocket}
73-
pageId={pageId}
74-
/>
77+
{isMultiplayerEnabledForUser && (
78+
<CanvasMultiPointerArena
79+
pageEditSocket={pageEditSocket}
80+
pageId={pageId}
81+
/>
82+
)}
7583
</ArtBoard>
7684
</>
7785
);

app/client/src/pages/Editor/EditorHeader.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { useLocation } from "react-router";
5757
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
5858
import RealtimeAppEditors from "./RealtimeAppEditors";
5959
import { EditorSaveIndicator } from "./EditorSaveIndicator";
60+
import { isMultiplayerEnabledForUser as isMultiplayerEnabledForUserSelector } from "selectors/appCollabSelectors";
6061

6162
const HeaderWrapper = styled(StyledHeader)`
6263
width: 100%;
@@ -227,6 +228,10 @@ export function EditorHeader(props: EditorHeaderProps) {
227228
dispatch(setIsGitSyncModalOpen(true));
228229
}, [dispatch, setIsGitSyncModalOpen]);
229230

231+
const isMultiplayerEnabledForUser = useSelector(
232+
isMultiplayerEnabledForUserSelector,
233+
);
234+
230235
return (
231236
<ThemeProvider theme={props.darkTheme}>
232237
<HeaderWrapper>
@@ -276,7 +281,9 @@ export function EditorHeader(props: EditorHeaderProps) {
276281
</HeaderSection>
277282
<HeaderSection>
278283
<EditorSaveIndicator />
279-
<RealtimeAppEditors applicationId={applicationId} />
284+
{isMultiplayerEnabledForUser && (
285+
<RealtimeAppEditors applicationId={applicationId} />
286+
)}
280287
<Boxed step={OnboardingStep.FINISH}>
281288
<FormDialogComponent
282289
Form={AppInviteUsersForm}

app/client/src/sagas/WebsocketSagas/WebsocketSagas.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { io } from "socket.io-client";
22
import { eventChannel } from "redux-saga";
3-
import { fork, take, call, cancel, put, delay } from "redux-saga/effects";
3+
import {
4+
fork,
5+
take,
6+
call,
7+
cancel,
8+
put,
9+
delay,
10+
select,
11+
} from "redux-saga/effects";
412
import {
513
ReduxActionTypes,
614
ReduxSagaChannels,
@@ -17,6 +25,8 @@ import {
1725
} from "actions/websocketActions";
1826

1927
import handleSocketEvent from "./handleSocketEvent";
28+
import { isMultiplayerEnabledForUser } from "selectors/appCollabSelectors";
29+
import { areCommentsEnabledForUserAndApp } from "selectors/commentsSelectors";
2030

2131
function connect() {
2232
const socket = io();
@@ -85,6 +95,10 @@ function* handleIO(socket: any) {
8595

8696
function* flow() {
8797
while (true) {
98+
yield take([
99+
ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS,
100+
ReduxActionTypes.RETRY_WEBSOCKET_CONNECTION, // for manually triggering reconnection
101+
]);
88102
try {
89103
/**
90104
* Incase the socket is disconnected due to network latencies
@@ -93,13 +107,17 @@ function* flow() {
93107
* We only need to retry incase the socket connection isn't made
94108
* in the first attempt itself
95109
*/
96-
const socket = yield call(connect);
97-
const task = yield fork(handleIO, socket);
98-
yield put(setIsWebsocketConnected(true));
99-
yield take([ReduxActionTypes.LOGOUT_USER_INIT]);
100-
yield take();
101-
yield cancel(task);
102-
socket.disconnect();
110+
const commentsEnabled = yield select(areCommentsEnabledForUserAndApp);
111+
const multiplayerEnabled = yield select(isMultiplayerEnabledForUser);
112+
if (commentsEnabled || multiplayerEnabled) {
113+
const socket = yield call(connect);
114+
const task = yield fork(handleIO, socket);
115+
yield put(setIsWebsocketConnected(true));
116+
yield take([ReduxActionTypes.LOGOUT_USER_INIT]);
117+
yield take();
118+
yield cancel(task);
119+
socket.disconnect();
120+
}
103121
} catch (e) {
104122
// this has to be non blocking
105123
yield fork(function*() {

app/client/src/selectors/appCollabSelectors.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createSelector } from "reselect";
22
import { AppState } from "reducers";
33
import { AppCollabReducerState } from "reducers/uiReducers/appCollabReducer";
44
import { getCurrentUser } from "./usersSelectors";
5+
import getFeatureFlags from "../utils/featureFlags";
56

67
export const getAppCollabState = (state: AppState) => state.ui.appCollab;
78

@@ -11,3 +12,5 @@ export const getRealtimeAppEditors = createSelector(
1112
(appCollab: AppCollabReducerState, currentUser) =>
1213
appCollab.editors.filter((el) => el.email !== currentUser?.email),
1314
);
15+
16+
export const isMultiplayerEnabledForUser = () => getFeatureFlags().MULTIPLAYER;

0 commit comments

Comments
 (0)