feat: Created component for ai signposting (#25187)
## Description feat: Created component for ai signposting #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
This commit is contained in:
parent
0bc1213795
commit
6dcb996bbb
|
|
@ -0,0 +1,26 @@
|
||||||
|
import BindingPrompt from "components/editorComponents/CodeEditor/BindingPrompt";
|
||||||
|
import type {
|
||||||
|
EditorTheme,
|
||||||
|
TEditorModes,
|
||||||
|
} from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export function CodeEditorSignPosting(props: {
|
||||||
|
promptMessage?: React.ReactNode | string;
|
||||||
|
isOpen?: boolean;
|
||||||
|
editorTheme?: EditorTheme;
|
||||||
|
showLightningMenu?: boolean;
|
||||||
|
isAIEnabled?: boolean;
|
||||||
|
mode: TEditorModes;
|
||||||
|
forComp?: string;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<BindingPrompt
|
||||||
|
editorTheme={props.editorTheme}
|
||||||
|
isAIEnabled={props.isAIEnabled}
|
||||||
|
isOpen={props.isOpen || false}
|
||||||
|
promptMessage={props.promptMessage}
|
||||||
|
showLightningMenu={props.showLightningMenu}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import type { TEditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
isAIEnabled: boolean;
|
||||||
|
mode: TEditorModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export function EditorFormSignPosting(props: Props) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import type { CodeEditorExpected } from "components/editorComponents/CodeEditor";
|
import type { CodeEditorExpected } from "components/editorComponents/CodeEditor";
|
||||||
import type { TEditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
|
import type {
|
||||||
|
FieldEntityInformation,
|
||||||
|
TEditorModes,
|
||||||
|
} from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export type TAIWrapperProps = {
|
export type TAIWrapperProps = {
|
||||||
|
|
@ -12,6 +15,7 @@ export type TAIWrapperProps = {
|
||||||
enableAIAssistance: boolean;
|
enableAIAssistance: boolean;
|
||||||
dataTreePath?: string;
|
dataTreePath?: string;
|
||||||
mode: TEditorModes;
|
mode: TEditorModes;
|
||||||
|
entity: FieldEntityInformation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AIWindow(props: TAIWrapperProps) {
|
export function AIWindow(props: TAIWrapperProps) {
|
||||||
|
|
|
||||||
|
|
@ -834,6 +834,7 @@ const ActionTypes = {
|
||||||
BIND_WIDGET_TO_DATASOURCE_ERROR: "BIND_WIDGET_TO_DATASOURCE_ERROR",
|
BIND_WIDGET_TO_DATASOURCE_ERROR: "BIND_WIDGET_TO_DATASOURCE_ERROR",
|
||||||
LOAD_FILE_PICKER_ACTION: "LOAD_FILE_PICKER_ACTION",
|
LOAD_FILE_PICKER_ACTION: "LOAD_FILE_PICKER_ACTION",
|
||||||
TOGGLE_AI_WINDOW: "TOGGLE_AI_WINDOW",
|
TOGGLE_AI_WINDOW: "TOGGLE_AI_WINDOW",
|
||||||
|
UPDATE_AI_TRIGGERED: "UPDATE_AI_TRIGGERED",
|
||||||
UPDATE_DATASOURCE_AUTH_STATE: "UPDATE_DATASOURCE_AUTH_STATE",
|
UPDATE_DATASOURCE_AUTH_STATE: "UPDATE_DATASOURCE_AUTH_STATE",
|
||||||
UPDATE_POSITIONS_ON_TAB_CHANGE: "UPDATE_POSITIONS_ON_TAB_CHANGE",
|
UPDATE_POSITIONS_ON_TAB_CHANGE: "UPDATE_POSITIONS_ON_TAB_CHANGE",
|
||||||
RESET_DATA_TREE: "RESET_DATA_TREE",
|
RESET_DATA_TREE: "RESET_DATA_TREE",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { Icon } from "design-system";
|
||||||
|
|
||||||
|
export type SignPostingBannerProps = {
|
||||||
|
iconName: string;
|
||||||
|
content: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Container = styled.div`
|
||||||
|
background-color: var(--ads-v2-color-blue-100);
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function SignPostingBanner(props: SignPostingBannerProps) {
|
||||||
|
return (
|
||||||
|
<Container className="py-2 px-3 rounded">
|
||||||
|
<div className="flex items-start">
|
||||||
|
<Icon
|
||||||
|
className="font-semibold mr-2 flex items-start"
|
||||||
|
color="var(--ads-v2-color-fg-information)"
|
||||||
|
name={props.iconName}
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{props.content}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SignPostingBanner;
|
||||||
|
|
@ -6,6 +6,7 @@ import type { EntityNavigationData } from "selectors/navigationSelectors";
|
||||||
import type { ExpectedValueExample } from "utils/validation/common";
|
import type { ExpectedValueExample } from "utils/validation/common";
|
||||||
|
|
||||||
import { editorSQLModes } from "./sql/config";
|
import { editorSQLModes } from "./sql/config";
|
||||||
|
import type { WidgetType } from "constants/WidgetConstants";
|
||||||
|
|
||||||
export const EditorModes = {
|
export const EditorModes = {
|
||||||
TEXT: "text/plain",
|
TEXT: "text/plain",
|
||||||
|
|
@ -61,6 +62,7 @@ export type FieldEntityInformation = {
|
||||||
example?: ExpectedValueExample;
|
example?: ExpectedValueExample;
|
||||||
mode?: TEditorModes;
|
mode?: TEditorModes;
|
||||||
token?: CodeMirror.Token;
|
token?: CodeMirror.Token;
|
||||||
|
widgetType?: WidgetType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HintHelper = (
|
export type HintHelper = (
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ import {
|
||||||
bindingHint,
|
bindingHint,
|
||||||
sqlHint,
|
sqlHint,
|
||||||
} from "components/editorComponents/CodeEditor/hintHelpers";
|
} from "components/editorComponents/CodeEditor/hintHelpers";
|
||||||
import BindingPrompt from "./BindingPrompt";
|
|
||||||
import { showBindingPrompt } from "./BindingPromptHelper";
|
import { showBindingPrompt } from "./BindingPromptHelper";
|
||||||
import { Button } from "design-system";
|
import { Button } from "design-system";
|
||||||
import "codemirror/addon/fold/brace-fold";
|
import "codemirror/addon/fold/brace-fold";
|
||||||
|
|
@ -103,6 +103,7 @@ import { getLintAnnotations, getLintTooltipDirection } from "./lintHelpers";
|
||||||
import { executeCommandAction } from "actions/apiPaneActions";
|
import { executeCommandAction } from "actions/apiPaneActions";
|
||||||
import { startingEntityUpdate } from "actions/editorActions";
|
import { startingEntityUpdate } from "actions/editorActions";
|
||||||
import type { SlashCommandPayload } from "entities/Action";
|
import type { SlashCommandPayload } from "entities/Action";
|
||||||
|
import { SlashCommand } from "entities/Action";
|
||||||
import type { Indices } from "constants/Layers";
|
import type { Indices } from "constants/Layers";
|
||||||
import { replayHighlightClass } from "globalStyles/portals";
|
import { replayHighlightClass } from "globalStyles/portals";
|
||||||
import {
|
import {
|
||||||
|
|
@ -158,6 +159,7 @@ import { PeekOverlayExpressionIdentifier, SourceType } from "@shared/ast";
|
||||||
import type { MultiplexingModeConfig } from "components/editorComponents/CodeEditor/modes";
|
import type { MultiplexingModeConfig } from "components/editorComponents/CodeEditor/modes";
|
||||||
import { MULTIPLEXING_MODE_CONFIGS } from "components/editorComponents/CodeEditor/modes";
|
import { MULTIPLEXING_MODE_CONFIGS } from "components/editorComponents/CodeEditor/modes";
|
||||||
import { getDeleteLineShortcut } from "./utils/deleteLine";
|
import { getDeleteLineShortcut } from "./utils/deleteLine";
|
||||||
|
import { CodeEditorSignPosting } from "@appsmith/components/editorComponents/CodeEditorSignPosting";
|
||||||
|
|
||||||
type ReduxStateProps = ReturnType<typeof mapStateToProps>;
|
type ReduxStateProps = ReturnType<typeof mapStateToProps>;
|
||||||
type ReduxDispatchProps = ReturnType<typeof mapDispatchToProps>;
|
type ReduxDispatchProps = ReturnType<typeof mapDispatchToProps>;
|
||||||
|
|
@ -489,6 +491,10 @@ class CodeEditor extends Component<Props, State> {
|
||||||
handleSlashCommandSelection = (...args: any) => {
|
handleSlashCommandSelection = (...args: any) => {
|
||||||
const [command] = args;
|
const [command] = args;
|
||||||
if (command === APPSMITH_AI) {
|
if (command === APPSMITH_AI) {
|
||||||
|
this.props.executeCommand({
|
||||||
|
actionType: SlashCommand.ASK_AI,
|
||||||
|
args: {},
|
||||||
|
});
|
||||||
this.setState({ showAIWindow: true });
|
this.setState({ showAIWindow: true });
|
||||||
}
|
}
|
||||||
this.handleAutocompleteVisibility(this.editor);
|
this.handleAutocompleteVisibility(this.editor);
|
||||||
|
|
@ -1244,6 +1250,8 @@ class CodeEditor extends Component<Props, State> {
|
||||||
entityInformation.entityId = entity.widgetId;
|
entityInformation.entityId = entity.widgetId;
|
||||||
if (isTriggerPath)
|
if (isTriggerPath)
|
||||||
entityInformation.expectedType = AutocompleteDataType.FUNCTION;
|
entityInformation.expectedType = AutocompleteDataType.FUNCTION;
|
||||||
|
|
||||||
|
entityInformation.widgetType = entity.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entityInformation.propertyPath = propertyPath;
|
entityInformation.propertyPath = propertyPath;
|
||||||
|
|
@ -1552,12 +1560,14 @@ class CodeEditor extends Component<Props, State> {
|
||||||
currentValue={this.props.input.value}
|
currentValue={this.props.input.value}
|
||||||
dataTreePath={dataTreePath}
|
dataTreePath={dataTreePath}
|
||||||
enableAIAssistance={this.AIEnabled}
|
enableAIAssistance={this.AIEnabled}
|
||||||
|
entity={entityInformation}
|
||||||
isOpen={this.state.showAIWindow}
|
isOpen={this.state.showAIWindow}
|
||||||
mode={this.props.mode}
|
mode={this.props.mode}
|
||||||
triggerContext={this.props.expected}
|
triggerContext={this.props.expected}
|
||||||
update={this.updateValueWithAIResponse}
|
update={this.updateValueWithAIResponse}
|
||||||
>
|
>
|
||||||
<EditorWrapper
|
<EditorWrapper
|
||||||
|
AIEnabled
|
||||||
border={border}
|
border={border}
|
||||||
borderLess={borderLess}
|
borderLess={borderLess}
|
||||||
className={`${className} ${replayHighlightClass} ${
|
className={`${className} ${replayHighlightClass} ${
|
||||||
|
|
@ -1575,6 +1585,7 @@ class CodeEditor extends Component<Props, State> {
|
||||||
isNotHover={this.state.isFocused || this.state.isOpened}
|
isNotHover={this.state.isFocused || this.state.isOpened}
|
||||||
isRawView={this.props.isRawView}
|
isRawView={this.props.isRawView}
|
||||||
isReadOnly={this.props.isReadOnly}
|
isReadOnly={this.props.isReadOnly}
|
||||||
|
mode={this.props.mode}
|
||||||
onMouseMove={this.handleLintTooltip}
|
onMouseMove={this.handleLintTooltip}
|
||||||
onMouseOver={this.handleMouseMove}
|
onMouseOver={this.handleMouseMove}
|
||||||
ref={this.editorWrapperRef}
|
ref={this.editorWrapperRef}
|
||||||
|
|
@ -1604,10 +1615,12 @@ class CodeEditor extends Component<Props, State> {
|
||||||
ref={this.codeEditorTarget}
|
ref={this.codeEditorTarget}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<BindingPrompt
|
<CodeEditorSignPosting
|
||||||
editorTheme={this.props.theme}
|
editorTheme={this.props.theme}
|
||||||
|
forComp="editor"
|
||||||
isAIEnabled={this.AIEnabled}
|
isAIEnabled={this.AIEnabled}
|
||||||
isOpen={this.isBindingPromptOpen()}
|
isOpen={this.isBindingPromptOpen()}
|
||||||
|
mode={this.props.mode}
|
||||||
promptMessage={this.props.promptMessage}
|
promptMessage={this.props.promptMessage}
|
||||||
showLightningMenu={this.props.showLightningMenu}
|
showLightningMenu={this.props.showLightningMenu}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import type { CodeEditorBorder } from "components/editorComponents/CodeEditor/EditorConfig";
|
import type { CodeEditorBorder } from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EditorSize,
|
EditorSize,
|
||||||
EditorTheme,
|
EditorTheme,
|
||||||
|
|
@ -56,6 +57,8 @@ export const EditorWrapper = styled.div<{
|
||||||
codeEditorVisibleOverflow?: boolean;
|
codeEditorVisibleOverflow?: boolean;
|
||||||
ctrlPressed: boolean;
|
ctrlPressed: boolean;
|
||||||
removeHoverAndFocusStyle?: boolean;
|
removeHoverAndFocusStyle?: boolean;
|
||||||
|
AIEnabled?: boolean;
|
||||||
|
mode: string;
|
||||||
}>`
|
}>`
|
||||||
// Bottom border was getting clipped
|
// Bottom border was getting clipped
|
||||||
.CodeMirror.cm-s-duotone-light.CodeMirror-wrap {
|
.CodeMirror.cm-s-duotone-light.CodeMirror-wrap {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "ce/components/editorComponents/CodeEditorSignPosting";
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "ce/components/editorComponents/EditorFormSignPosting";
|
||||||
|
|
@ -58,6 +58,7 @@ import {
|
||||||
} from "@appsmith/sagas/userSagas";
|
} from "@appsmith/sagas/userSagas";
|
||||||
import { getFirstTimeUserOnboardingComplete } from "selectors/onboardingSelectors";
|
import { getFirstTimeUserOnboardingComplete } from "selectors/onboardingSelectors";
|
||||||
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
|
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
|
||||||
|
import { getAIPromptTriggered } from "utils/storage";
|
||||||
|
|
||||||
export default class AppEditorEngine extends AppEngine {
|
export default class AppEditorEngine extends AppEngine {
|
||||||
constructor(mode: APP_MODE) {
|
constructor(mode: APP_MODE) {
|
||||||
|
|
@ -214,6 +215,16 @@ export default class AppEditorEngine extends AppEngine {
|
||||||
payload: [],
|
payload: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const noOfTimesAIPromptTriggered: number = yield getAIPromptTriggered();
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.UPDATE_AI_TRIGGERED,
|
||||||
|
payload: {
|
||||||
|
value: noOfTimesAIPromptTriggered,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
yield call(waitForWidgetConfigBuild);
|
yield call(waitForWidgetConfigBuild);
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,14 @@ import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "entities/AppsmithConsole";
|
||||||
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
|
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
|
||||||
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
|
import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView";
|
||||||
import { CloseDebugger } from "components/editorComponents/Debugger/DebuggerTabs";
|
import { CloseDebugger } from "components/editorComponents/Debugger/DebuggerTabs";
|
||||||
|
import { isAIEnabled } from "@appsmith/components/editorComponents/GPT/trigger";
|
||||||
|
import { editorSQLModes } from "components/editorComponents/CodeEditor/sql/config";
|
||||||
|
import { EditorFormSignPosting } from "@appsmith/components/editorComponents/EditorFormSignPosting";
|
||||||
import { DatasourceStructureContext } from "../Explorer/Datasources/DatasourceStructureContainer";
|
import { DatasourceStructureContext } from "../Explorer/Datasources/DatasourceStructureContainer";
|
||||||
import { selectFeatureFlagCheck } from "@appsmith/selectors/featureFlagsSelectors";
|
import {
|
||||||
|
selectFeatureFlagCheck,
|
||||||
|
selectFeatureFlags,
|
||||||
|
} from "@appsmith/selectors/featureFlagsSelectors";
|
||||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||||
|
|
||||||
const QueryFormContainer = styled.form`
|
const QueryFormContainer = styled.form`
|
||||||
|
|
@ -875,6 +881,14 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
dispatch(setDebuggerSelectedTab(tabKey));
|
dispatch(setDebuggerSelectedTab(tabKey));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const featureFlags = useSelector(selectFeatureFlags);
|
||||||
|
const editorMode =
|
||||||
|
currentActionPluginName === PluginName.POSTGRES
|
||||||
|
? editorSQLModes.POSTGRESQL_WITH_BINDING
|
||||||
|
: editorSQLModes.MYSQL_WITH_BINDING;
|
||||||
|
|
||||||
|
const isAIEnabledForPosting = isAIEnabled(featureFlags, editorMode);
|
||||||
|
|
||||||
// close the debugger
|
// close the debugger
|
||||||
//TODO: move this to a common place
|
//TODO: move this to a common place
|
||||||
const onClose = () => dispatch(showDebugger(false));
|
const onClose = () => dispatch(showDebugger(false));
|
||||||
|
|
@ -980,6 +994,11 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
className="tab-panel"
|
className="tab-panel"
|
||||||
value={EDITOR_TABS.QUERY}
|
value={EDITOR_TABS.QUERY}
|
||||||
>
|
>
|
||||||
|
<EditorFormSignPosting
|
||||||
|
isAIEnabled={isAIEnabledForPosting}
|
||||||
|
mode={editorMode}
|
||||||
|
/>
|
||||||
|
|
||||||
<SettingsWrapper>
|
<SettingsWrapper>
|
||||||
{editorConfig && editorConfig.length > 0 ? (
|
{editorConfig && editorConfig.length > 0 ? (
|
||||||
renderConfig(editorConfig)
|
renderConfig(editorConfig)
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ import { DEFAULT_GRAPHQL_ACTION_CONFIG } from "constants/ApiEditorConstants/Grap
|
||||||
import { DEFAULT_API_ACTION_CONFIG } from "constants/ApiEditorConstants/ApiEditorConstants";
|
import { DEFAULT_API_ACTION_CONFIG } from "constants/ApiEditorConstants/ApiEditorConstants";
|
||||||
import { createNewApiName, createNewQueryName } from "utils/AppsmithUtils";
|
import { createNewApiName, createNewQueryName } from "utils/AppsmithUtils";
|
||||||
import { fetchDatasourceStructure } from "actions/datasourceActions";
|
import { fetchDatasourceStructure } from "actions/datasourceActions";
|
||||||
|
import { setAIPromptTriggered } from "utils/storage";
|
||||||
|
|
||||||
export function* createDefaultActionPayload(
|
export function* createDefaultActionPayload(
|
||||||
pageId: string,
|
pageId: string,
|
||||||
|
|
@ -942,6 +943,21 @@ function* executeCommandSaga(actionPayload: ReduxAction<SlashCommandPayload>) {
|
||||||
break;
|
break;
|
||||||
case SlashCommand.ASK_AI: {
|
case SlashCommand.ASK_AI: {
|
||||||
const context = get(actionPayload, "payload.args", {});
|
const context = get(actionPayload, "payload.args", {});
|
||||||
|
|
||||||
|
const noOfTimesAIPromptTriggered: number = yield select(
|
||||||
|
(state) => state.ai.noOfTimesAITriggered,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (noOfTimesAIPromptTriggered < 5) {
|
||||||
|
const currentValue: number = yield setAIPromptTriggered();
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.UPDATE_AI_TRIGGERED,
|
||||||
|
payload: {
|
||||||
|
value: currentValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.TOGGLE_AI_WINDOW,
|
type: ReduxActionTypes.TOGGLE_AI_WINDOW,
|
||||||
payload: {
|
payload: {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ export const STORAGE_KEYS: {
|
||||||
FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY:
|
FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY:
|
||||||
"FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY",
|
"FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY",
|
||||||
SIGNPOSTING_APP_STATE: "SIGNPOSTING_APP_STATE",
|
SIGNPOSTING_APP_STATE: "SIGNPOSTING_APP_STATE",
|
||||||
|
AI_TRIGGERED: "AI_TRIGGERED",
|
||||||
FEATURE_WALKTHROUGH: "FEATURE_WALKTHROUGH",
|
FEATURE_WALKTHROUGH: "FEATURE_WALKTHROUGH",
|
||||||
USER_SIGN_UP: "USER_SIGN_UP",
|
USER_SIGN_UP: "USER_SIGN_UP",
|
||||||
};
|
};
|
||||||
|
|
@ -421,6 +422,39 @@ export const setFirstTimeUserOnboardingTelemetryCalloutVisibility = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setAIPromptTriggered = async () => {
|
||||||
|
try {
|
||||||
|
let noOfTimesAITriggered: number = await getAIPromptTriggered();
|
||||||
|
|
||||||
|
if (noOfTimesAITriggered >= 5) {
|
||||||
|
return noOfTimesAITriggered;
|
||||||
|
}
|
||||||
|
|
||||||
|
noOfTimesAITriggered += 1;
|
||||||
|
await store.setItem(STORAGE_KEYS.AI_TRIGGERED, noOfTimesAITriggered);
|
||||||
|
|
||||||
|
return noOfTimesAITriggered;
|
||||||
|
} catch (error) {
|
||||||
|
log.error("An error occurred while setting AI_TRIGGERED");
|
||||||
|
log.error(error);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAIPromptTriggered = async () => {
|
||||||
|
try {
|
||||||
|
const flag: number | null = await store.getItem(STORAGE_KEYS.AI_TRIGGERED);
|
||||||
|
|
||||||
|
if (flag === null) return 0;
|
||||||
|
|
||||||
|
return flag;
|
||||||
|
} catch (error) {
|
||||||
|
log.error("An error occurred while fetching AI_TRIGGERED");
|
||||||
|
log.error(error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
export const setFeatureFlagShownStatus = async (key: string, value: any) => {
|
export const setFeatureFlagShownStatus = async (key: string, value: any) => {
|
||||||
try {
|
try {
|
||||||
let flagsJSON: Record<string, any> | null = await store.getItem(
|
let flagsJSON: Record<string, any> | null = await store.getItem(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user