Menu

Rate this page:

Thanks for rating this page!

We are always striving to improve our documentation quality, and your feedback is valuable to us. How could this documentation serve you better?

UI migration guide for Flex-UI and WebChat-UI 0.13 -> 1.0

This guide contains a list of the breaking changes introduced between version 0.13 and 1.0 of Flex UI. If you have done programmatic customizations to Flex UI or Flex WebChat UI, you will need to make sure that your custom code is updated to support these changes.

Breaking and notable changes

Using a custom Redux store

To use a custom redux store, use the Flex store enhancer with a store creation method:

const reducers = combineReducers({
    flex: Flex.FlexReducer,
    app: myReducer
});

const middleware = applyMiddleware();

const store = createStore(
    reducers,
    compose(
        middleware,
        Flex.applyFlexMiddleware()
    )
);

Flex
    .Manager.create(configuration, store)
    .then(manager => {     
        ReactDOM.render(
            <Provider store={store}>
                <Flex.ContextProvider manager={manager}>
                    <Flex.RootContainer />
                </Flex.ContextProvider>
            </Provider>,
            container
        );
    })

Plugins and Plugin Builder

After Flex GA, the preferred way of customizing Flex, both Twilio hosted and locally hosted deployment models, is plugins.

Flex plugins are essential to customizing Flex instances so your development team can share components with agents and supervisors across your organization. Read more about customizing Flex with plugins here

Theming changes

If you have styled you UI, using the theming object, then you will need to make sure you do the following changes:

The colorTheme object now has 4 parameters:

  • baseName: string - to set a predefined theme
  • colors: object - to define a set of base colors that are used throughout the UI to define your custom theme
  • light: boolean - controls whether UI will aim at choosing dark texts or light text colors to allow for readability. It also controls icon colors, hover colors and more
  • overrides: object - a set of style overrides for each component

An example of setting the color configurations in appConfig:

config.colorTheme = {
	baseName: "DarkTheme",
	colors: {
		base1: "blue",
		base2: "orange",
		base3: "yellow",
		base4: "black",
		base5: "white",
		base6: "pink",
		base7: "red",
		base8: "blue",
		base9: "brown",
		base10: "black",
		base11: "white",
	},
	light: false,
	overrides: {
		MainHeader: {
			Container: {
				background: "#35372c"
			}
		},
		SideNav: {
			Container: {
				background: "#35372c"
			},
			Button: {
				background: "35372c"
			},
		},
	}
}

React-router

We have introduced React Router for routing in Flex UI

  • New actions to navigate the browser to different locations in the way similar to HTML5
  • History API:
    • HistoryPush
    • HistoryReplace
    • HistoryGo
    • HistoryGoBack
    • HistoryGoForward
  • New property route for a View component to mount a view to a route different from its name

Support for browser and memory history that is configurable through the configuration file.

Making Flex UI work with routing within your application:

In case you are using routing libraries like react-router-redux or connected-react-router, you may wish to sync history between your application and Flex. To do so, provide the history object that you are using for your Router as a parameter to Flex store enhancer:

const reducers = combineReducers({
    flex: Flex.FlexReducer,
    app: myReducer
});

const history = createHistory();

const middleware = applyMiddleware();

const store = createStore(
    reducers,
    compose(
        middleware,
        Flex.applyFlexMiddleware(history)
    )
);

Flex
    .Manager.create(configuration, store)
    .then(manager => {        
        ReactDOM.render(
            <Provider store={store}>
                <ConnectedRouter history={history}>
                    <Switch>
                        <Route path="/hi" component={() => {
                            setTimeout(() => { history.push("/"); }, 5000);
                            return (
                                <div>Hi! I will redirect to Flex in 5 seconds flat!</div>
                            );
                        }}></Route>
                        <Route component={() => {
                            return (<Flex.ContextProvider manager={manager}>
                                <Flex.RootContainer />
                            </Flex.ContextProvider>);
                        }}></Route>
                    </Switch>
                </ConnectedRouter>
            </Provider>,
            container
        );
    })

Custom components

We have introduced Component.Content.remove to allow the removal of components from dynamic component children (both native and programmatically-added ones). See the specification here.

This feature now requires a key property for all custom components passed to Component.Content.register/add. E.g. <div key="custom-key"/>

If you have added custom components to Flex UI, make sure they have a key property defined.

