{totalMessageCount > 9 ? "9+" : totalMessageCount}
diff --git a/app/client/src/constants/Layers.tsx b/app/client/src/constants/Layers.tsx
index 61d4ce402b..5cccb2f860 100644
--- a/app/client/src/constants/Layers.tsx
+++ b/app/client/src/constants/Layers.tsx
@@ -56,6 +56,7 @@ export const Layers = {
max: Indices.LayerMax,
sideStickyBar: Indices.Layer7,
evaluationPopper: Indices.Layer3,
+ concurrentEditorWarning: Indices.Layer2,
};
export const LayersContext = React.createContext(Layers);
diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx
index 1164bef5fc..5fe8162849 100644
--- a/app/client/src/constants/ReduxActionConstants.tsx
+++ b/app/client/src/constants/ReduxActionConstants.tsx
@@ -11,6 +11,8 @@ export const ReduxSagaChannels = {
};
export const ReduxActionTypes = {
+ APP_COLLAB_SET_CONCURRENT_PAGE_EDITORS:
+ "APP_COLLAB_SET_CONCURRENT_PAGE_EDITORS",
FETCH_SSH_KEY_PAIR_INIT: "FETCH_SSH_KEY_PAIR_INIT",
FETCH_SSH_KEY_PAIR_SUCCESS: "FETCH_SSH_KEY_PAIR_SUCCESS",
SET_IS_IMPORT_APP_VIA_GIT_MODAL_OPEN: "SET_IS_IMPORT_APP_VIA_GIT_MODAL_OPEN",
diff --git a/app/client/src/pages/Editor/BottomBar/index.tsx b/app/client/src/pages/Editor/BottomBar/index.tsx
index 0cde89d240..03e7252e3b 100644
--- a/app/client/src/pages/Editor/BottomBar/index.tsx
+++ b/app/client/src/pages/Editor/BottomBar/index.tsx
@@ -3,6 +3,7 @@ import styled from "styled-components";
import QuickGitActions from "pages/Editor/gitSync/QuickGitActions";
import { Layers } from "constants/Layers";
import { DebuggerTrigger } from "components/editorComponents/Debugger";
+import { Colors } from "constants/Colors";
const Container = styled.div`
position: relative;
@@ -12,6 +13,7 @@ const Container = styled.div`
justify-content: space-between;
background-color: ${(props) => props.theme.colors.editorBottomBar.background};
z-index: ${Layers.bottomBar};
+ border-top: solid 1px ${Colors.MERCURY};
`;
export default function BottomBar() {
diff --git a/app/client/src/pages/Editor/MainContainer.tsx b/app/client/src/pages/Editor/MainContainer.tsx
index 5a2d746e4f..d61d6c2609 100644
--- a/app/client/src/pages/Editor/MainContainer.tsx
+++ b/app/client/src/pages/Editor/MainContainer.tsx
@@ -7,8 +7,6 @@ import WidgetsEditor from "./WidgetsEditor";
import Sidebar from "components/editorComponents/Sidebar";
import BottomBar from "./BottomBar";
-import getFeatureFlags from "utils/featureFlags";
-
import { BUILDER_CHECKLIST_URL, BUILDER_URL } from "constants/routes";
import OnboardingChecklist from "./FirstTimeUserOnboarding/Checklist";
const SentryRoute = Sentry.withSentryRouting(Route);
@@ -17,8 +15,7 @@ const Container = styled.div`
display: flex;
height: calc(
100vh - ${(props) => props.theme.smallHeaderHeight} -
- ${(props) =>
- getFeatureFlags().GIT ? props.theme.bottomBarHeight : "0px"}
+ ${(props) => props.theme.bottomBarHeight}
);
background-color: ${(props) => props.theme.appBackground};
`;
@@ -47,7 +44,7 @@ function MainContainer() {
- {getFeatureFlags().GIT &&
}
+
>
);
}
diff --git a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
index 66f4cfc421..8ea5edf87f 100644
--- a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
+++ b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx
@@ -1,4 +1,4 @@
-import React, { RefObject, useRef, useState } from "react";
+import React, { RefObject, useEffect, useRef, useState } from "react";
import { InjectedFormProps } from "redux-form";
import { Icon, Tag } from "@blueprintjs/core";
import { isString } from "lodash";
@@ -71,6 +71,7 @@ import TooltipComponent from "components/ads/Tooltip";
import * as Sentry from "@sentry/react";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
import SearchSnippets from "components/ads/SnippetButton";
+import { setActionTabsInitialIndex } from "actions/pluginActionActions";
const QueryFormContainer = styled.form`
flex: 1;
@@ -430,6 +431,17 @@ export function EditorJSONtoForm(props: Props) {
window.innerHeight,
);
+ useEffect(() => {
+ if (selectedIndex !== initialIndex) setSelectedIndex(initialIndex);
+ }, [initialIndex]);
+
+ useEffect(() => {
+ // reset on unmount
+ return () => {
+ dispatch(setActionTabsInitialIndex(0));
+ };
+ }, []);
+
const params = useParams<{ apiId?: string; queryId?: string }>();
const actions: Action[] = useSelector((state: AppState) =>
@@ -729,7 +741,7 @@ export function EditorJSONtoForm(props: Props) {
tabName: responseTabs[index].key,
});
}
-
+ dispatch(setActionTabsInitialIndex(index));
setSelectedIndex(index);
};
const { entityDependencies, hasDependencies } = useEntityDependencies(
diff --git a/app/client/src/pages/Editor/RealtimeAppEditors.tsx b/app/client/src/pages/Editor/RealtimeAppEditors.tsx
index 6b652afb10..b63e5baea5 100644
--- a/app/client/src/pages/Editor/RealtimeAppEditors.tsx
+++ b/app/client/src/pages/Editor/RealtimeAppEditors.tsx
@@ -10,6 +10,7 @@ import {
collabStopEditingAppEvent,
collabResetAppEditors,
} from "actions/appCollabActions";
+import { getCurrentPageId } from "selectors/editorSelectors";
import { getIsAppLevelSocketConnected } from "selectors/websocketSelectors";
const UserImageContainer = styled.div`
@@ -37,6 +38,8 @@ export function useEditAppCollabEvents(applicationId?: string) {
const isWebsocketConnected = useSelector(getIsAppLevelSocketConnected);
+ const currentPageId = useSelector(getCurrentPageId);
+
useEffect(() => {
// websocket has to be connected as we only fire this event once.
isWebsocketConnected &&
@@ -48,7 +51,7 @@ export function useEditAppCollabEvents(applicationId?: string) {
applicationId &&
dispatch(collabStopEditingAppEvent(applicationId));
};
- }, [applicationId, isWebsocketConnected]);
+ }, [applicationId, currentPageId, isWebsocketConnected]);
}
function RealtimeAppEditors(props: RealtimeAppEditorsProps) {
diff --git a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx
index 5e7f3e9569..206bb8356a 100644
--- a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx
+++ b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx
@@ -27,6 +27,7 @@ import { ReactComponent as GitCommitLine } from "assets/icons/ads/git-commit-lin
import Button, { Category, Size } from "components/ads/Button";
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
import { GitSyncModalTab } from "entities/GitSync";
+import getFeatureFlags from "utils/featureFlags";
type QuickActionButtonProps = {
count?: number;
@@ -119,6 +120,23 @@ const Container = styled.div`
height: 100%;
display: flex;
align-items: center;
+ margin-left: ${(props) => props.theme.spaces[10]}px;
+`;
+
+const StyledIcon = styled(GitCommitLine)`
+ & path {
+ fill: ${Colors.DARK_GRAY};
+ }
+`;
+
+const PlaceholderButton = styled.div`
+ padding: ${(props) =>
+ `${props.theme.spaces[1]}px ${props.theme.spaces[3]}px`};
+ border: solid 1px ${Colors.MERCURY};
+ ${(props) => getTypographyByKey(props, "btnSmall")};
+ text-transform: uppercase;
+ background-color: ${Colors.ALABASTER_ALT};
+ color: ${Colors.GRAY};
`;
function ConnectGitPlaceholder() {
@@ -126,15 +144,34 @@ function ConnectGitPlaceholder() {
return (
-
-
);
}
@@ -163,7 +200,7 @@ export default function QuickGitActions() {
);
},
});
- return isGitRepoSetup ? (
+ return getFeatureFlags().GIT && isGitRepoSetup ? (
{quickActionButtons.map((button) => (
diff --git a/app/client/src/pages/Editor/index.tsx b/app/client/src/pages/Editor/index.tsx
index ed6fc65e24..ef9735848a 100644
--- a/app/client/src/pages/Editor/index.tsx
+++ b/app/client/src/pages/Editor/index.tsx
@@ -36,6 +36,13 @@ import GitSyncModal from "pages/Editor/gitSync/GitSyncModal";
import history from "utils/history";
import { fetchPage, updateCurrentPage } from "actions/pageActions";
+import ConcurrentPageEditorToast from "comments/ConcurrentPageEditorToast";
+import { getIsPageLevelSocketConnected } from "selectors/websocketSelectors";
+import {
+ collabStartSharingPointerEvent,
+ collabStopSharingPointerEvent,
+} from "actions/appCollabActions";
+
type EditorProps = {
currentApplicationId?: string;
currentApplicationName?: string;
@@ -52,6 +59,9 @@ type EditorProps = {
handlePathUpdated: (location: typeof window.location) => void;
fetchPage: (pageId: string) => void;
updateCurrentPage: (pageId: string) => void;
+ isPageLevelSocketConnected: boolean;
+ collabStartSharingPointerEvent: (pageId: string) => void;
+ collabStopSharingPointerEvent: () => void;
};
type Props = EditorProps & RouteComponentProps;
@@ -73,6 +83,10 @@ class Editor extends Component {
}
this.props.handlePathUpdated(window.location);
this.unlisten = history.listen(this.handleHistoryChange);
+
+ if (this.props.isPageLevelSocketConnected && pageId) {
+ this.props.collabStartSharingPointerEvent(pageId);
+ }
}
shouldComponentUpdate(nextProps: Props, nextState: { registered: boolean }) {
@@ -88,22 +102,30 @@ class Editor extends Component {
this.props.isEditorInitializeError ||
nextProps.creatingOnboardingDatabase !==
this.props.creatingOnboardingDatabase ||
- nextState.registered !== this.state.registered
+ nextState.registered !== this.state.registered ||
+ (nextProps.isPageLevelSocketConnected &&
+ !this.props.isPageLevelSocketConnected)
);
}
componentDidUpdate(prevProps: Props) {
const { pageId } = this.props.match.params || {};
const { pageId: prevPageId } = prevProps.match.params || {};
- if (pageId && pageId !== prevPageId) {
+ const isPageIdUpdated = pageId !== prevPageId;
+ if (pageId && isPageIdUpdated) {
this.props.updateCurrentPage(pageId);
this.props.fetchPage(pageId);
}
+
+ if (this.props.isPageLevelSocketConnected && isPageIdUpdated) {
+ this.props.collabStartSharingPointerEvent(pageId);
+ }
}
componentWillUnmount() {
this.props.resetEditorRequest();
if (typeof this.unlisten === "function") this.unlisten();
+ this.props.collabStopSharingPointerEvent();
}
handleHistoryChange = (location: any) => {
@@ -143,6 +165,7 @@ class Editor extends Component {
+