Skip to content

Commit

Permalink
plugin-flow-builder: WebviewContentsContext and useWebviewContents (#…
Browse files Browse the repository at this point in the history
…2810)

## Description

**@botonic/core**
Update interface Session to add `organization_id`

**@botonic/react**
Create `WebviewRequestContext` to properly type what is received in the
request context of a webview, the functions: `getString` and
`closeWebview` and the objects `session` and `params`.
Also add the `closeWebview` function in the browser API, now we will be
able to do `Botonic.closeWebview()`

**@botonic/plugin-flow-builder**
Change `BotonicPluginFlowBuilderOptions` remove `flowUrl` and add
`apiUrl` and `jsonVersion` as optionals params. If this params are
undefined the plugin build flowUrl using PROD URL and latest version.
Create a hook `useWebviewContents` and `WebviewContentsContext`

## Approach taken / Explain the design

**@botonic/plugin-flow-builder**
The call to fetch the contents should only be made once at the start of
the webview.
This hook gets the contents of the webview and returns `isLoading`,
`error`and the `webviewContentsContext`
With the `webviewContentsContext` the `WebviewContentsContext` has to be
created.
This has the functions `getTextContent` and `getImageSrc` to use in the
renders of each component.

## Tests

Add organization and organization_id in @botonic/react helpers to have
green tests
  • Loading branch information
Iru89 committed Apr 17, 2024
1 parent fad4514 commit c5f3601
Show file tree
Hide file tree
Showing 22 changed files with 283 additions and 61 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Expand Up @@ -20,6 +20,31 @@ All notable changes to Botonic will be documented in this file.

</details>

## [0.26.0-beta.0] - 2024-04-16

### Added