Data model changes

  • Property task type changed to ITask for task-based components. Previously, the TaskState interface, which had only source and reservation properties, was used. The source and reservation properties remain the same, but now attributes and other task properties can be accessed from the task object itself. For example, this.props.task.attributes can be used where applicable and there should be no further need to use the source sub-property (which refers to the Task Router SDK object).
  • Tasks in store are referenced by reservation sid now. (Previously, they had been referenced by task sid.)
  • Task objects in the Actions framework have a new field, sourceObject, which will point to the actual SDK object:
    • Reservation in case the data source for the task is TaskRouterSDK. Applies for components and actions in AgentDesktopView.
    • InsightsObject in case the data source for the task is InsightsSDK. Applies for components and actions in TeamsView
    • Source is still there, but is deprecated.

Changes to Actions

  • All actions that had taskSid in the payload will now expect sid instead
  • Payloads have changed for some Actions:
    • SetActivity action now has a payload in the form of {activitySid: string; activityName?: string; activityAvailable?: boolean}. Only activitySid is used in the default implementation, and is required when invoking the action by the user, but the other two parameters are filled for better context for users who override the action and need more information on what the new activity will be. Additionally, SetActivity can now be called with just activityName in the payload.
    • SelectTaskInSupervisor now expects/provides an object as its payload in the form of {task?: ITask, sid?: string}. Providing either will autofill the other, so both will be available for whoever taps into the action via the Actions framework.
    • SelectWorkerInSupervisor now expects/provides an object as its payload in the form of {worker?: ITask, workerSid?: string}. Providing either will autofill the other, so both will be available for whoever taps into the action via the Actions framework.
    • MonitorCall now expects/provides an object as its payload in the form of {task?: ITask, sid?: string}. Providing either will autofill the other, so both will be available for whoever taps into the action via the Actions framework.
    • HoldCall will no longer toggle the hold state, but will instead be meant only for call holding. A separate UnholdCall action was added.

The HangupCall and HoldCall actions will now accept optional parameters sid:string or task:ITask in the payload object. Actions will work without them if just one call is available, but it is advised to use those parameters to be more specific for future multi-call scenarios. Even if the task/taskSid are not specified, when adding listeners or overriding these actions, those parameters are filled out automatically.

Call recordings

Call recording can be enabled from the configuration service. This option sets the following parameters in the conference payload when a conference is created:

  • "conferenceRecord": true,
  • "conferenceRecordingStatusCallback":
    "https://webhooks.twilio.com/v1/Accounts/{accountSid}/Workspaces/{workspaceSid}/Tasks/{taskSid}/FlexRecordingWebhook",
  • "conferenceRecordingStatusCallbackMethod": "POST"

You can retrieve the accountSid and workspaceSid via the configuration services, i.e.

manager.serviceConfiguration.account_sid

manager.serviceConfiguration.taskrouter_workspace_sid

If you have replaced an AcceptTask action, registered a custom action that issues a conference instruction, or directly issued a conference instruction via TaskRouterSDK (or any other way of setting a conference payload), make sure that the above params are set properly to support call recording.

Transfers and endConferenceOnExit

To enable call transfers, calls will be currently accepted with endConferenceOnExit set to false, meaning that the call will not stop for the customer when an agent hangs up, and to end the call, the customer will need to hang up themselves (otherwise, they will stay in the call alone).

If the above behavior is not acceptable for your use case and you will not be using transfer functionality, you may opt out of transfers by setting the disableTransfers config option to true. If this option is set to true, the endConferenceOnExit option will be set to true, but transfers to other agents will not be available.

Functions

All of the functions to orchestrate different channels and tokens have been removed. Now, a combination of backend services and Studio flows is used instead.

WebChat

  • Theming - the same changes that have been implemented for Flex UI have been implemented here as well.

  • Pre-engagement data - when a chat session is created, pre-engagement data is saved to chat channel attributes (channel.attributes.pre_engagement_data), that can be accessed in the Studio Flow or directly from Programmable Chat via the SDK or REST API.

  • Configuration options startEngagementUrl and serviceBaseUrl have been removed and new configuration options are required in the application configuration:

    • accountSid - Account SID where Flex is running

    • flexFlowSid - Flex Flow SID created at onboarding for chat

The accountSid and flexFlowSid can be found on the admin dashboard development configuration page: https://flex.twilio.com/admin/developers

