chore: Parallelise feature flags and user info API (#12263)
This commit is contained in:
parent
461f35380b
commit
fabfb65a4f
|
|
@ -48,14 +48,15 @@ import { trimTrailingSlash } from "utils/helpers";
|
||||||
import { getSafeCrash, getSafeCrashCode } from "selectors/errorSelectors";
|
import { getSafeCrash, getSafeCrashCode } from "selectors/errorSelectors";
|
||||||
import UserProfile from "pages/UserProfile";
|
import UserProfile from "pages/UserProfile";
|
||||||
import { getCurrentUser } from "actions/authActions";
|
import { getCurrentUser } from "actions/authActions";
|
||||||
import { getFeatureFlagsFetched } from "selectors/usersSelectors";
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
import Setup from "pages/setup";
|
import Setup from "pages/setup";
|
||||||
import Settings from "pages/Settings";
|
import Settings from "pages/Settings";
|
||||||
import SignupSuccess from "pages/setup/SignupSuccess";
|
import SignupSuccess from "pages/setup/SignupSuccess";
|
||||||
import { Theme } from "constants/DefaultTheme";
|
import { Theme } from "constants/DefaultTheme";
|
||||||
import { ERROR_CODES } from "ce/constants/ApiConstants";
|
import { ERROR_CODES } from "ce/constants/ApiConstants";
|
||||||
import TemplatesListLoader from "pages/Templates/loader";
|
import TemplatesListLoader from "pages/Templates/loader";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
import { fetchFeatureFlagsInit } from "actions/userActions";
|
||||||
|
import FeatureFlags from "entities/FeatureFlags";
|
||||||
|
|
||||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||||
|
|
||||||
|
|
@ -78,19 +79,21 @@ function changeAppBackground(currentTheme: any) {
|
||||||
function AppRouter(props: {
|
function AppRouter(props: {
|
||||||
safeCrash: boolean;
|
safeCrash: boolean;
|
||||||
getCurrentUser: () => void;
|
getCurrentUser: () => void;
|
||||||
|
getFeatureFlags: () => void;
|
||||||
currentTheme: Theme;
|
currentTheme: Theme;
|
||||||
featureFlagsFetched: boolean;
|
|
||||||
safeCrashCode?: ERROR_CODES;
|
safeCrashCode?: ERROR_CODES;
|
||||||
|
featureFlags: FeatureFlags;
|
||||||
setTheme: (theme: ThemeMode) => void;
|
setTheme: (theme: ThemeMode) => void;
|
||||||
}) {
|
}) {
|
||||||
|
const { featureFlags, getCurrentUser, getFeatureFlags } = props;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: window.location.pathname });
|
AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: window.location.pathname });
|
||||||
const stopListener = history.listen((location: any) => {
|
const stopListener = history.listen((location: any) => {
|
||||||
AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: location.pathname });
|
AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: location.pathname });
|
||||||
changeAppBackground(props.currentTheme);
|
changeAppBackground(props.currentTheme);
|
||||||
});
|
});
|
||||||
props.getCurrentUser();
|
getCurrentUser();
|
||||||
|
getFeatureFlags();
|
||||||
return stopListener;
|
return stopListener;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -98,8 +101,6 @@ function AppRouter(props: {
|
||||||
changeAppBackground(props.currentTheme);
|
changeAppBackground(props.currentTheme);
|
||||||
}, [props.currentTheme]);
|
}, [props.currentTheme]);
|
||||||
|
|
||||||
if (!props.featureFlagsFetched) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Suspense fallback={loadingIndicator}>
|
<Suspense fallback={loadingIndicator}>
|
||||||
|
|
@ -128,14 +129,13 @@ function AppRouter(props: {
|
||||||
exact
|
exact
|
||||||
path={SIGNUP_SUCCESS_URL}
|
path={SIGNUP_SUCCESS_URL}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SentryRoute component={UserProfile} path={PROFILE} />
|
<SentryRoute component={UserProfile} path={PROFILE} />
|
||||||
<SentryRoute
|
<SentryRoute
|
||||||
component={UnsubscribeEmail}
|
component={UnsubscribeEmail}
|
||||||
path={UNSUBSCRIBE_EMAIL_URL}
|
path={UNSUBSCRIBE_EMAIL_URL}
|
||||||
/>
|
/>
|
||||||
<SentryRoute component={Setup} exact path={SETUP} />
|
<SentryRoute component={Setup} exact path={SETUP} />
|
||||||
{getFeatureFlags().APP_TEMPLATE && (
|
{featureFlags.APP_TEMPLATE && (
|
||||||
<SentryRoute
|
<SentryRoute
|
||||||
component={TemplatesListLoader}
|
component={TemplatesListLoader}
|
||||||
path={TEMPLATES_PATH}
|
path={TEMPLATES_PATH}
|
||||||
|
|
@ -174,7 +174,7 @@ const mapStateToProps = (state: AppState) => ({
|
||||||
currentTheme: getCurrentThemeDetails(state),
|
currentTheme: getCurrentThemeDetails(state),
|
||||||
safeCrash: getSafeCrash(state),
|
safeCrash: getSafeCrash(state),
|
||||||
safeCrashCode: getSafeCrashCode(state),
|
safeCrashCode: getSafeCrashCode(state),
|
||||||
featureFlagsFetched: getFeatureFlagsFetched(state),
|
featureFlags: selectFeatureFlags(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: any) => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
|
|
@ -182,6 +182,7 @@ const mapDispatchToProps = (dispatch: any) => ({
|
||||||
dispatch(setThemeMode(mode));
|
dispatch(setThemeMode(mode));
|
||||||
},
|
},
|
||||||
getCurrentUser: () => dispatch(getCurrentUser()),
|
getCurrentUser: () => dispatch(getCurrentUser()),
|
||||||
|
getFeatureFlags: () => dispatch(fetchFeatureFlagsInit()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AppRouter);
|
export default connect(mapStateToProps, mapDispatchToProps)(AppRouter);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
UpdateUserRequest,
|
UpdateUserRequest,
|
||||||
VerifyTokenRequest,
|
VerifyTokenRequest,
|
||||||
} from "api/UserApi";
|
} from "api/UserApi";
|
||||||
|
import FeatureFlags from "entities/FeatureFlags";
|
||||||
|
|
||||||
export const logoutUser = (payload?: { redirectURL: string }) => ({
|
export const logoutUser = (payload?: { redirectURL: string }) => ({
|
||||||
type: ReduxActionTypes.LOGOUT_USER_INIT,
|
type: ReduxActionTypes.LOGOUT_USER_INIT,
|
||||||
|
|
@ -108,8 +109,9 @@ export const fetchFeatureFlagsInit = () => ({
|
||||||
type: ReduxActionTypes.FETCH_FEATURE_FLAGS_INIT,
|
type: ReduxActionTypes.FETCH_FEATURE_FLAGS_INIT,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fetchFeatureFlagsSuccess = () => ({
|
export const fetchFeatureFlagsSuccess = (payload: FeatureFlags) => ({
|
||||||
type: ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS,
|
type: ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS,
|
||||||
|
payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fetchFeatureFlagsError = (error: any) => ({
|
export const fetchFeatureFlagsError = (error: any) => ({
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ import { Icon, Popover, PopoverPosition } from "@blueprintjs/core";
|
||||||
import { Theme } from "constants/DefaultTheme";
|
import { Theme } from "constants/DefaultTheme";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
import { getIsGitConnected } from "../../../../selectors/gitSyncSelectors";
|
import { getIsGitConnected } from "../../../../selectors/gitSyncSelectors";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
|
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
|
||||||
import { GitSyncModalTab } from "entities/GitSync";
|
import { GitSyncModalTab } from "entities/GitSync";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
|
|
||||||
import { ReactComponent as GitBranch } from "assets/icons/ads/git-branch.svg";
|
import { ReactComponent as GitBranch } from "assets/icons/ads/git-branch.svg";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
|
||||||
const DeployLinkDialog = styled.div`
|
const DeployLinkDialog = styled.div`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -74,6 +74,8 @@ export const DeployLinkButton = withTheme((props: Props) => {
|
||||||
|
|
||||||
const isGitConnected = useSelector(getIsGitConnected);
|
const isGitConnected = useSelector(getIsGitConnected);
|
||||||
|
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
|
|
@ -96,7 +98,7 @@ export const DeployLinkButton = withTheme((props: Props) => {
|
||||||
canEscapeKeyClose={false}
|
canEscapeKeyClose={false}
|
||||||
content={
|
content={
|
||||||
<DeployLinkDialog>
|
<DeployLinkDialog>
|
||||||
{getFeatureFlags().GIT && !isGitConnected && (
|
{featureFlags.GIT && !isGitConnected && (
|
||||||
<DeployLink
|
<DeployLink
|
||||||
className="t--connect-to-git-btn"
|
className="t--connect-to-git-btn"
|
||||||
onClick={goToGitConnectionPopup}
|
onClick={goToGitConnectionPopup}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ import { DataTree, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||||
import { getEntityNameAndPropertyPath } from "workers/evaluationUtils";
|
import { getEntityNameAndPropertyPath } from "workers/evaluationUtils";
|
||||||
import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
|
import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
|
||||||
import { createNewJSCollection } from "actions/jsPaneActions";
|
import { createNewJSCollection } from "actions/jsPaneActions";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import { JSAction, Variable } from "entities/JSCollection";
|
import { JSAction, Variable } from "entities/JSCollection";
|
||||||
import {
|
import {
|
||||||
CLEAR_INTERVAL,
|
CLEAR_INTERVAL,
|
||||||
|
|
@ -60,10 +59,11 @@ import {
|
||||||
import { toggleShowGlobalSearchModal } from "actions/globalSearchActions";
|
import { toggleShowGlobalSearchModal } from "actions/globalSearchActions";
|
||||||
import { filterCategories, SEARCH_CATEGORY_ID } from "../GlobalSearch/utils";
|
import { filterCategories, SEARCH_CATEGORY_ID } from "../GlobalSearch/utils";
|
||||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||||
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
import FeatureFlags from "entities/FeatureFlags";
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/ban-types */
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
/* TODO: Function and object types need to be updated to enable the lint rule */
|
/* TODO: Function and object types need to be updated to enable the lint rule */
|
||||||
const isJSEditorEnabled = getFeatureFlags().JS_EDITOR;
|
|
||||||
const baseOptions: { label: string; value: string }[] = [
|
const baseOptions: { label: string; value: string }[] = [
|
||||||
{
|
{
|
||||||
label: createMessage(NO_ACTION),
|
label: createMessage(NO_ACTION),
|
||||||
|
|
@ -127,7 +127,8 @@ const baseOptions: { label: string; value: string }[] = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const getBaseOptions = () => {
|
const getBaseOptions = (featureFlags: FeatureFlags) => {
|
||||||
|
const { JS_EDITOR: isJSEditorEnabled } = featureFlags;
|
||||||
if (isJSEditorEnabled) {
|
if (isJSEditorEnabled) {
|
||||||
const jsOption = baseOptions.find(
|
const jsOption = baseOptions.find(
|
||||||
(option: any) => option.value === ActionType.jsFunction,
|
(option: any) => option.value === ActionType.jsFunction,
|
||||||
|
|
@ -413,8 +414,9 @@ function getIntegrationOptionsWithChildren(
|
||||||
jsActions: Array<JSCollectionData>,
|
jsActions: Array<JSCollectionData>,
|
||||||
createIntegrationOption: TreeDropdownOption,
|
createIntegrationOption: TreeDropdownOption,
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
|
featureFlags: FeatureFlags,
|
||||||
) {
|
) {
|
||||||
const isJSEditorEnabled = getFeatureFlags().JS_EDITOR;
|
const { JS_EDITOR: isJSEditorEnabled } = featureFlags;
|
||||||
const createJSObject: TreeDropdownOption = {
|
const createJSObject: TreeDropdownOption = {
|
||||||
label: "New JS Object",
|
label: "New JS Object",
|
||||||
value: "JSObject",
|
value: "JSObject",
|
||||||
|
|
@ -527,6 +529,7 @@ function getIntegrationOptionsWithChildren(
|
||||||
function useIntegrationsOptionTree() {
|
function useIntegrationsOptionTree() {
|
||||||
const pageId = useSelector(getCurrentPageId) || "";
|
const pageId = useSelector(getCurrentPageId) || "";
|
||||||
const applicationId = useSelector(getCurrentApplicationId) as string;
|
const applicationId = useSelector(getCurrentApplicationId) as string;
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const plugins = useSelector((state: AppState) => {
|
const plugins = useSelector((state: AppState) => {
|
||||||
return state.entities.plugins.list;
|
return state.entities.plugins.list;
|
||||||
|
|
@ -539,7 +542,7 @@ function useIntegrationsOptionTree() {
|
||||||
pageId,
|
pageId,
|
||||||
applicationId,
|
applicationId,
|
||||||
pluginGroups,
|
pluginGroups,
|
||||||
getBaseOptions(),
|
getBaseOptions(featureFlags),
|
||||||
actions,
|
actions,
|
||||||
jsActions,
|
jsActions,
|
||||||
{
|
{
|
||||||
|
|
@ -557,6 +560,7 @@ function useIntegrationsOptionTree() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dispatch,
|
dispatch,
|
||||||
|
featureFlags,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
type FeatureFlag = {
|
|
||||||
APP_TEMPLATE: boolean;
|
|
||||||
JS_EDITOR: boolean;
|
|
||||||
MULTIPLAYER: boolean;
|
|
||||||
SNIPPET: boolean;
|
|
||||||
GIT: boolean;
|
|
||||||
GIT_IMPORT: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FeatureFlag;
|
|
||||||
10
app/client/src/entities/FeatureFlags.ts
Normal file
10
app/client/src/entities/FeatureFlags.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
type FeatureFlags = {
|
||||||
|
APP_TEMPLATE?: boolean;
|
||||||
|
JS_EDITOR?: boolean;
|
||||||
|
MULTIPLAYER?: boolean;
|
||||||
|
SNIPPET?: boolean;
|
||||||
|
GIT?: boolean;
|
||||||
|
GIT_IMPORT?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeatureFlags;
|
||||||
|
|
@ -27,7 +27,7 @@ import { getIsImportingApplication } from "selectors/applicationSelectors";
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import Dialog from "../../components/ads/DialogComponent";
|
import Dialog from "../../components/ads/DialogComponent";
|
||||||
import { Classes } from "@blueprintjs/core";
|
import { Classes } from "@blueprintjs/core";
|
||||||
import getFeatureFlags from "../../utils/featureFlags";
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
|
||||||
const StyledDialog = styled(Dialog)`
|
const StyledDialog = styled(Dialog)`
|
||||||
&& .${Classes.DIALOG_HEADER} {
|
&& .${Classes.DIALOG_HEADER} {
|
||||||
|
|
@ -259,7 +259,9 @@ function ImportApplicationModal(props: ImportApplicationModalProps) {
|
||||||
}, [appFileToBeUploaded, importingApplication]);
|
}, [appFileToBeUploaded, importingApplication]);
|
||||||
|
|
||||||
const onRemoveFile = useCallback(() => setAppFileToBeUploaded(null), []);
|
const onRemoveFile = useCallback(() => setAppFileToBeUploaded(null), []);
|
||||||
const isGitImportFeatureEnabled = getFeatureFlags().GIT_IMPORT;
|
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
const { GIT_IMPORT: isGitImportFeatureEnabled } = featureFlags;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDialog
|
<StyledDialog
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ import { isPermitted, PERMISSION_TYPE } from "./permissionHelpers";
|
||||||
import FormDialogComponent from "components/editorComponents/form/FormDialogComponent";
|
import FormDialogComponent from "components/editorComponents/form/FormDialogComponent";
|
||||||
import Dialog from "components/ads/DialogComponent";
|
import Dialog from "components/ads/DialogComponent";
|
||||||
import { User } from "constants/userConstants";
|
import { User } from "constants/userConstants";
|
||||||
import { getCurrentUser } from "selectors/usersSelectors";
|
import { getCurrentUser, selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
import { CREATE_ORGANIZATION_FORM_NAME } from "constants/forms";
|
import { CREATE_ORGANIZATION_FORM_NAME } from "constants/forms";
|
||||||
import {
|
import {
|
||||||
DropdownOnSelectActions,
|
DropdownOnSelectActions,
|
||||||
|
|
@ -85,7 +85,6 @@ import {
|
||||||
import { ReactComponent as NoAppsFoundIcon } from "assets/svg/no-apps-icon.svg";
|
import { ReactComponent as NoAppsFoundIcon } from "assets/svg/no-apps-icon.svg";
|
||||||
|
|
||||||
import { setHeaderMeta } from "actions/themeActions";
|
import { setHeaderMeta } from "actions/themeActions";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import SharedUserList from "pages/common/SharedUserList";
|
import SharedUserList from "pages/common/SharedUserList";
|
||||||
import { useIsMobileDevice } from "utils/hooks/useDeviceDetect";
|
import { useIsMobileDevice } from "utils/hooks/useDeviceDetect";
|
||||||
import { Indices } from "constants/Layers";
|
import { Indices } from "constants/Layers";
|
||||||
|
|
@ -523,6 +522,7 @@ function ApplicationsSection(props: any) {
|
||||||
) => {
|
) => {
|
||||||
dispatch(updateApplication(id, data));
|
dispatch(updateApplication(id, data));
|
||||||
};
|
};
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Clears URL params cache
|
// Clears URL params cache
|
||||||
|
|
@ -931,7 +931,7 @@ function ApplicationsSection(props: any) {
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
>
|
>
|
||||||
{organizationsListComponent}
|
{organizationsListComponent}
|
||||||
{getFeatureFlags().GIT_IMPORT && <GitSyncModal isImport />}
|
{featureFlags.GIT_IMPORT && <GitSyncModal isImport />}
|
||||||
<ReconnectDatasourceModal />
|
<ReconnectDatasourceModal />
|
||||||
</ApplicationContainer>
|
</ApplicationContainer>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import {
|
||||||
} from "../../Applications/permissionHelpers";
|
} from "../../Applications/permissionHelpers";
|
||||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
|
import { setIsGitSyncModalOpen } from "actions/gitSyncActions";
|
||||||
import { GitSyncModalTab } from "entities/GitSync";
|
import { GitSyncModalTab } from "entities/GitSync";
|
||||||
import { getIsGitConnected } from "selectors/gitSyncSelectors";
|
import { getIsGitConnected } from "selectors/gitSyncSelectors";
|
||||||
|
|
@ -38,6 +37,7 @@ import { redoAction, undoAction } from "actions/pageActions";
|
||||||
import { redoShortCut, undoShortCut } from "utils/helpers";
|
import { redoShortCut, undoShortCut } from "utils/helpers";
|
||||||
import { pageListEditorURL } from "RouteBuilder";
|
import { pageListEditorURL } from "RouteBuilder";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
|
||||||
type NavigationMenuDataProps = ThemeProp & {
|
type NavigationMenuDataProps = ThemeProp & {
|
||||||
editMode: typeof noop;
|
editMode: typeof noop;
|
||||||
|
|
@ -122,7 +122,9 @@ export const GetNavigationMenuData = ({
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (getFeatureFlags().GIT && !isGitConnected) {
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
|
if (featureFlags.GIT && !isGitConnected) {
|
||||||
deployOptions.push({
|
deployOptions.push({
|
||||||
text: createMessage(CONNECT_TO_GIT_OPTION),
|
text: createMessage(CONNECT_TO_GIT_OPTION),
|
||||||
onClick: () => openGitConnectionPopup(),
|
onClick: () => openGitConnectionPopup(),
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import {
|
||||||
} from "selectors/applicationSelectors";
|
} from "selectors/applicationSelectors";
|
||||||
import EditorAppName from "./EditorAppName";
|
import EditorAppName from "./EditorAppName";
|
||||||
import ProfileDropdown from "pages/common/ProfileDropdown";
|
import ProfileDropdown from "pages/common/ProfileDropdown";
|
||||||
import { getCurrentUser } from "selectors/usersSelectors";
|
import { getCurrentUser, selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
import { ANONYMOUS_USERNAME, User } from "constants/userConstants";
|
import { ANONYMOUS_USERNAME, User } from "constants/userConstants";
|
||||||
import Button, { Size } from "components/ads/Button";
|
import Button, { Size } from "components/ads/Button";
|
||||||
import Icon, { IconSize } from "components/ads/Icon";
|
import Icon, { IconSize } from "components/ads/Icon";
|
||||||
|
|
@ -52,7 +52,6 @@ import { useLocation } from "react-router";
|
||||||
import { showConnectGitModal } from "actions/gitSyncActions";
|
import { showConnectGitModal } from "actions/gitSyncActions";
|
||||||
import RealtimeAppEditors from "./RealtimeAppEditors";
|
import RealtimeAppEditors from "./RealtimeAppEditors";
|
||||||
import { EditorSaveIndicator } from "./EditorSaveIndicator";
|
import { EditorSaveIndicator } from "./EditorSaveIndicator";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
|
|
||||||
import { retryPromise } from "utils/AppsmithUtils";
|
import { retryPromise } from "utils/AppsmithUtils";
|
||||||
import { fetchUsersForOrg } from "actions/orgActions";
|
import { fetchUsersForOrg } from "actions/orgActions";
|
||||||
|
|
@ -298,9 +297,11 @@ export function EditorHeader(props: EditorHeaderProps) {
|
||||||
showAppInviteUsersDialogSelector,
|
showAppInviteUsersDialogSelector,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
const handleClickDeploy = useCallback(
|
const handleClickDeploy = useCallback(
|
||||||
(fromDeploy?: boolean) => {
|
(fromDeploy?: boolean) => {
|
||||||
if (getFeatureFlags().GIT && isGitConnected) {
|
if (featureFlags.GIT && isGitConnected) {
|
||||||
dispatch(showConnectGitModal());
|
dispatch(showConnectGitModal());
|
||||||
AnalyticsUtil.logEvent("GS_DEPLOY_GIT_CLICK", {
|
AnalyticsUtil.logEvent("GS_DEPLOY_GIT_CLICK", {
|
||||||
source: fromDeploy
|
source: fromDeploy
|
||||||
|
|
@ -311,7 +312,7 @@ export function EditorHeader(props: EditorHeaderProps) {
|
||||||
handlePublish();
|
handlePublish();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getFeatureFlags().GIT, dispatch, handlePublish],
|
[featureFlags.GIT, dispatch, handlePublish],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import {
|
||||||
showConnectGitModal,
|
showConnectGitModal,
|
||||||
} from "actions/gitSyncActions";
|
} from "actions/gitSyncActions";
|
||||||
import { GitSyncModalTab } from "entities/GitSync";
|
import { GitSyncModalTab } from "entities/GitSync";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import {
|
import {
|
||||||
getCountOfChangesToCommit,
|
getCountOfChangesToCommit,
|
||||||
getGitStatus,
|
getGitStatus,
|
||||||
|
|
@ -45,6 +44,7 @@ import SpinnerLoader from "pages/common/SpinnerLoader";
|
||||||
import { inGuidedTour } from "selectors/onboardingSelectors";
|
import { inGuidedTour } from "selectors/onboardingSelectors";
|
||||||
import Icon, { IconName, IconSize } from "components/ads/Icon";
|
import Icon, { IconName, IconSize } from "components/ads/Icon";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
|
||||||
type QuickActionButtonProps = {
|
type QuickActionButtonProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
@ -229,8 +229,9 @@ const PlaceholderButton = styled.div`
|
||||||
function ConnectGitPlaceholder() {
|
function ConnectGitPlaceholder() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isInGuidedTour = useSelector(inGuidedTour);
|
const isInGuidedTour = useSelector(inGuidedTour);
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
const isTooltipEnabled = !getFeatureFlags().GIT || isInGuidedTour;
|
const isTooltipEnabled = !featureFlags.GIT || isInGuidedTour;
|
||||||
const tooltipContent = !isInGuidedTour ? (
|
const tooltipContent = !isInGuidedTour ? (
|
||||||
<>
|
<>
|
||||||
<div>{createMessage(NOT_LIVE_FOR_YOU_YET)}</div>
|
<div>{createMessage(NOT_LIVE_FOR_YOU_YET)}</div>
|
||||||
|
|
@ -242,7 +243,7 @@ function ConnectGitPlaceholder() {
|
||||||
<div>{createMessage(DURING_ONBOARDING_TOUR)}</div>
|
<div>{createMessage(DURING_ONBOARDING_TOUR)}</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
const isGitConnectionEnabled = getFeatureFlags().GIT && !isInGuidedTour;
|
const isGitConnectionEnabled = featureFlags.GIT && !isInGuidedTour;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|
@ -297,6 +298,7 @@ export default function QuickGitActions() {
|
||||||
const isFetchingGitStatus = useSelector(getIsFetchingGitStatus);
|
const isFetchingGitStatus = useSelector(getIsFetchingGitStatus);
|
||||||
const showPullLoadingState = isPullInProgress || isFetchingGitStatus;
|
const showPullLoadingState = isPullInProgress || isFetchingGitStatus;
|
||||||
const changesToCommit = useSelector(getCountOfChangesToCommit);
|
const changesToCommit = useSelector(getCountOfChangesToCommit);
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
const quickActionButtons = getQuickActionButtons({
|
const quickActionButtons = getQuickActionButtons({
|
||||||
commit: () => {
|
commit: () => {
|
||||||
|
|
@ -345,7 +347,7 @@ export default function QuickGitActions() {
|
||||||
showPullLoadingState,
|
showPullLoadingState,
|
||||||
changesToCommit,
|
changesToCommit,
|
||||||
});
|
});
|
||||||
return getFeatureFlags().GIT && isGitConnected ? (
|
return featureFlags.GIT && isGitConnected ? (
|
||||||
<Container>
|
<Container>
|
||||||
<BranchButton />
|
<BranchButton />
|
||||||
{quickActionButtons.map((button) => (
|
{quickActionButtons.map((button) => (
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default function WithSuperUserHOC(
|
||||||
) {
|
) {
|
||||||
return function Wrapped(props: RouteComponentProps) {
|
return function Wrapped(props: RouteComponentProps) {
|
||||||
const user = useSelector(getCurrentUser);
|
const user = useSelector(getCurrentUser);
|
||||||
|
if (!user) return null;
|
||||||
if (!user?.isSuperUser || !user?.isConfigurable) {
|
if (!user?.isSuperUser || !user?.isConfigurable) {
|
||||||
return <Redirect to={APPLICATIONS_URL} />;
|
return <Redirect to={APPLICATIONS_URL} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { APPLICATIONS_URL, AUTH_LOGIN_URL } from "constants/routes";
|
||||||
export const requiresUnauth = (Component: React.ComponentType) => {
|
export const requiresUnauth = (Component: React.ComponentType) => {
|
||||||
function Wrapped(props: any) {
|
function Wrapped(props: any) {
|
||||||
const user = useSelector(getCurrentUser);
|
const user = useSelector(getCurrentUser);
|
||||||
|
if (!user) return null;
|
||||||
if (user?.email && user?.email !== ANONYMOUS_USERNAME) {
|
if (user?.email && user?.email !== ANONYMOUS_USERNAME) {
|
||||||
return <Redirect to={APPLICATIONS_URL} />;
|
return <Redirect to={APPLICATIONS_URL} />;
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ export const requiresUnauth = (Component: React.ComponentType) => {
|
||||||
export const requiresAuth = (Component: React.ComponentType) => {
|
export const requiresAuth = (Component: React.ComponentType) => {
|
||||||
return function Wrapped(props: any) {
|
return function Wrapped(props: any) {
|
||||||
const user = useSelector(getCurrentUser);
|
const user = useSelector(getCurrentUser);
|
||||||
|
if (!user) return null;
|
||||||
if (user?.email && user?.email !== ANONYMOUS_USERNAME) {
|
if (user?.email && user?.email !== ANONYMOUS_USERNAME) {
|
||||||
return <Component {...props} />;
|
return <Component {...props} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ import { Icon } from "@blueprintjs/core";
|
||||||
// import { Link } from "react-router-dom";
|
// import { Link } from "react-router-dom";
|
||||||
import General from "./General";
|
import General from "./General";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import GitConfig from "./GitConfig";
|
import GitConfig from "./GitConfig";
|
||||||
import { useLocation } from "react-router";
|
import { useLocation } from "react-router";
|
||||||
import { GIT_PROFILE_ROUTE } from "constants/routes";
|
import { GIT_PROFILE_ROUTE } from "constants/routes";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
|
||||||
const ProfileWrapper = styled.div`
|
const ProfileWrapper = styled.div`
|
||||||
width: ${(props) => props.theme.pageContentWidth}px;
|
width: ${(props) => props.theme.pageContentWidth}px;
|
||||||
|
|
@ -32,6 +33,7 @@ const LinkToApplications = styled.div`
|
||||||
|
|
||||||
function UserProfile() {
|
function UserProfile() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
let initialTabIndex = 0;
|
let initialTabIndex = 0;
|
||||||
const tabs: TabProp[] = [
|
const tabs: TabProp[] = [
|
||||||
|
|
@ -43,7 +45,7 @@ function UserProfile() {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (getFeatureFlags().GIT) {
|
if (featureFlags.GIT) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
key: "gitConfig",
|
key: "gitConfig",
|
||||||
title: "Git user config",
|
title: "Git user config",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState, useMemo, useEffect } from "react";
|
import React, { useState, useMemo, useEffect } from "react";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { connect, useDispatch } from "react-redux";
|
import { connect, useDispatch, useSelector } from "react-redux";
|
||||||
import { getCurrentUser } from "selectors/usersSelectors";
|
import { getCurrentUser, selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import StyledHeader from "components/designSystems/appsmith/StyledHeader";
|
import StyledHeader from "components/designSystems/appsmith/StyledHeader";
|
||||||
import { ReactComponent as AppsmithLogo } from "assets/svg/appsmith_logo_primary.svg";
|
import { ReactComponent as AppsmithLogo } from "assets/svg/appsmith_logo_primary.svg";
|
||||||
|
|
@ -28,7 +28,6 @@ import { Indices } from "constants/Layers";
|
||||||
import Icon, { IconSize } from "components/ads/Icon";
|
import Icon, { IconSize } from "components/ads/Icon";
|
||||||
import { TemplatesTabItem } from "pages/Templates/TemplatesTabItem";
|
import { TemplatesTabItem } from "pages/Templates/TemplatesTabItem";
|
||||||
import { getTemplateNotificationSeenAction } from "actions/templateActions";
|
import { getTemplateNotificationSeenAction } from "actions/templateActions";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
|
|
||||||
const StyledPageHeader = styled(StyledHeader)<{
|
const StyledPageHeader = styled(StyledHeader)<{
|
||||||
hideShadow?: boolean;
|
hideShadow?: boolean;
|
||||||
|
|
@ -131,6 +130,8 @@ export function PageHeader(props: PageHeaderProps) {
|
||||||
=${queryParams.get("redirectUrl")}`;
|
=${queryParams.get("redirectUrl")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(getTemplateNotificationSeenAction());
|
dispatch(getTemplateNotificationSeenAction());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -156,9 +157,9 @@ export function PageHeader(props: PageHeaderProps) {
|
||||||
const showTabs = useMemo(() => {
|
const showTabs = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
tabs.some((tab) => tab.matcher(location.pathname)) &&
|
tabs.some((tab) => tab.matcher(location.pathname)) &&
|
||||||
getFeatureFlags().APP_TEMPLATE
|
!!featureFlags.APP_TEMPLATE
|
||||||
);
|
);
|
||||||
}, [location.pathname]);
|
}, [featureFlags, location.pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPageHeader
|
<StyledPageHeader
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
DefaultCurrentUserDetails,
|
DefaultCurrentUserDetails,
|
||||||
User,
|
User,
|
||||||
} from "constants/userConstants";
|
} from "constants/userConstants";
|
||||||
|
import FeatureFlags from "entities/FeatureFlags";
|
||||||
|
|
||||||
const initialState: UsersReduxState = {
|
const initialState: UsersReduxState = {
|
||||||
loadingStates: {
|
loadingStates: {
|
||||||
|
|
@ -22,7 +23,10 @@ const initialState: UsersReduxState = {
|
||||||
error: "",
|
error: "",
|
||||||
current: undefined,
|
current: undefined,
|
||||||
currentUser: undefined,
|
currentUser: undefined,
|
||||||
featureFlagFetched: false,
|
featureFlag: {
|
||||||
|
data: {},
|
||||||
|
isFetched: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const usersReducer = createReducer(initialState, {
|
const usersReducer = createReducer(initialState, {
|
||||||
|
|
@ -155,15 +159,24 @@ const usersReducer = createReducer(initialState, {
|
||||||
photoId: action.payload.photoId,
|
photoId: action.payload.photoId,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS]: (state: UsersReduxState) => ({
|
[ReduxActionTypes.FETCH_FEATURE_FLAGS_SUCCESS]: (
|
||||||
|
state: UsersReduxState,
|
||||||
|
action: ReduxAction<FeatureFlags>,
|
||||||
|
) => ({
|
||||||
...state,
|
...state,
|
||||||
featureFlagFetched: true,
|
featureFlag: {
|
||||||
|
data: action.payload,
|
||||||
|
isFetched: true,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[ReduxActionErrorTypes.FETCH_FEATURE_FLAGS_ERROR]: (
|
[ReduxActionErrorTypes.FETCH_FEATURE_FLAGS_ERROR]: (
|
||||||
state: UsersReduxState,
|
state: UsersReduxState,
|
||||||
) => ({
|
) => ({
|
||||||
...state,
|
...state,
|
||||||
featureFlagFetched: true,
|
featureFlag: {
|
||||||
|
data: {},
|
||||||
|
isFetched: true,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[ReduxActionTypes.UPDATE_USERS_COMMENTS_ONBOARDING_STATE]: (
|
[ReduxActionTypes.UPDATE_USERS_COMMENTS_ONBOARDING_STATE]: (
|
||||||
state: UsersReduxState,
|
state: UsersReduxState,
|
||||||
|
|
@ -195,7 +208,10 @@ export interface UsersReduxState {
|
||||||
currentUser?: User;
|
currentUser?: User;
|
||||||
error: string;
|
error: string;
|
||||||
propPanePreferences?: PropertyPanePositionConfig;
|
propPanePreferences?: PropertyPanePositionConfig;
|
||||||
featureFlagFetched: boolean;
|
featureFlag: {
|
||||||
|
isFetched: boolean;
|
||||||
|
data: FeatureFlags;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default usersReducer;
|
export default usersReducer;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ import { getAppMode } from "selectors/applicationSelectors";
|
||||||
import { APP_MODE } from "entities/App";
|
import { APP_MODE } from "entities/App";
|
||||||
import { dataTreeTypeDefCreator } from "utils/autocomplete/dataTreeTypeDefCreator";
|
import { dataTreeTypeDefCreator } from "utils/autocomplete/dataTreeTypeDefCreator";
|
||||||
import TernServer from "utils/autocomplete/TernServer";
|
import TernServer from "utils/autocomplete/TernServer";
|
||||||
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
||||||
|
import FeatureFlags from "entities/FeatureFlags";
|
||||||
|
|
||||||
const getDebuggerErrors = (state: AppState) => state.ui.debugger.errors;
|
const getDebuggerErrors = (state: AppState) => state.ui.debugger.errors;
|
||||||
/**
|
/**
|
||||||
|
|
@ -301,7 +303,7 @@ export function* logSuccessfulBindings(
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
evaluationOrder: string[],
|
evaluationOrder: string[],
|
||||||
) {
|
) {
|
||||||
const appMode = yield select(getAppMode);
|
const appMode: APP_MODE = yield select(getAppMode);
|
||||||
if (appMode === APP_MODE.PUBLISHED) return;
|
if (appMode === APP_MODE.PUBLISHED) return;
|
||||||
if (!evaluationOrder) return;
|
if (!evaluationOrder) return;
|
||||||
evaluationOrder.forEach((evaluatedPath) => {
|
evaluationOrder.forEach((evaluatedPath) => {
|
||||||
|
|
@ -371,8 +373,10 @@ export function* updateTernDefinitions(
|
||||||
const treeWithoutPrivateWidgets = getDataTreeWithoutPrivateWidgets(
|
const treeWithoutPrivateWidgets = getDataTreeWithoutPrivateWidgets(
|
||||||
dataTree,
|
dataTree,
|
||||||
);
|
);
|
||||||
|
const featureFlags: FeatureFlags = yield select(selectFeatureFlags);
|
||||||
const { def, entityInfo } = dataTreeTypeDefCreator(
|
const { def, entityInfo } = dataTreeTypeDefCreator(
|
||||||
treeWithoutPrivateWidgets,
|
treeWithoutPrivateWidgets,
|
||||||
|
!!featureFlags.JS_EDITOR,
|
||||||
);
|
);
|
||||||
TernServer.updateDef("DATA_TREE", def, entityInfo);
|
TernServer.updateDef("DATA_TREE", def, entityInfo);
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@ import {
|
||||||
updateReflowOnBoardingAction,
|
updateReflowOnBoardingAction,
|
||||||
} from "actions/reflowActions";
|
} from "actions/reflowActions";
|
||||||
import {
|
import {
|
||||||
|
ReduxAction,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
ReflowReduxActionTypes,
|
ReflowReduxActionTypes,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { User } from "constants/userConstants";
|
import { User } from "constants/userConstants";
|
||||||
import { isBoolean } from "lodash";
|
import { isBoolean } from "lodash";
|
||||||
import { all, put, select, takeLatest } from "redux-saga/effects";
|
import { all, put, select, takeLatest, take } from "redux-saga/effects";
|
||||||
import { getCurrentUser } from "selectors/usersSelectors";
|
import { getCurrentUser } from "selectors/usersSelectors";
|
||||||
import {
|
import {
|
||||||
getReflowBetaFlag,
|
getReflowBetaFlag,
|
||||||
|
|
@ -20,7 +21,13 @@ import {
|
||||||
|
|
||||||
function* initReflowStates() {
|
function* initReflowStates() {
|
||||||
try {
|
try {
|
||||||
const user: User = yield select(getCurrentUser);
|
let user: User = yield select(getCurrentUser);
|
||||||
|
if (!user) {
|
||||||
|
const userFetched: ReduxAction<User> = yield take(
|
||||||
|
ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
||||||
|
);
|
||||||
|
user = userFetched.payload;
|
||||||
|
}
|
||||||
const { email } = user;
|
const { email } = user;
|
||||||
if (email) {
|
if (email) {
|
||||||
const enableReflow: boolean = yield getReflowBetaFlag(email);
|
const enableReflow: boolean = yield getReflowBetaFlag(email);
|
||||||
|
|
@ -62,8 +69,8 @@ function* closeReflowOnboardingCard() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function* reflowSagas() {
|
export default function* reflowSagas() {
|
||||||
yield all([takeLatest(ReduxActionTypes.INITIALIZE_EDITOR, initReflowStates)]);
|
|
||||||
yield all([
|
yield all([
|
||||||
|
takeLatest(ReduxActionTypes.INITIALIZE_EDITOR, initReflowStates),
|
||||||
takeLatest(
|
takeLatest(
|
||||||
ReflowReduxActionTypes.CLOSE_ONBOARDING_CARD,
|
ReflowReduxActionTypes.CLOSE_ONBOARDING_CARD,
|
||||||
closeReflowOnboardingCard,
|
closeReflowOnboardingCard,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { call, all, spawn } from "redux-saga/effects";
|
import { call, all, spawn, race, take } from "redux-saga/effects";
|
||||||
import pageSagas from "sagas/PageSagas";
|
import pageSagas from "sagas/PageSagas";
|
||||||
import { watchActionSagas } from "./ActionSagas";
|
import { watchActionSagas } from "./ActionSagas";
|
||||||
import { watchJSActionSagas } from "./JSActionSagas";
|
import { watchJSActionSagas } from "./JSActionSagas";
|
||||||
|
|
@ -45,6 +45,7 @@ import * as sentry from "@sentry/react";
|
||||||
import formEvaluationChangeListener from "./FormEvaluationSaga";
|
import formEvaluationChangeListener from "./FormEvaluationSaga";
|
||||||
import SuperUserSagas from "./SuperUserSagas";
|
import SuperUserSagas from "./SuperUserSagas";
|
||||||
import reflowSagas from "./ReflowSagas";
|
import reflowSagas from "./ReflowSagas";
|
||||||
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
|
|
||||||
const sagas = [
|
const sagas = [
|
||||||
initSagas,
|
initSagas,
|
||||||
|
|
@ -92,20 +93,26 @@ const sagas = [
|
||||||
reflowSagas,
|
reflowSagas,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function* rootSaga(sagasToRun = sagas) {
|
export function* rootSaga(sagasToRun = sagas): any {
|
||||||
yield all(
|
// This race effect ensures that we fail as soon as the first safe crash is dispatched.
|
||||||
sagasToRun.map((saga) =>
|
// Without this, all the subsequent safe crash failures would be shown in the toast messages as well.
|
||||||
spawn(function*() {
|
const result = yield race({
|
||||||
while (true) {
|
running: all(
|
||||||
try {
|
sagasToRun.map((saga) =>
|
||||||
yield call(saga);
|
spawn(function*() {
|
||||||
break;
|
while (true) {
|
||||||
} catch (e) {
|
try {
|
||||||
log.error(e);
|
yield call(saga);
|
||||||
sentry.captureException(e);
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
log.error(e);
|
||||||
|
sentry.captureException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
);
|
crashed: take(ReduxActionTypes.SAFE_CRASH_APPSMITH),
|
||||||
|
});
|
||||||
|
if (result.crashed) yield call(rootSaga);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,7 @@ import UserApi, {
|
||||||
UpdateUserRequest,
|
UpdateUserRequest,
|
||||||
LeaveOrgRequest,
|
LeaveOrgRequest,
|
||||||
} from "api/UserApi";
|
} from "api/UserApi";
|
||||||
import {
|
import { AUTH_LOGIN_URL, SETUP } from "constants/routes";
|
||||||
APPLICATIONS_URL,
|
|
||||||
AUTH_LOGIN_URL,
|
|
||||||
BASE_URL,
|
|
||||||
SETUP,
|
|
||||||
} from "constants/routes";
|
|
||||||
import history from "utils/history";
|
import history from "utils/history";
|
||||||
import { ApiResponse } from "api/ApiResponses";
|
import { ApiResponse } from "api/ApiResponses";
|
||||||
import {
|
import {
|
||||||
|
|
@ -37,7 +32,6 @@ import {
|
||||||
invitedUserSignupSuccess,
|
invitedUserSignupSuccess,
|
||||||
fetchFeatureFlagsSuccess,
|
fetchFeatureFlagsSuccess,
|
||||||
fetchFeatureFlagsError,
|
fetchFeatureFlagsError,
|
||||||
fetchFeatureFlagsInit,
|
|
||||||
} from "actions/userActions";
|
} from "actions/userActions";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { INVITE_USERS_TO_ORG_FORM } from "constants/forms";
|
import { INVITE_USERS_TO_ORG_FORM } from "constants/forms";
|
||||||
|
|
@ -125,11 +119,6 @@ export function* getCurrentUserSaga() {
|
||||||
response.data.username !== ANONYMOUS_USERNAME
|
response.data.username !== ANONYMOUS_USERNAME
|
||||||
) {
|
) {
|
||||||
enableTelemetry && AnalyticsUtil.identifyUser(response.data);
|
enableTelemetry && AnalyticsUtil.identifyUser(response.data);
|
||||||
// make fetch feature call only if logged in
|
|
||||||
yield put(fetchFeatureFlagsInit());
|
|
||||||
} else {
|
|
||||||
// reset the flagsFetched flag
|
|
||||||
yield put(fetchFeatureFlagsSuccess());
|
|
||||||
}
|
}
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
type: ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
||||||
|
|
@ -137,12 +126,6 @@ export function* getCurrentUserSaga() {
|
||||||
});
|
});
|
||||||
if (response.data.emptyInstance) {
|
if (response.data.emptyInstance) {
|
||||||
history.replace(SETUP);
|
history.replace(SETUP);
|
||||||
} else if (window.location.pathname === BASE_URL) {
|
|
||||||
if (response.data.isAnonymous) {
|
|
||||||
history.replace(AUTH_LOGIN_URL);
|
|
||||||
} else {
|
|
||||||
history.replace(APPLICATIONS_URL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PerformanceTracker.stopAsyncTracking(
|
PerformanceTracker.stopAsyncTracking(
|
||||||
PerformanceTransactionName.USER_ME_API,
|
PerformanceTransactionName.USER_ME_API,
|
||||||
|
|
@ -161,7 +144,7 @@ export function* getCurrentUserSaga() {
|
||||||
});
|
});
|
||||||
|
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.SAFE_CRASH_APPSMITH,
|
type: ReduxActionTypes.SAFE_CRASH_APPSMITH_REQUEST,
|
||||||
payload: {
|
payload: {
|
||||||
code: ERROR_CODES.SERVER_ERROR,
|
code: ERROR_CODES.SERVER_ERROR,
|
||||||
},
|
},
|
||||||
|
|
@ -441,8 +424,7 @@ function* fetchFeatureFlags() {
|
||||||
const response: ApiResponse = yield call(UserApi.fetchFeatureFlags);
|
const response: ApiResponse = yield call(UserApi.fetchFeatureFlags);
|
||||||
const isValidResponse: boolean = yield validateResponse(response);
|
const isValidResponse: boolean = yield validateResponse(response);
|
||||||
if (isValidResponse) {
|
if (isValidResponse) {
|
||||||
(window as any).FEATURE_FLAGS = response.data;
|
yield put(fetchFeatureFlagsSuccess(response.data));
|
||||||
yield put(fetchFeatureFlagsSuccess());
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { AppCollabReducerState } from "reducers/uiReducers/appCollabReducer";
|
import { AppCollabReducerState } from "reducers/uiReducers/appCollabReducer";
|
||||||
import { getCurrentUser } from "./usersSelectors";
|
import { getCurrentUser, selectFeatureFlags } from "./usersSelectors";
|
||||||
import getFeatureFlags from "../utils/featureFlags";
|
|
||||||
import { User } from "entities/AppCollab/CollabInterfaces";
|
import { User } from "entities/AppCollab/CollabInterfaces";
|
||||||
import { ANONYMOUS_USERNAME } from "constants/userConstants";
|
import { ANONYMOUS_USERNAME } from "constants/userConstants";
|
||||||
|
|
||||||
|
|
@ -15,7 +14,10 @@ export const getRealtimeAppEditors = createSelector(
|
||||||
appCollab.editors.filter((el) => el.email !== currentUser?.email),
|
appCollab.editors.filter((el) => el.email !== currentUser?.email),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const isMultiplayerEnabledForUser = () => getFeatureFlags().MULTIPLAYER;
|
export const isMultiplayerEnabledForUser = createSelector(
|
||||||
|
selectFeatureFlags,
|
||||||
|
(featureFlags) => featureFlags.MULTIPLAYER,
|
||||||
|
);
|
||||||
|
|
||||||
export const getConcurrentPageEditors = (state: AppState) =>
|
export const getConcurrentPageEditors = (state: AppState) =>
|
||||||
state.ui.appCollab.pageEditors;
|
state.ui.appCollab.pageEditors;
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ import { JSCollectionDataState } from "reducers/entityReducers/jsActionsReducer"
|
||||||
import { JSCollection } from "entities/JSCollection";
|
import { JSCollection } from "entities/JSCollection";
|
||||||
import { DefaultPlugin, GenerateCRUDEnabledPluginMap } from "../api/PluginApi";
|
import { DefaultPlugin, GenerateCRUDEnabledPluginMap } from "../api/PluginApi";
|
||||||
import { APP_MODE } from "entities/App";
|
import { APP_MODE } from "entities/App";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
import { ExplorerFileEntity } from "pages/Editor/Explorer/helpers";
|
import { ExplorerFileEntity } from "pages/Editor/Explorer/helpers";
|
||||||
import { ActionValidationConfigMap } from "constants/PropertyControlConstants";
|
import { ActionValidationConfigMap } from "constants/PropertyControlConstants";
|
||||||
|
import { selectFeatureFlags } from "./usersSelectors";
|
||||||
|
|
||||||
export const getEntities = (state: AppState): AppState["entities"] =>
|
export const getEntities = (state: AppState): AppState["entities"] =>
|
||||||
state.entities;
|
state.entities;
|
||||||
|
|
@ -663,8 +663,9 @@ export const selectFilesForExplorer = createSelector(
|
||||||
getActionsForCurrentPage,
|
getActionsForCurrentPage,
|
||||||
getJSCollectionsForCurrentPage,
|
getJSCollectionsForCurrentPage,
|
||||||
selectDatasourceIdToNameMap,
|
selectDatasourceIdToNameMap,
|
||||||
(actions, jsActions, datasourceIdToNameMap) => {
|
selectFeatureFlags,
|
||||||
const isJSEditorEnabled = getFeatureFlags().JS_EDITOR;
|
(actions, jsActions, datasourceIdToNameMap, featureFlags) => {
|
||||||
|
const { JS_EDITOR: isJSEditorEnabled } = featureFlags;
|
||||||
const files = [...actions, ...(isJSEditorEnabled ? jsActions : [])].reduce(
|
const files = [...actions, ...(isJSEditorEnabled ? jsActions : [])].reduce(
|
||||||
(acc, file) => {
|
(acc, file) => {
|
||||||
let group = "";
|
let group = "";
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,7 @@ export const getProppanePreference = (
|
||||||
state: AppState,
|
state: AppState,
|
||||||
): PropertyPanePositionConfig | undefined => state.ui.users.propPanePreferences;
|
): PropertyPanePositionConfig | undefined => state.ui.users.propPanePreferences;
|
||||||
export const getFeatureFlagsFetched = (state: AppState) =>
|
export const getFeatureFlagsFetched = (state: AppState) =>
|
||||||
state.ui.users.featureFlagFetched;
|
state.ui.users.featureFlag.isFetched;
|
||||||
|
|
||||||
|
export const selectFeatureFlags = (state: AppState) =>
|
||||||
|
state.ui.users.featureFlag.data;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import {
|
||||||
isWidget,
|
isWidget,
|
||||||
} from "workers/evaluationUtils";
|
} from "workers/evaluationUtils";
|
||||||
import { DataTreeDefEntityInformation } from "utils/autocomplete/TernServer";
|
import { DataTreeDefEntityInformation } from "utils/autocomplete/TernServer";
|
||||||
import getFeatureFlags from "utils/featureFlags";
|
|
||||||
// When there is a complex data type, we store it in extra def and refer to it
|
// When there is a complex data type, we store it in extra def and refer to it
|
||||||
// in the def
|
// in the def
|
||||||
let extraDefs: any = {};
|
let extraDefs: any = {};
|
||||||
|
|
@ -27,12 +26,12 @@ let extraDefs: any = {};
|
||||||
// or DATA_TREE.ACTION.ACTION.Api1
|
// or DATA_TREE.ACTION.ACTION.Api1
|
||||||
export const dataTreeTypeDefCreator = (
|
export const dataTreeTypeDefCreator = (
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
|
isJSEditorEnabled: boolean,
|
||||||
): { def: Def; entityInfo: Map<string, DataTreeDefEntityInformation> } => {
|
): { def: Def; entityInfo: Map<string, DataTreeDefEntityInformation> } => {
|
||||||
const def: any = {
|
const def: any = {
|
||||||
"!name": "DATA_TREE",
|
"!name": "DATA_TREE",
|
||||||
};
|
};
|
||||||
const entityMap: Map<string, DataTreeDefEntityInformation> = new Map();
|
const entityMap: Map<string, DataTreeDefEntityInformation> = new Map();
|
||||||
const isJSEditorEnabled = getFeatureFlags().JS_EDITOR;
|
|
||||||
Object.entries(dataTree).forEach(([entityName, entity]) => {
|
Object.entries(dataTree).forEach(([entityName, entity]) => {
|
||||||
if (isWidget(entity)) {
|
if (isWidget(entity)) {
|
||||||
const widgetType = entity.type;
|
const widgetType = entity.type;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import FeatureFlag from "entities/FeatureFlag";
|
|
||||||
|
|
||||||
export default function getFeatureFlags(): FeatureFlag {
|
|
||||||
return (window as any).FEATURE_FLAGS || {};
|
|
||||||
}
|
|
||||||
|
|
@ -121,6 +121,7 @@ public class SecurityConfig {
|
||||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/invite/verify"),
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/invite/verify"),
|
||||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.PUT, USER_URL + "/invite/confirm"),
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.PUT, USER_URL + "/invite/confirm"),
|
||||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/me"),
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/me"),
|
||||||
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/features"),
|
||||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_URL + "/**"),
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_URL + "/**"),
|
||||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_COLLECTION_URL + "/view"),
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_COLLECTION_URL + "/view"),
|
||||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, PAGE_URL + "/**"),
|
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, PAGE_URL + "/**"),
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,10 @@ public class FeatureFlagServiceCEImpl implements FeatureFlagServiceCE {
|
||||||
.flatMap(featureName -> Mono.just(featureName).zipWith(currentUser));
|
.flatMap(featureName -> Mono.just(featureName).zipWith(currentUser));
|
||||||
|
|
||||||
return featureUserTuple
|
return featureUserTuple
|
||||||
.collectMap(tuple -> tuple.getT1(), tuple -> check(tuple.getT1(), tuple.getT2()));
|
.filter(objects -> !objects.getT2().isAnonymous())
|
||||||
|
.collectMap(
|
||||||
|
Tuple2::getT1,
|
||||||
|
tuple -> check(tuple.getT1(), tuple.getT2())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user