From 0ee9f42656410560dafc5a8c32724b68040aa78a Mon Sep 17 00:00:00 2001 From: mdshamoon Date: Thu, 3 Mar 2022 12:31:45 +0530 Subject: [PATCH 1/6] added lazy loading for interactive message --- src/components/dialog/Dialog.tsx | 3 +- src/components/flow/actions/action/Action.tsx | 14 +++- .../sendbroadcast/SendBroadcastForm.tsx | 1 + .../sendinteractivemsg/SendInteractiveMsg.tsx | 82 +++++++++++++++---- .../SendInteractiveMsgForm.tsx | 17 ++++ .../actions/sendinteractivemsg/helpers.tsx | 23 +++++- .../localization/RouterLocalizationForm.tsx | 1 + src/external/index.ts | 17 ++++ src/flowTypes.ts | 6 +- 9 files changed, 139 insertions(+), 25 deletions(-) diff --git a/src/components/dialog/Dialog.tsx b/src/components/dialog/Dialog.tsx index 3290385f7..5300aa6da 100644 --- a/src/components/dialog/Dialog.tsx +++ b/src/components/dialog/Dialog.tsx @@ -41,6 +41,7 @@ export interface DialogProps { noPadding?: boolean; tabs?: Tab[]; className?: string; + defaultTab?: number; } export interface DialogState { @@ -57,7 +58,7 @@ export default class Dialog extends React.Component { constructor(props: DialogProps) { super(props); this.state = { - activeTab: -1 + activeTab: this.props.defaultTab !== null ? this.props.defaultTab : -1 }; bindCallbacks(this, { diff --git a/src/components/flow/actions/action/Action.tsx b/src/components/flow/actions/action/Action.tsx index fce615ccd..e8115efcd 100644 --- a/src/components/flow/actions/action/Action.tsx +++ b/src/components/flow/actions/action/Action.tsx @@ -120,7 +120,8 @@ export class ActionWrapper extends React.Component { if ( this.props.action.type === Types.send_msg || this.props.action.type === Types.send_broadcast || - this.props.action.type === Types.say_msg + this.props.action.type === Types.say_msg || + this.props.action.type === Types.send_interactive_msg ) { localizedKeys.push('text'); } @@ -136,6 +137,8 @@ export class ActionWrapper extends React.Component { this.props.language ); + console.log(localization); + if (localization.isLocalized()) { for (const key of localizedKeys) { if (!(key in localization.localizedKeys)) { @@ -178,9 +181,12 @@ export class ActionWrapper extends React.Component { titleBarClass = shared.missing; } - const events = this.context.config.mutable - ? createClickHandler(this.handleActionClicked, () => this.props.selected) - : {}; + const isInteractive = + this.props.translating && this.props.action.type === Types.send_interactive_msg; + const events = + this.context.config.mutable && !isInteractive + ? createClickHandler(this.handleActionClicked, () => this.props.selected) + : {}; const body = ( <> diff --git a/src/components/flow/actions/sendbroadcast/SendBroadcastForm.tsx b/src/components/flow/actions/sendbroadcast/SendBroadcastForm.tsx index 022cd4e5f..ebc01c867 100644 --- a/src/components/flow/actions/sendbroadcast/SendBroadcastForm.tsx +++ b/src/components/flow/actions/sendbroadcast/SendBroadcastForm.tsx @@ -458,6 +458,7 @@ export default class SendBroadcastForm extends React.Component< headerClass={typeConfig.type} buttons={this.getButtons()} tabs={[templates, attachments]} + defaultTab={0} > = ( action: SendInteractiveMsg ): JSX.Element => { - const message = JSON.parse(action.text); - const body = getMsgBody(message); + const [body, setBody] = React.useState(null); + const [header, setHeader] = React.useState(null); + + React.useEffect(() => { + setHeader(null); + setBody(null); + const { endpoint, type } = action.assetStore.interactives; + + getAsset(endpoint, type, action.id.toString()).then(res => { + if (res.error) { + setBody(PLACEHOLDER); + } else { + let languageId = languageToId[action.language.id]; + + if (action.language.id === 'base') { + languageId = 1; + } + + let message = res.translations[languageId]; + message = message ? message : res.interactive_content; + + setBody(getMsgBody(message)); + setHeader(getHeader(message)); + } + }); + }, [action.language]); + const endpoints: any = {}; let labels = null; @@ -40,19 +87,20 @@ const SendInteractiveMsgComp: React.SFC = ( endpoints ); } - if (action.name) { - return ( + return ( +
-
- {action.name} -
-
{body}
- {labels} + {header}
- ); - } - - return
{PLACEHOLDER}
; +
{body ? body : }
+ {labels} +
+ ); }; -export default SendInteractiveMsgComp; +const mapStateToProps = ({ flowContext: { assetStore }, editorState: { language } }: AppState) => ({ + assetStore, + language +}); + +export default connect(mapStateToProps)(SendInteractiveMsgComp); diff --git a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx index 3239ffe8d..4f010373f 100644 --- a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx +++ b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx @@ -21,6 +21,7 @@ import i18n from 'config/i18n'; import AssetSelector from 'components/form/assetselector/AssetSelector'; import { Asset } from 'store/flowContext'; import { AddLabelsFormState } from '../addlabels/AddLabelsForm'; +import { getAsset } from 'external'; export interface SendInteractiveMsgFormState extends FormState { interactives: FormEntry; @@ -152,6 +153,22 @@ export default class SendMsgForm extends React.Component< ); } + async componentDidMount(): Promise { + const { endpoint, type } = this.props.assetStore.interactives; + + let content = await getAsset(endpoint, type, this.state.interactives.value.id); + + if (content.interactive_content) { + this.setState({ + interactives: { + value: { + ...this.state.interactives.value, + interactive_content: content.interactive_content + } + } + }); + } + } public render(): JSX.Element { const typeConfig = this.props.typeConfig; diff --git a/src/components/flow/actions/sendinteractivemsg/helpers.tsx b/src/components/flow/actions/sendinteractivemsg/helpers.tsx index de0ade969..85c6fb9ef 100644 --- a/src/components/flow/actions/sendinteractivemsg/helpers.tsx +++ b/src/components/flow/actions/sendinteractivemsg/helpers.tsx @@ -26,8 +26,9 @@ export const initializeForm = ( return label; }) : []; + return { - interactives: { value: { id, interactive_content: text, name } }, + interactives: { value: { id, interactive_content: {}, name } }, labels: { value: labels }, @@ -65,6 +66,22 @@ export const stateToAction = ( return result; }; +export const getHeader = (message: any) => { + let header; + if (message) { + if (message.type === 'list') { + header = message.title; + } else if (message.type === 'quick_reply') { + if (message.content.type === 'text') { + header = message.content.header; + } else if (['image', 'video', 'file'].includes(message.content.type)) { + header = ''; + } + } + } + return header; +}; + export const getMsgBody = (message: any) => { let body; if (message) { @@ -94,7 +111,9 @@ export const getMsgBody = (message: any) => {
{body}
{message.options.map((option: any) => ( -
{option.title}
+
+ {option.title} +
))}
); diff --git a/src/components/flow/routers/localization/RouterLocalizationForm.tsx b/src/components/flow/routers/localization/RouterLocalizationForm.tsx index 0320fdb3d..75ea9cbb3 100644 --- a/src/components/flow/routers/localization/RouterLocalizationForm.tsx +++ b/src/components/flow/routers/localization/RouterLocalizationForm.tsx @@ -227,6 +227,7 @@ export default class RouterLocalizationForm extends React.Component< headerClass={typeConfig.type} buttons={this.getButtons()} tabs={tabs} + defaultTab={0} >

When category names are referenced later in the flow, the appropriate language for the diff --git a/src/external/index.ts b/src/external/index.ts index aee5279eb..f3ba3e414 100644 --- a/src/external/index.ts +++ b/src/external/index.ts @@ -167,6 +167,22 @@ export const getAssets = async (url: string, type: AssetType, id: string): Promi return assets; }; +export const getAsset = async (url: string, type: AssetType, id: string): Promise => { + if (!url) { + return new Promise((resolve, reject) => resolve([])); + } + + return new Promise((resolve, reject) => { + axios + .get(url + '/' + id) + .then((response: AxiosResponse) => { + console.log(response); + resolve(response.data); + }) + .catch(error => reject(error)); + }); +}; + export const getFlowType = (flow: any) => { switch (flow.type) { case 'message': @@ -352,6 +368,7 @@ export const createAssetStore = (endpoints: Endpoints): Promise => { }, interactives: { items: {}, + id: 'id', type: AssetType.Interactives, endpoint: getURL(endpoints.interactives) } diff --git a/src/flowTypes.ts b/src/flowTypes.ts index b807789bf..6e6036a9a 100644 --- a/src/flowTypes.ts +++ b/src/flowTypes.ts @@ -1,5 +1,6 @@ import { Methods } from 'components/flow/routers/webhook/helpers'; import { FlowTypes, Operators, Types, ContactStatus } from 'config/interfaces'; +import { AssetStore } from 'store/flowContext'; // we don't concern ourselves with patch versions export const SPEC_VERSION = '13.1'; @@ -54,7 +55,7 @@ export interface FlowEditorConfig { endpoints: Endpoints; flow: string; flowType: FlowTypes; - showNodeUUIDs?: boolean; + showNodeLabel?: boolean; showTemplates?: boolean; showDownload?: boolean; mutable?: boolean; @@ -380,6 +381,8 @@ export interface SendInteractiveMsg extends Action { id: number; name: string; labels?: Label[]; + assetStore?: AssetStore; + language?: any; } export interface Delay extends Action { @@ -531,6 +534,7 @@ export type AnyAction = | SetContactName | SetRunResult | SendMsg + | SendInteractiveMsg | SetPreferredChannel | SendEmail | CallClassifier From 4e57189ca9bf993ccabee4c024b5ec3f3f8eac21 Mon Sep 17 00:00:00 2001 From: mdshamoon Date: Fri, 25 Mar 2022 17:11:23 +0530 Subject: [PATCH 2/6] code cleanup --- src/components/flow/actions/action/Action.tsx | 2 -- .../flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/flow/actions/action/Action.tsx b/src/components/flow/actions/action/Action.tsx index e8115efcd..1566b44e2 100644 --- a/src/components/flow/actions/action/Action.tsx +++ b/src/components/flow/actions/action/Action.tsx @@ -137,8 +137,6 @@ export class ActionWrapper extends React.Component { this.props.language ); - console.log(localization); - if (localization.isLocalized()) { for (const key of localizedKeys) { if (!(key in localization.localizedKeys)) { diff --git a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx index 13e8aec9a..84c4da0ce 100644 --- a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx +++ b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx @@ -31,7 +31,7 @@ const languageToId: any = { export const PLACEHOLDER = i18n.t( 'actions.send_interactive_msg.placeholder', - 'The interactive message in not available' + 'The interactive message is not available' ); const SendInteractiveMsgComp: React.SFC = ( From bd0a07d8a8233a1e123462cb2f9e237a1df3c6d9 Mon Sep 17 00:00:00 2001 From: mdshamoon Date: Fri, 25 Mar 2022 18:13:02 +0530 Subject: [PATCH 3/6] code cleanup --- .../sendinteractivemsg/SendInteractiveMsg.tsx | 22 ++----------------- .../actions/sendinteractivemsg/helpers.tsx | 4 ++-- .../flow/actions/sendmsg/SendMsgForm.tsx | 1 - .../flow/routers/result/ResultRouterForm.tsx | 2 +- .../routers/webhook/WebhookRouterForm.tsx | 1 - 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx index 84c4da0ce..504a92db7 100644 --- a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx +++ b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx @@ -11,24 +11,6 @@ import { connect } from 'react-redux'; import { getAsset } from 'external'; import Loading from 'components/loading/Loading'; -const languageToId: any = { - en: 1, - hi: 2, - ta: 3, - kn: 4, - ml: 5, - te: 6, - or: 7, - as: 8, - gu: 9, - bn: 10, - pa: 11, - mr: 12, - ur: 13, - es: 14, - isl: 15 -}; - export const PLACEHOLDER = i18n.t( 'actions.send_interactive_msg.placeholder', 'The interactive message is not available' @@ -49,10 +31,10 @@ const SendInteractiveMsgComp: React.SFC = ( if (res.error) { setBody(PLACEHOLDER); } else { - let languageId = languageToId[action.language.id]; + let languageId = action.language.id; if (action.language.id === 'base') { - languageId = 1; + languageId = 'en'; } let message = res.translations[languageId]; diff --git a/src/components/flow/actions/sendinteractivemsg/helpers.tsx b/src/components/flow/actions/sendinteractivemsg/helpers.tsx index 85c6fb9ef..a50bfcab0 100644 --- a/src/components/flow/actions/sendinteractivemsg/helpers.tsx +++ b/src/components/flow/actions/sendinteractivemsg/helpers.tsx @@ -16,8 +16,8 @@ export const initializeForm = ( ): SendInteractiveMsgFormState => { if (settings.originalAction && settings.originalAction.type === Types.send_interactive_msg) { const action = settings.originalAction as SendInteractiveMsg; - let { id, text, name } = action; - text = JSON.parse(text); + let { id, name } = action; + const labels = action.labels ? action.labels.map((label: Label) => { if (label.name_match) { diff --git a/src/components/flow/actions/sendmsg/SendMsgForm.tsx b/src/components/flow/actions/sendmsg/SendMsgForm.tsx index 15788764b..c0a6f0a3b 100644 --- a/src/components/flow/actions/sendmsg/SendMsgForm.tsx +++ b/src/components/flow/actions/sendmsg/SendMsgForm.tsx @@ -43,7 +43,6 @@ import { hasFeature } from 'config/typeConfigs'; import { FeatureFilter } from 'config/interfaces'; import i18n from 'config/i18n'; -import { Trans } from 'react-i18next'; import { Attachment, renderAttachments, validateURL } from './attachments'; import { AddLabelsFormState } from '../addlabels/AddLabelsForm'; diff --git a/src/components/flow/routers/result/ResultRouterForm.tsx b/src/components/flow/routers/result/ResultRouterForm.tsx index 82f73cf2a..d5c40dd42 100644 --- a/src/components/flow/routers/result/ResultRouterForm.tsx +++ b/src/components/flow/routers/result/ResultRouterForm.tsx @@ -1,6 +1,6 @@ import { react as bindCallbacks } from 'auto-bind'; import Dialog, { ButtonSet } from 'components/dialog/Dialog'; -import { hasErrors, renderIssues } from 'components/flow/actions/helpers'; +import { renderIssues } from 'components/flow/actions/helpers'; import { RouterFormProps } from 'components/flow/props'; import CaseList, { CaseProps } from 'components/flow/routers/caselist/CaseList'; import { createResultNameInput } from 'components/flow/routers/widgets'; diff --git a/src/components/flow/routers/webhook/WebhookRouterForm.tsx b/src/components/flow/routers/webhook/WebhookRouterForm.tsx index 7742ee45b..208c05055 100644 --- a/src/components/flow/routers/webhook/WebhookRouterForm.tsx +++ b/src/components/flow/routers/webhook/WebhookRouterForm.tsx @@ -20,7 +20,6 @@ import * as React from 'react'; import { FormEntry, FormState, mergeForm, StringEntry, ValidationFailure } from 'store/nodeEditor'; import { LowerCaseAlphaNumeric, - Required, shouldRequireIf, StartIsNonNumeric, validate, From b679069bfc07e1417922384ded73403d5a25d9b2 Mon Sep 17 00:00:00 2001 From: mdshamoon Date: Tue, 29 Mar 2022 11:03:15 +0530 Subject: [PATCH 4/6] fixed all issues --- .../sendinteractivemsg/SendInteractiveMsg.tsx | 88 ++++++++++++------- .../SendInteractiveMsgForm.tsx | 25 +++++- src/external/index.ts | 1 - src/flowTypes.ts | 1 + 4 files changed, 80 insertions(+), 35 deletions(-) diff --git a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx index 504a92db7..3031d588e 100644 --- a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx +++ b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsg.tsx @@ -1,5 +1,5 @@ import { SendInteractiveMsg } from 'flowTypes'; -import * as React from 'react'; +import React, { useEffect, useState } from 'react'; import i18n from 'config/i18n'; import { getHeader, getMsgBody } from './helpers'; @@ -10,48 +10,64 @@ import AppState from 'store/state'; import { connect } from 'react-redux'; import { getAsset } from 'external'; import Loading from 'components/loading/Loading'; +import { addAsset, DispatchWithState } from 'store/thunks'; +import { bindActionCreators } from 'redux'; export const PLACEHOLDER = i18n.t( 'actions.send_interactive_msg.placeholder', 'The interactive message is not available' ); -const SendInteractiveMsgComp: React.SFC = ( - action: SendInteractiveMsg -): JSX.Element => { - const [body, setBody] = React.useState(null); - const [header, setHeader] = React.useState(null); +const SendInteractiveMsgComp: React.SFC = ({ + assetStore, + labels, + language, + id, + addAsset +}: SendInteractiveMsg): JSX.Element => { + const [body, setBody] = useState(null); + const [header, setHeader] = useState(null); + const { endpoint, type, items } = assetStore.interactives; + + let languageId = language.id; + + if (language.id === 'base') { + languageId = 'en'; + } + + const interactive: any = items[id]; - React.useEffect(() => { + useEffect(() => { setHeader(null); setBody(null); - const { endpoint, type } = action.assetStore.interactives; - getAsset(endpoint, type, action.id.toString()).then(res => { - if (res.error) { - setBody(PLACEHOLDER); - } else { - let languageId = action.language.id; + const setNode = (content: any) => { + let message = content.translations[languageId]; + message = message ? message : content.interactive_content; + setBody(getMsgBody(message)); + setHeader(getHeader(message)); + }; - if (action.language.id === 'base') { - languageId = 'en'; + if (interactive) { + setNode(interactive); + } else { + getAsset(endpoint, type, id.toString()).then(response => { + if (response.error) { + setBody(PLACEHOLDER); + } else { + addAsset('interactives', response); + setNode(response); } - - let message = res.translations[languageId]; - message = message ? message : res.interactive_content; - - setBody(getMsgBody(message)); - setHeader(getHeader(message)); - } - }); - }, [action.language]); + }); + } + }, [language, id]); const endpoints: any = {}; - let labels = null; + let labelsList = null; - if (action.labels) { - labels = renderAssetList( - action.labels.map((label: any) => { + if (labels) { + labelsList = renderAssetList( + labels.map((label: any) => { if (label.name_match) { return { id: label.name_match, @@ -75,7 +91,7 @@ const SendInteractiveMsgComp: React.SFC = ( {header}

{body ? body : }
- {labels} + {labelsList} ); }; @@ -85,4 +101,16 @@ const mapStateToProps = ({ flowContext: { assetStore }, editorState: { language language }); -export default connect(mapStateToProps)(SendInteractiveMsgComp); +/* istanbul ignore next */ +const mapDispatchToProps = (dispatch: DispatchWithState) => + bindActionCreators( + { + addAsset + }, + dispatch + ); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(SendInteractiveMsgComp); diff --git a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx index 4f010373f..6a6e23700 100644 --- a/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx +++ b/src/components/flow/actions/sendinteractivemsg/SendInteractiveMsgForm.tsx @@ -154,19 +154,36 @@ export default class SendMsgForm extends React.Component< ); } async componentDidMount(): Promise { - const { endpoint, type } = this.props.assetStore.interactives; + const id = this.state.interactives.value.id; - let content = await getAsset(endpoint, type, this.state.interactives.value.id); + if (!id) { + return; + } + const { endpoint, type, items } = this.props.assetStore.interactives; + const interactive: any = items[id]; - if (content.interactive_content) { + if (interactive) { this.setState({ interactives: { value: { ...this.state.interactives.value, - interactive_content: content.interactive_content + interactive_content: interactive.interactive_content } } }); + } else { + let content = await getAsset(endpoint, type, id); + + if (content.interactive_content) { + this.setState({ + interactives: { + value: { + ...this.state.interactives.value, + interactive_content: content.interactive_content + } + } + }); + } } } diff --git a/src/external/index.ts b/src/external/index.ts index f3ba3e414..effe264ac 100644 --- a/src/external/index.ts +++ b/src/external/index.ts @@ -176,7 +176,6 @@ export const getAsset = async (url: string, type: AssetType, id: string): Promis axios .get(url + '/' + id) .then((response: AxiosResponse) => { - console.log(response); resolve(response.data); }) .catch(error => reject(error)); diff --git a/src/flowTypes.ts b/src/flowTypes.ts index 6e6036a9a..0ec9baa9f 100644 --- a/src/flowTypes.ts +++ b/src/flowTypes.ts @@ -383,6 +383,7 @@ export interface SendInteractiveMsg extends Action { labels?: Label[]; assetStore?: AssetStore; language?: any; + addAsset?: any; } export interface Delay extends Action { From 623b5627ef1679b2ad3349721e6a55fa888b0a3b Mon Sep 17 00:00:00 2001 From: mdshamoon Date: Tue, 29 Mar 2022 11:05:55 +0530 Subject: [PATCH 5/6] fixed split by collection membership --- .../groups/__snapshots__/GroupsRouterForm.test.ts.snap | 6 +++--- src/config/__snapshots__/typeConfigs.test.ts.snap | 8 ++++---- src/config/i18n/defaults.json | 4 ++-- src/config/typeConfigs.ts | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/flow/routers/groups/__snapshots__/GroupsRouterForm.test.ts.snap b/src/components/flow/routers/groups/__snapshots__/GroupsRouterForm.test.ts.snap index 31ed66afc..6bc557b73 100644 --- a/src/components/flow/routers/groups/__snapshots__/GroupsRouterForm.test.ts.snap +++ b/src/components/flow/routers/groups/__snapshots__/GroupsRouterForm.test.ts.snap @@ -57,19 +57,19 @@ exports[`GroupsRouterForm render should render 1`] = ` } } headerClass="split_by_groups" - title="Split by Collection Membership" + title="Split by collection Membership" > Date: Tue, 29 Mar 2022 11:17:03 +0530 Subject: [PATCH 6/6] updated package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 76d9972e5..accad43ab 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@glific/flow-editor", "license": "AGPL-3.0", "repository": "git://github.com/glific/floweditor.git", - "version": "1.17.0-2", + "version": "1.17.0-3", "description": "'Standalone flow editing tool designed for use within the Glific suite of messaging tools'", "browser": "umd/flow-editor.min.js", "unpkg": "umd/flow-editor.min.js",