- [@botonic/plugin-flow-builder](https://www.npmjs.com/package/@botonic/plugin-flow-builder)

- [Add `WebviewContentsContext` and `useWebviewContents`](https://github.com/hubtype/botonic/pull/2810)

- [@botonic/react](https://www.npmjs.com/package/@botonic/react)
- [Add `closeWebview` in browser API](https://github.com/hubtype/botonic/pull/2810)

### Changed

- [@botonic/plugin-flow-builder](https://www.npmjs.com/package/@botonic/plugin-flow-builder)

- [Remove `flowUrl` and add `apiUrl`, `jsonVersion` in BotonicPluginFlowBuilderOptions](https://github.com/hubtype/botonic/pull/2810)

- [@botonic/react](https://www.npmjs.com/package/@botonic/react)
- [Remove `RequestContext` and create `WebviewRequestContext` for webview](https://github.com/hubtype/botonic/pull/2810)

### Fixed

- [@botonic/core](https://www.npmjs.com/package/@botonic/core)
- [Add `organization_id` in Session](https://github.com/hubtype/botonic/pull/2810)

## [0.25.0] - 2024-03-27

**NOTE**: [Required version has been updated to be run with Node 20 and npm 10](https://github.com/hubtype/botonic/pull/2780).
Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@botonic/core",
"version": "0.25.0",
"version": "0.26.0-beta.0",
"license": "MIT",
"description": "Build Chatbots using React",
"main": "./lib/cjs/index.js",
Expand Down
3 changes: 2 additions & 1 deletion packages/botonic-core/src/models/legacy-types.ts
Expand Up @@ -170,7 +170,8 @@ export interface Session {
__retries: number
is_first_interaction: boolean
last_session?: any
organization?: string
organization: string
organization_id: string
user: SessionUser
// after handoff
_hubtype_case_status?: CaseStatusType
Expand Down
2 changes: 2 additions & 0 deletions packages/botonic-core/tests/helpers/routing.ts
Expand Up @@ -9,6 +9,8 @@ export function testSession(): Session {
user: { id: 'userid', provider: PROVIDER.DEV },
bot: { id: 'bot_id' },
is_first_interaction: true,
organization: 'test_org',
organization_id: '1234567890',
__retries: 0,
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/botonic-plugin-flow-builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@botonic/plugin-flow-builder",
"version": "0.25.0",
"version": "0.26.0-beta.0",
"main": "./lib/cjs/index.js",
"module": "./lib/esm/index.js",
"description": "Use Flow Builder to show your contents",
Expand All @@ -14,7 +14,7 @@
"lint_core": "../../node_modules/.bin/eslint_d --cache --quiet 'src/**/*.ts*'"
},
"dependencies": {
"@botonic/react": "^0.25.0",
"@botonic/react": "^0.26.0-beta.0",
"axios": "^1.6.8",
"uuid": "^9.0.1"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/botonic-plugin-flow-builder/src/constants.ts
@@ -1,3 +1,5 @@
export const FLOW_BUILDER_API_URL_PROD =
'https://api.ent0.flowbuilder.prod.hubtype.com'
export const SEPARATOR = '|'
export const SOURCE_INFO_SEPARATOR = `${SEPARATOR}source_`
export const VARIABLE_PATTERN = /{([^}]+)}/g
Expand Down
18 changes: 15 additions & 3 deletions packages/botonic-plugin-flow-builder/src/index.ts
Expand Up @@ -2,7 +2,11 @@ import { Plugin, PluginPreRequest, Session } from '@botonic/core'
import { ActionRequest } from '@botonic/react'

import { FlowBuilderApi } from './api'
import { SEPARATOR, SOURCE_INFO_SEPARATOR } from './constants'
import {
FLOW_BUILDER_API_URL_PROD,
SEPARATOR,
SOURCE_INFO_SEPARATOR,
} from './constants'
import {
FlowCarousel,
FlowContent,
Expand All @@ -24,6 +28,7 @@ import {
import { DEFAULT_FUNCTIONS } from './functions'
import {
BotonicPluginFlowBuilderOptions,
FlowBuilderJSONVersion,
KnowledgeBaseResponse,
PayloadParamsBase,
} from './types'
Expand All @@ -48,7 +53,9 @@ export default class BotonicPluginFlowBuilder implements Plugin {
) => Promise<KnowledgeBaseResponse>

constructor(readonly options: BotonicPluginFlowBuilderOptions) {
this.flowUrl = options.flowUrl
const apiUrl = options.apiUrl || FLOW_BUILDER_API_URL_PROD
const jsonVersion = options.jsonVersion || FlowBuilderJSONVersion.LATEST
this.flowUrl = `${apiUrl}/flow/${jsonVersion}`
this.flow = options.flow
this.getLocale = options.getLocale
this.getAccessToken = resolveGetAccessToken(options)
Expand Down Expand Up @@ -229,4 +236,9 @@ export default class BotonicPluginFlowBuilder implements Plugin {

export * from './action'
export * from './content-fields'
export { BotonicPluginFlowBuilderOptions, PayloadParamsBase } from './types'
export {
BotonicPluginFlowBuilderOptions,
FlowBuilderJSONVersion,
PayloadParamsBase,
} from './types'
export * from './webview'
8 changes: 7 additions & 1 deletion packages/botonic-plugin-flow-builder/src/types.ts
Expand Up @@ -4,7 +4,8 @@ import { ActionRequest } from '@botonic/react'
import { HtFlowBuilderData } from './content-fields/hubtype-fields'

export interface BotonicPluginFlowBuilderOptions {
flowUrl: string
apiUrl?: string
jsonVersion?: FlowBuilderJSONVersion
flow?: HtFlowBuilderData
customFunctions?: Record<any, any>
getLocale: (session: Session) => string
Expand All @@ -31,6 +32,11 @@ export enum ProcessEnvNodeEnvs {
DEVELOPMENT = 'development',
}

export enum FlowBuilderJSONVersion {
DRAFT = 'draft',
LATEST = 'latest',
}

export interface KnowledgeBaseResponse {
answer: string
hasKnowledge: boolean
Expand Down
@@ -0,0 +1,11 @@
import { createContext } from 'react'

import { WebviewContentsContextType } from './types'

export const WebviewContentsContext = createContext<WebviewContentsContextType>(
{
getTextContent: () => undefined,
getImageSrc: () => undefined,
setCurrentLocale: () => undefined,
}
)
3 changes: 3 additions & 0 deletions packages/botonic-plugin-flow-builder/src/webview/index.ts
@@ -0,0 +1,3 @@
export { WebviewContentsContext } from './contents-context'
export * from './types'
export { useWebviewContents } from './use-webview-contents'
47 changes: 47 additions & 0 deletions packages/botonic-plugin-flow-builder/src/webview/types.ts
@@ -0,0 +1,47 @@
import { FlowBuilderJSONVersion } from '../types'

export enum WebviewContentType {
TEXT = 'webview-text',
IMAGE = 'webview-image',
}

export interface WebviewContentsResponse {
webview_contents: (WebviewTextContent | WebviewImageContent)[]
}

export interface WebviewTextContent {
code: string
type: WebviewContentType.TEXT
content: {
text: { message: string; locale: string }[]
}
}

export interface WebviewImageContent {
code: string
type: WebviewContentType.IMAGE
content: {
image: { file: string; locale: string }[]
}
}

export interface UseWebviewContentsProps {
apiUrl?: string
version?: FlowBuilderJSONVersion
orgId: string
botId: string
webviewId: string
locale: string
}

export interface UseWebviewContents {
isLoading: boolean
error: boolean
webviewContentsContext: WebviewContentsContextType
}

export interface WebviewContentsContextType {
getTextContent: (code: string) => string | undefined
getImageSrc: (code: string) => string | undefined
setCurrentLocale: (locale: string) => void
}
@@ -0,0 +1,78 @@
import axios from 'axios'
import { useEffect, useState } from 'react'

import { FLOW_BUILDER_API_URL_PROD } from '../constants'
import { FlowBuilderJSONVersion } from '../types'
import {
UseWebviewContents,
UseWebviewContentsProps,
WebviewContentsResponse,
WebviewContentType,
WebviewImageContent,
WebviewTextContent,
} from './types'

export function useWebviewContents({
apiUrl = FLOW_BUILDER_API_URL_PROD,
version = FlowBuilderJSONVersion.LATEST,
orgId,
botId,
webviewId,
locale,
}: UseWebviewContentsProps): UseWebviewContents {
const [textContents, setTextContents] = useState<WebviewTextContent[]>()
const [imageContents, setImageContents] = useState<WebviewImageContent[]>()
const [currentLocale, setCurrentLocale] = useState(locale)
const [isLoading, setLoading] = useState(false)
const [error, setError] = useState(false)

useEffect(() => {
const getResponseContents = async () => {
setLoading(true)
const url = `${apiUrl}/webview/${version}`
try {
const response = await axios.get<WebviewContentsResponse>(url, {
params: { org: orgId, bot: botId, webview: webviewId },
})

const textResponseContents = response.data.webview_contents.filter(
webviewContent => webviewContent.type === WebviewContentType.TEXT
) as WebviewTextContent[]
setTextContents(textResponseContents)

const imageResponseContents = response.data.webview_contents.filter(
webviewContent => webviewContent.type === WebviewContentType.IMAGE
) as WebviewImageContent[]
setImageContents(imageResponseContents)
} catch (error) {
console.error('Error fetching webview contents:', error)
setError(true)
} finally {
setLoading(false)
}
}
getResponseContents()
}, [])

const getTextContent = (contentID: string): string | undefined => {
return textContents
?.find(textContent => textContent.code === contentID)
?.content.text.find(text => text.locale === currentLocale)?.message
}

const getImageSrc = (contentID: string): string | undefined => {
return imageContents
?.find(imageContent => imageContent.code === contentID)
?.content.image.find(image => image.locale === currentLocale)?.file
}

return {
isLoading,
error,
webviewContentsContext: {
getTextContent,
getImageSrc,
setCurrentLocale,
},
}
}
19 changes: 0 additions & 19 deletions packages/botonic-plugin-flow-builder/tsconfig.base.json

This file was deleted.

4 changes: 2 additions & 2 deletions packages/botonic-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@botonic/react",
"version": "0.25.0",
"version": "0.26.0-beta.0",
"license": "MIT",
"description": "Build Chatbots using React",
"main": "./lib/cjs",
Expand All @@ -20,7 +20,7 @@
"lint_core": "../../node_modules/.bin/eslint_d --cache --quiet '.*.js' '*.js' 'src/**/*.js*' --fix"
},
"dependencies": {
"@botonic/core": "^0.25.0",
"@botonic/core": "^0.26.0-beta.0",
"axios": "^1.6.8",
"emoji-picker-react": "^4.4.9",
"framer-motion": "^3.1.1",
Expand Down

0 comments on commit c5f3601

Please sign in to comment.