Skip to content

Commit

Permalink
feat(content): add notice when content shared
Browse files Browse the repository at this point in the history
  • Loading branch information
rob-gordon committed Dec 6, 2021
1 parent eecee77 commit ec8ef0c
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 82 deletions.
45 changes: 25 additions & 20 deletions packages/studio-ui/src/web/components/Content/Select/Widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import withLanguage from '../../Util/withLanguage'
import CreateOrEditModal from '../CreateOrEditModal'

import style from './style.scss'
import { WidgetContext } from './WidgetContext'

interface DispatchProps {
deleteMedia: (formData: any) => Promise<void>
Expand Down Expand Up @@ -125,27 +126,31 @@ class ContentPickerWidget extends Component<Props, State> {
}

return (
<ControlGroup fill>
<div
className={style.clickableInput}
onClick={() => (contentItem ? this.editItem() : window.botpress.pickContent({ contentType }, this.onChange))}
>
<InputGroup
placeholder={placeholder}
value={textContent}
disabled
id={inputId || ''}
className={style.contentInput}
<WidgetContext.Provider value={{ itemId: this.props.itemId }}>
<ControlGroup fill>
<div
className={style.clickableInput}
onClick={() =>
contentItem ? this.editItem() : window.botpress.pickContent({ contentType }, this.onChange)
}
>
<InputGroup
placeholder={placeholder}
value={textContent}
disabled
id={inputId || ''}
className={style.contentInput}
/>
{contentItem && <Button icon="edit" className={Classes.FIXED} />}
</div>
<Button
icon="folder-open"
onClick={() => window.botpress.pickContent({ contentType }, this.onChange)}
className={Classes.FIXED}
/>
{contentItem && <Button icon="edit" className={Classes.FIXED} />}
</div>
<Button
icon="folder-open"
onClick={() => window.botpress.pickContent({ contentType }, this.onChange)}
className={Classes.FIXED}
/>
{this.renderModal()}
</ControlGroup>
{this.renderModal()}
</ControlGroup>
</WidgetContext.Provider>
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContext } from 'react'

export const WidgetContext = createContext({
itemId: ''
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Callout, Intent } from '@blueprintjs/core'
import React, { useContext } from 'react'
import { connect } from 'react-redux'
import { RootReducer } from '~/reducers'
import { FlowReducer } from '~/reducers/flows'
import { WidgetContext } from '../Content/Select/WidgetContext'
import { getContentItemUsage } from '../Shared/Utils'
import style from './style.scss'

/** Displays a warning to user if cms content used in multiple places */
export function ContentNotice({ flows, qnaUsage }: { flows: FlowReducer; qnaUsage: any }) {
const { itemId } = useContext(WidgetContext)
const usage = itemId ? getContentItemUsage(itemId, flows, qnaUsage) : []
if (usage.length <= 1) {
return null
}
return (
<Callout className={style.contentNotice} title="Changing Shared Content" intent={Intent.PRIMARY} icon="info-sign">
This content is used in {usage.length} places. Changing it will affect all places.
</Callout>
)
}

const mapStateToProps = (state: RootReducer) => ({
flows: state.flows,
qnaUsage: state.content.qnaUsage
})
export default connect(mapStateToProps)(ContentNotice)
30 changes: 17 additions & 13 deletions packages/studio-ui/src/web/components/ContentForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import style from '~/views/FlowBuilder/sidePanelTopics/form/style.scss'
import withLanguage from '../Util/withLanguage'

import ArrayFieldTemplate from './ArrayFieldTemplate'
import ContentNotice from './ContentNotice'
import FlowPickWidget from './FlowPickWidget'
import ArrayMl from './i18n/Array'
import renderWrapped from './i18n/I18nWrapper'
Expand Down Expand Up @@ -182,19 +183,22 @@ const ContentForm: FC<Props> = props => {
}

return (
<Form<FormData>
{...props}
formData={currentFormData}
formContext={context}
safeRenderCompletion
widgets={widgets}
fields={fields}
ArrayFieldTemplate={ArrayFieldTemplate}
onChange={handleOnChange}
schema={translatePropsRecursive(schema)}
>
{props.children}
</Form>
<>
<ContentNotice />
<Form<FormData>
{...props}
formData={currentFormData}
formContext={context}
safeRenderCompletion
widgets={widgets}
fields={fields}
ArrayFieldTemplate={ArrayFieldTemplate}
onChange={handleOnChange}
schema={translatePropsRecursive(schema)}
>
{props.children}
</Form>
</>
)
}

Expand Down
4 changes: 4 additions & 0 deletions packages/studio-ui/src/web/components/ContentForm/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@
.warning {
color: var(--lighthouse);
}

.contentNotice {
margin-bottom: 20px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Please do not change this file!
interface CssExports {
'actionsWrapper': string;
'contentNotice': string;
'flexContainer': string;
'missingIcon': string;
'warning': string;
Expand Down
70 changes: 69 additions & 1 deletion packages/studio-ui/src/web/components/Shared/Utils/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { FlowNode } from 'botpress/sdk'
import { ActionBuilderProps, ContentElement, FlowNode } from 'botpress/sdk'
import { FlowView, NodeView } from 'common/typings'
import { FlowReducer } from '~/reducers/flows'

export { default as ElementPreview } from './ElementPreview'
export { toastSuccess, toastFailure, toastInfo, Timeout } from './Toaster'
Expand Down Expand Up @@ -38,3 +40,69 @@ export const getFlowLabel = (name: string) => {
return name
}
}

/* MOVE THESE TYPES INTO ONE LOCATION */
interface ContentUsage {
type: string
id?: string
name: string
node?: string
count: number
}

type ContentElementUsage = {
usage: ContentUsage[]
} & ContentElement

export const getContentItemUsage = (elementId: string, flows: FlowReducer, qnaUsage: ContentElementUsage[]) => {
const elementUsage: ContentUsage[] = []
Object.values(flows.flowsByName).forEach((flow: FlowView) => {
// Skip skill flows
if (flow.skillData) {
return
}

flow.nodes.forEach((node: NodeView) => {
const usage: ContentUsage = {
type: 'Flow',
name: flow.name,
node: node.name,
count: 0
}

const addUsage = (v: string | ActionBuilderProps) => {
if (typeof v === 'string' && v.startsWith(`say #!${elementId}`)) {
if (!usage.count) {
elementUsage.push(usage)
}
usage.count++
}
}

const addNodeUsage = (node: NodeView) => {
node.onEnter?.forEach(addUsage)
node.onReceive?.forEach(addUsage)
}

if (node.flow && node.type === 'skill-call') {
const nodeSubFlow = flows.flowsByName[node.flow]
nodeSubFlow?.nodes.forEach((node: NodeView) => {
addNodeUsage(node)
})
} else {
addNodeUsage(node)
}
})
})

const usage = qnaUsage?.[`#!${elementId}`]
usage &&
elementUsage.push({
type: 'Q&A',
id: usage.qna,
name: usage.qna.substr(usage.qna.indexOf('_') + 1),
count: usage.count
})

return elementUsage
}
50 changes: 2 additions & 48 deletions packages/studio-ui/src/web/views/Content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '~/actions'
import CreateOrEditModal from '~/components/Content/CreateOrEditModal'
import { Container } from '~/components/Shared/Interface'
import { getContentItemUsage } from '~/components/Shared/Utils'
import { isOperationAllowed } from '~/components/Shared/Utils/AccessControl'
import DocumentationProvider from '~/components/Util/DocumentationProvider'
import { RootReducer } from '~/reducers'
Expand Down Expand Up @@ -80,54 +81,7 @@ class ContentView extends Component<Props, State> {

currentContentType() {
this.props.contentItems.forEach((element: ContentElementUsage) => {
element.usage = []
Object.values(this.props.flows.flowsByName).forEach((flow: FlowView) => {
// Skip skill flows
if (flow.skillData) {
return
}

flow.nodes.forEach((node: NodeView) => {
const usage: ContentUsage = {
type: 'Flow',
name: flow.name,
node: node.name,
count: 0
}

const addUsage = (v: string | ActionBuilderProps) => {
if (typeof v === 'string' && v.startsWith(`say #!${element.id}`)) {
if (!usage.count) {
element.usage.push(usage)
}
usage.count++
}
}

const addNodeUsage = (node: NodeView) => {
node.onEnter?.forEach(addUsage)
node.onReceive?.forEach(addUsage)
}

if (node.flow && node.type === 'skill-call') {
const nodeSubFlow = this.props.flows.flowsByName[node.flow]
nodeSubFlow?.nodes.forEach((node: NodeView) => {
addNodeUsage(node)
})
} else {
addNodeUsage(node)
}
})
})

const usage = this.props.qnaUsage?.[`#!${element.id}`]
usage &&
element.usage.push({
type: 'Q&A',
id: usage.qna,
name: usage.qna.substr(usage.qna.indexOf('_') + 1),
count: usage.count
})
element.usage = getContentItemUsage(element.id, this.props.flows, this.props.qnaUsage)
})

return this.state.modifyId
Expand Down

0 comments on commit ec8ef0c

Please sign in to comment.