feat: add multiplayer feature flag (#6888)

* updated feature flag to type

* added feature flag dependency for pointer sharing and editor listing features
This commit is contained in:
Pranav Kanade 2021-08-26 12:26:13 +05:30 committed by GitHub
parent 49909d24b8
commit f65ef8ddf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 14 deletions

View File

@ -1,6 +1,7 @@
type FeatureFlag = {
COMMENT: boolean;
JS_EDITOR: boolean;
MULTIPLAYER: boolean;
LINTING: boolean;
SNIPPET: boolean;
};

View File

@ -1,5 +1,5 @@
import React, { memo, useCallback } from "react";
import store from "store";
import store, { useSelector } from "store";
import WidgetFactory from "utils/WidgetFactory";
import { RenderModes } from "constants/WidgetConstants";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
@ -17,6 +17,7 @@ import {
APP_COLLAB_EVENTS,
NAMESPACE_COLLAB_PAGE_EDIT,
} from "constants/AppCollabConstants";
import { isMultiplayerEnabledForUser as isMultiplayerEnabledForUserSelector } from "selectors/appCollabSelectors";
interface CanvasProps {
dsl: ContainerWidgetProps<WidgetProps>;
@ -46,12 +47,16 @@ const shareMousePointer = (e: any, pageId: string) => {
// TODO(abhinav): get the render mode from context
const Canvas = memo((props: CanvasProps) => {
const { pageId } = props;
const isMultiplayerEnabledForUser = useSelector(
isMultiplayerEnabledForUserSelector,
);
const delayedShareMousePointer = useCallback(
throttle((e) => shareMousePointer(e, pageId), 50, {
trailing: false,
}),
[shareMousePointer, pageId],
);
try {
return (
<>
@ -62,16 +67,19 @@ const Canvas = memo((props: CanvasProps) => {
id="art-board"
onMouseMove={(e) => {
e.persist();
if (!isMultiplayerEnabledForUser) return;
delayedShareMousePointer(e);
}}
width={props.dsl.rightColumn}
>
{props.dsl.widgetId &&
WidgetFactory.createWidget(props.dsl, RenderModes.CANVAS)}
<CanvasMultiPointerArena
pageEditSocket={pageEditSocket}
pageId={pageId}
/>
{isMultiplayerEnabledForUser && (
<CanvasMultiPointerArena
pageEditSocket={pageEditSocket}
pageId={pageId}
/>
)}
</ArtBoard>
</>
);

View File

@ -57,6 +57,7 @@ import { useLocation } from "react-router";
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
import RealtimeAppEditors from "./RealtimeAppEditors";
import { EditorSaveIndicator } from "./EditorSaveIndicator";
import { isMultiplayerEnabledForUser as isMultiplayerEnabledForUserSelector } from "selectors/appCollabSelectors";
const HeaderWrapper = styled(StyledHeader)`
width: 100%;
@ -227,6 +228,10 @@ export function EditorHeader(props: EditorHeaderProps) {
dispatch(setIsGitSyncModalOpen(true));
}, [dispatch, setIsGitSyncModalOpen]);
const isMultiplayerEnabledForUser = useSelector(
isMultiplayerEnabledForUserSelector,
);
return (
<ThemeProvider theme={props.darkTheme}>
<HeaderWrapper>
@ -276,7 +281,9 @@ export function EditorHeader(props: EditorHeaderProps) {
</HeaderSection>
<HeaderSection>
<EditorSaveIndicator />
<RealtimeAppEditors applicationId={applicationId} />
{isMultiplayerEnabledForUser && (
<RealtimeAppEditors applicationId={applicationId} />
)}
<Boxed step={OnboardingStep.FINISH}>
<FormDialogComponent
Form={AppInviteUsersForm}

View File

@ -1,6 +1,14 @@
import { io } from "socket.io-client";
import { eventChannel } from "redux-saga";
import { fork, take, call, cancel, put, delay } from "redux-saga/effects";
import {
fork,
take,
call,
cancel,
put,
delay,
select,
} from "redux-saga/effects";
import {
ReduxActionTypes,
ReduxSagaChannels,
@ -17,6 +25,8 @@ import {
} from "actions/websocketActions";
import handleSocketEvent from "./handleSocketEvent";
import { isMultiplayerEnabledForUser } from "selectors/appCollabSelectors";
import { areCommentsEnabledForUserAndApp } from "selectors/commentsSelectors";
function connect() {
const socket = io();
@ -85,6 +95,10 @@ function* handleIO(socket: any) {
function* flow() {
while (true) {
yield take([
ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS,
ReduxActionTypes.RETRY_WEBSOCKET_CONNECTION, // for manually triggering reconnection
]);
try {
/**
* Incase the socket is disconnected due to network latencies
@ -93,13 +107,17 @@ function* flow() {
* We only need to retry incase the socket connection isn't made
* in the first attempt itself
*/
const socket = yield call(connect);
const task = yield fork(handleIO, socket);
yield put(setIsWebsocketConnected(true));
yield take([ReduxActionTypes.LOGOUT_USER_INIT]);
yield take();
yield cancel(task);
socket.disconnect();
const commentsEnabled = yield select(areCommentsEnabledForUserAndApp);
const multiplayerEnabled = yield select(isMultiplayerEnabledForUser);
if (commentsEnabled || multiplayerEnabled) {
const socket = yield call(connect);
const task = yield fork(handleIO, socket);
yield put(setIsWebsocketConnected(true));
yield take([ReduxActionTypes.LOGOUT_USER_INIT]);
yield take();
yield cancel(task);
socket.disconnect();
}
} catch (e) {
// this has to be non blocking
yield fork(function*() {

View File

@ -2,6 +2,7 @@ import { createSelector } from "reselect";
import { AppState } from "reducers";
import { AppCollabReducerState } from "reducers/uiReducers/appCollabReducer";
import { getCurrentUser } from "./usersSelectors";
import getFeatureFlags from "../utils/featureFlags";
export const getAppCollabState = (state: AppState) => state.ui.appCollab;
@ -11,3 +12,5 @@ export const getRealtimeAppEditors = createSelector(
(appCollab: AppCollabReducerState, currentUser) =>
appCollab.editors.filter((el) => el.email !== currentUser?.email),
);
export const isMultiplayerEnabledForUser = () => getFeatureFlags().MULTIPLAYER;