Changes to Manager object

  • ContactCenterManager was renamed to Manager
  • Changed Manager.create method signature - first accountSid parameter was dropped

Configuration service

We have started using a Twilio configuration service for project-level remote configuration that can be shared between different instances of Flex UI.

  • A new method for Manager's instances fetchConfiguration asynchronously retrieves a configuration from the Flex Configuration service
  • A new method for Manager's instance updateConfig merges provided configurations on top of any existing configuration
  • Manager's instance configuration behavior:
    • Whenever a new instance of Manager is created, it will fetch the remote configuration from Flex Configuration service. Local configuration will be applied on top of remote configuration
    • Manager's instance configuration setter is deprecated

Custom channels

The Task Channel Definition API has been introduced. This is how all of the native channels are defined in Flex UI, and is the advised way of registering your own custom channels or customizing existing ones. The specification can be found here.

Other

  • Only JWE tokens are now supported
  • The SupervisorDesktopView component was renamed to TeamsView
  • The template SideNavSupervisorView was renamed to SideNavTeamsView
  • Templates and channel definitions use the channelType attribute from a task to detect the chat channel type. Previously, the endpoint parameter was used.

A new Flex API method, progress, renders a fancy loading indicator to a provided selector: Flex.progress("#container")

Migrating from sample app 0.13 -> 1.0

  • Upgrade packages in package.json
    "@twilio/flex-ui": "^1.0.0",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    ADD: "eslintConfig": {
    "extends": "react-app"
    },
    REMOVE: react-app-rewired": "1.5.2”
    UPDATE: scripts: {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test"
    "eject": "react-scripts eject
    }
  • Update index.js
    import React from "react";
    import ReactDOM from "react-dom";
    import "regenerator-runtime/runtime";
    import * as Flex from "@twilio/flex-ui";
    import "./index.css";
    import App from "./App";
    import registerServiceWorker from "./registerServiceWorker";
     const configStorageKey = "FLEX_CONFIG";
    const mountNode = document.getElementById("root");
     window.onload = () => {
      const predefinedConfig = window.appConfig || {};
      const runtimeConfig = getRuntimeConfig();
       const configuration = {
        ...predefinedConfig,
        ...runtimeConfig
      };
       Flex
        .progress(mountNode)
        .Manager.create(configuration)
        .then(manager => renderApp(manager))
        .catch(error => handleError(error));
    };
     function renderApp(manager) {
      ReactDOM.render(
        <App manager={manager} />,
        mountNode
      );
    }
     function handleError(error) {
      console.error("Failed to initialize Flex", error);
       const missingAccountSid = error instanceof Flex.ConfigError && error.key === "accountSid";
       if (!missingAccountSid) {
        throw error;
      }
       ReactDOM.render(
        <Flex.RuntimeLoginView
          onSuccess={(loginData, runtimeDomain) => {
            setRuntimeConfig(loginData, runtimeDomain);
            window.location.reload();
          }}
        />,
        mountNode
      );
    }
     function setRuntimeConfig(loginData, runtimeDomain) {
        const config = {
            serviceBaseUrl: runtimeDomain,
            sso: {
                accountSid: loginData.accountSid
            }
        };
        const serializedConfig = JSON.stringify(config);
        localStorage.setItem(configStorageKey, serializedConfig);
    }
     function getRuntimeConfig() {
        const serializedConfig = localStorage.getItem(configStorageKey);
        localStorage.removeItem(configStorageKey);
        const config = JSON.parse(serializedConfig || "{}");
        return config;
    }
     registerServiceWorker();
    
  • Update App.js
    Move customization to manager into the render method below:
    import React from "react";
    import * as Flex from "@twilio/flex-ui";
    
    class App extends React.Component {
      render() {
        const { manager } = this.props;
    
        if (!manager) {
          return null;
        }
    
        return (
          <Flex.ContextProvider manager={manager}>
            <Flex.RootContainer />
          </Flex.ContextProvider>
        );
      }
    }
    
    export default App;
    
  • Refactor Checklist:
    • Any components accessing task status like this: this.props.task.reservation now can access status like this: this.props.task.status
    • If getting reservation like this: const reservation = StateHelper.getReservation(task.sid); now can use this: const reservation = payload.task.sourceObject;
    • Be sure to update any Actions you are using to reference reservation sid, not taskSid
Rate this page:

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.