Skip to content

Instantly share code, notes, and snippets.

@chase2981
Created April 5, 2020 05:27
Show Gist options
  • Save chase2981/5e3208e46ca747701eeb0219551823a9 to your computer and use it in GitHub Desktop.
Save chase2981/5e3208e46ca747701eeb0219551823a9 to your computer and use it in GitHub Desktop.
Twilio Flex MessagingCanvas Examples
import * as React from "react";
import {
Manager, Actions, MessagingCanvas, MessagingCanvasContext, DynamicComponent, ContextProvider, MessageList, StateHelper, TaskHelper, ITask,
Supervisor,
TaskChannelDefinition,
TaskContextProps,
Theme
} from '@twilio/flex-ui';
import CoreApiService from '../Shared/CoreApiService';
import { Channel } from 'twilio-chat/lib/channel';
import { createChatTaskChannel } from '@twilio/flex-ui/src/task_channels';
import { SupervisorState } from '@twilio/flex-ui/src/state/SupervisorState';
import { SessionState } from '@twilio/flex-ui/src/state/SessionState';
interface Props extends TaskContextProps {
key: string;
channelDefinition?: TaskChannelDefinition;
// channels: Channel[];
session: SessionState;
supervisor: SupervisorState;
task?: ITask;
theme?: Theme;
}
interface State {
callSid?: string;
channelSid?: string;
channel?: Channel;
task?: ITask;
}
export default class SupervisorTaskCanvasComponent extends React.Component<Props, State> {
coreApiSvc: CoreApiService;
channelSid: string;
myChannel: Channel;
task?: ITask;
constructor(public props: Props) {
super(props);
this.coreApiSvc = new CoreApiService();
}
componentDidMount() {
if (this.props && this.props.task) {
this.handleTaskChange(this.props.task);
}
}
componentDidUpdate(prevProps: Props, prevState: State) {
if (this.props && this.props.task && (!prevProps || !prevProps.task)) {
return this.handleTaskChange(this.props.task);
}
else if (prevProps && prevProps.task && this.props && this.props.task && prevProps.task.sid != this.props.task.sid) {
return this.handleTaskChange(this.props.task);
}
}
isMessageTask(task: ITask) {
return task.taskChannelUniqueName.includes('chat');
}
isVoiceTask(task: ITask) {
return task.taskChannelUniqueName.includes('voice');
}
handleVoiceTask(task: ITask) {
let voiceClient = Manager.getInstance().voiceClient;
this.channelSid = task.attributes.channelSid;
this.setState({ callSid: task.attributes.call_sid });
}
handleMessageTask(task: ITask) {
let chatClient = Manager.getInstance().chatClient;
this.channelSid = task.attributes.channelSid;
this.setState({ channelSid: task.attributes.channelSid });
chatClient.getChannelBySid(task.attributes.channelSid).then((channel: any) => {
// if (this.myChannel) {
// this.myChannel.removeMember(this.props.session.identity);
// }
this.myChannel = channel;
this.myChannel.getMembers().then((channelMembers) => {
if (!channelMembers.some(cm => cm.identity === this.props.session.identity)) {
// simply join the channel if not already joined, and everything else will just work, so long as the user has permissions to join
this.myChannel.join().then((r) => {
// var channelState = StateHelper.getChatChannelStateForTask(payload.task);
// var channelSid = TaskHelper.getTaskChatChannelSid(payload.task);
})
}
this.setState({ channel: this.myChannel }, () => {
/* after state updated callback */
})
});
}, (err) => {
console.warn(err);
})
}
handleTaskChange(task) {
this.setState({ task: task });
if (this.isMessageTask(task)) {
this.handleMessageTask(task);
}
if (this.isVoiceTask(task)) {
this.handleVoiceTask(task);
}
}
render() {
if (!this.state) {
return null;
}
return <MessagingCanvas autoInitChannel={true} sid={this.channelSid} key='demo-supervisor-task-canvas-messaging-canvas' showWelcomeMessage={false}></MessagingCanvas>
}
componentWillUnmount() {
// if (this.myChannel) {
// this.myChannel.removeMember(this.props.session.identity);
// }
}
}
import { connect } from 'react-redux';
import { FlexState, Manager, withTaskContext } from '@twilio/flex-ui';
import { RdFlexState } from 'core/rdFlexState';
import SupervisorTaskCanvasComponent from './SupervisorTaskCanvasComponent';
const mapStateToProps = (state: RdFlexState & FlexState, ownProps: any): any => {
if (!state || !state.flex || !state.rd)
return {};
return {
supervisor: state.flex.supervisor,
session: state.flex.session,
config: state.flex.config,
channels: state.flex.chat.channels
}
}
const mapDispatchToProps = {
// updateDisplay: updateDisplay,
// updateLeadCardForm: updateLeadCardForm,
// updateResidentCardForm: updateResidentCardForm,
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(SupervisorTaskCanvasComponent);
import { FlexPlugin } from "flex-plugin";
import * as React from "react";
// import RootContainer from "./RootContainer";
import * as Flex from "@twilio/flex-ui";
import { Manager, MessagingCanvas, Actions, SideLink, View } from '@twilio/flex-ui';
import SupervisorTaskCanvasContainer from './SupervisorTaskCanvasContainer';
import './SupervisorTaskCanvasComponent.scss';
interface Props {
key: string;
}
interface State {
channelSid?: string;
}
export default class SupervisorTaskCanvasFlexPlugin extends FlexPlugin {
state: State;
constructor(public props: Readonly<Props> = { key: 'SupervisorTaskCanvasFlexPlugin' }) {
super(props.key);
}
init(flex: typeof Flex, manager: Manager) {
flex.Supervisor.TaskOverviewCanvas.Content.add(
<SupervisorTaskCanvasContainer key='rd-supervisor-task-overview-canvas-supervisor-task-canvas-container'></SupervisorTaskCanvasContainer>,
{
// align: 'start',
// if: props => props.task.source.taskChannelUniqueName === "chat",
sortOrder: 1
}
)
}
}
import * as React from "react";
import {
Manager,
Actions,
MessagingCanvas,
MessagingCanvasContext,
DynamicComponent,
ContextProvider,
MessageList,
StateHelper,
TaskHelper,
ITask,
Supervisor,
TaskChannelDefinition,
Theme,
ThemeProvider,
Config,
MessageListItem
} from "@twilio/flex-ui";
import CoreApiService from "../../../Shared/CoreApiService";
import { Channel } from "twilio-chat/lib/channel";
import { SessionState } from '@twilio/flex-ui/src/state/SessionState';
interface Props {
channelSid?: string;
key: string;
session: SessionState;
taskChange?: Function;
theme: Theme;
}
interface State {
callSid?: string;
channel?: any;
channelSid?: string;
}
export default class TaskDetailMessagingCanvasComponent extends React.Component<Props, State> {
coreApiSvc: CoreApiService;
channelSid: string;
messagingCanvasRef: any;
myChannel: Channel;
task?: ITask;
static getDerivedStateFromProps(nextProps: Readonly<Props>, prevState: State) {
return {
...prevState,
...{
channelSid: nextProps.channelSid
}
}
}
constructor(public props: Props) {
super(props);
this.coreApiSvc = new CoreApiService();
this.messagingCanvasRef = React.createRef();
this.state = {
}
}
componentDidMount() {
if (this.props.channelSid) {
this.handleMessageTask(this.props.channelSid);
}
}
componentDidUpdate(prevProps: Props, prevState: State) {
if (this.props.channelSid && this.props.channelSid !== prevProps.channelSid) {
this.handleMessageTask(this.props.channelSid);
}
}
isMessageTask(task: ITask) {
return task.taskChannelUniqueName.includes('chat');
}
isVoiceTask(task: ITask) {
return task.taskChannelUniqueName.includes('voice');
}
handleVoiceTask(task: ITask) {
let voiceClient = Manager.getInstance().voiceClient;
this.channelSid = task.attributes.channelSid;
this.setState({ callSid: task.attributes.call_sid });
}
handleMessageTask(channelSid) {
let chatClient = Manager.getInstance().chatClient;
this.channelSid = channelSid;
this.setState({ channelSid: channelSid });
}
async update(newVal: any) {
return new Promise((resolve, reject) => {
this.setState(newVal, () => {
resolve(this.state);
});
});
}
render() {
const colors = this.props.theme.colors;
const palette: any = this.props.theme.palette;
const notFoundDetailTemplate = (
<div style={{
fontSize: '90px', textAlign: 'center', width: '100%',
margin: '25% 25%'
}}>
<svg version="1.1" viewBox="0 0 64 64" height="64" width="64"><g transform="matrix(2.6666666666666665,0,0,2.6666666666666665,0,0)"><path d="M 23.25,13.5c0.001-2.899-2.349-5.249-5.248-5.25c-0.001,0-0.001,0-0.002,0h-3c-2.899,0-5.25,2.351-5.25,5.25 s2.351,5.25,5.25,5.25h0.75l4.5,4.5v-5.024C22.079,17.362,23.246,15.522,23.25,13.5z " stroke="#707070" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M 6.75,12.75l-3,3v-5.024 C1.134,9.49,0.015,6.367,1.251,3.751C2.118,1.915,3.969,0.745,6,0.75h3c2.322,0,4.369,1.525,5.033,3.75" stroke="#707070" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>
</div>
);
return (
!this.state || !this.state.channelSid ? (
<div style={{ height: '90%', display: 'flex' }}>
{notFoundDetailTemplate}
</div>
) : (
<div className='task__details_cont' style={{ height: '90%' }}>
{this.state && this.state.channelSid ? (
<React.Fragment>
<MessagingCanvas autoInitChannel={true} sid={this.state.channelSid} key='rd-task-lookup-view-messaging-canvas' ref={this.messagingCanvasRef} showWelcomeMessage={false}></MessagingCanvas>
</React.Fragment>
) : null}
</div>
)
);
}
componentWillUnmount() {
if (this.myChannel) {
this.myChannel.removeMember(this.props.session.identity);
}
}
}
import { connect } from 'react-redux';
import { FlexState, Manager, withTheme } from '@twilio/flex-ui';
import { RdFlexState } from 'core/rdFlexState';
import TaskDetailMessagingCanvasComponent from './TaskDetailMessagingCanvasComponent';
const mapStateToProps = (state: RdFlexState & FlexState, ownProps: any) => {
if (!state || !state.flex || !state.rd)
return {};
return {
supervisor: state.flex.supervisor,
session: state.flex.session,
config: state.flex.config,
channels: state.flex.chat.channels
}
}
const mapDispatchToProps = {
}
export default
withTheme(
connect(
mapStateToProps,
mapDispatchToProps
)(TaskDetailMessagingCanvasComponent as any)
);
@chase2981
Copy link
Author

v2

here's the easier way to invoke the messaging-canvas in your flex apps, as of ~2021..

you no longer have to manually join the channel yourself and do all that jazz, you still need the supervisor programmable chat role setup on your user to auto-join the channels however..

TaskDetailCanvas.tsx

import * as React from "react";

import {
  Manager,
  Actions,
  MessagingCanvas,
  MessagingCanvasContext,
  DynamicComponent,
  ContextProvider,
  MessageList,
  StateHelper,
  TaskHelper,
  ITask,
  Supervisor,
  TaskChannelDefinition,
  Theme,
  ThemeProvider,
  Config,
  MessageListItem
} from "@twilio/flex-ui";
import CoreApiService from "../../../Shared/CoreApiService";

import { Channel } from "twilio-chat/lib/channel";
import { SessionState } from '@twilio/flex-ui/src/state/SessionState';

interface Props {
  channelSid?: string;
  key: string;
  session: SessionState;
  taskChange?: Function;
  theme: Theme;
}

interface State {
  callSid?: string;
  channel?: any;
  channelSid?: string;
}

export class TaskDetailCanvasComponent extends React.Component<Props, State> {
  coreApiSvc: CoreApiService;
  channelSid: string;
  messagingCanvasRef: any;
  myChannel: Channel;
  task?: ITask;

  static getDerivedStateFromProps(nextProps: Readonly<Props>, prevState: State) {
    return {
      ...prevState,
      ...{
        channelSid: nextProps.channelSid
      }
    }
  }

  constructor(public props: Props) {
    super(props);

    this.coreApiSvc = new CoreApiService();
    this.messagingCanvasRef = React.createRef();
    this.state = {
    }
  }

  componentDidMount() {
    if (this.props.channelSid) {
      this.handleMessageTask(this.props.channelSid);
    }
  }

  componentDidUpdate(prevProps: Props, prevState: State) {
    if (this.props.channelSid && this.props.channelSid !== prevProps.channelSid) {
      this.handleMessageTask(this.props.channelSid);
    }
  }

  isMessageTask(task: ITask) {
    return task.taskChannelUniqueName.includes('chat');
  }

  isVoiceTask(task: ITask) {
    return task.taskChannelUniqueName.includes('voice');
  }

  handleVoiceTask(task: ITask) {
    let voiceClient = Manager.getInstance().voiceClient;
    this.channelSid = task.attributes.channelSid;
    this.setState({ callSid: task.attributes.call_sid });
  }

  handleMessageTask(channelSid) {
    let chatClient = Manager.getInstance().chatClient;
    this.channelSid = channelSid;
    this.setState({ channelSid: channelSid });
  }

  async update(newVal: any) {
    return new Promise((resolve, reject) => {
      this.setState(newVal, () => {
        resolve(this.state);
      });
    });
  }

  render() {
    const colors = this.props.theme.colors;
    const palette: any = this.props.theme.palette;

    const notFoundDetailTemplate = (
      <div style={{
        fontSize: '90px', textAlign: 'center', width: '100%',
        margin: '25% 25%'
      }}>
        <svg version="1.1" viewBox="0 0 64 64" height="64" width="64"><g transform="matrix(2.6666666666666665,0,0,2.6666666666666665,0,0)"><path d="M 23.25,13.5c0.001-2.899-2.349-5.249-5.248-5.25c-0.001,0-0.001,0-0.002,0h-3c-2.899,0-5.25,2.351-5.25,5.25 s2.351,5.25,5.25,5.25h0.75l4.5,4.5v-5.024C22.079,17.362,23.246,15.522,23.25,13.5z " stroke="#707070" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M 6.75,12.75l-3,3v-5.024 C1.134,9.49,0.015,6.367,1.251,3.751C2.118,1.915,3.969,0.745,6,0.75h3c2.322,0,4.369,1.525,5.033,3.75" stroke="#707070" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>
      </div>
    );

    return (

      !this.state || !this.state.channelSid ? (
        <div style={{ height: '90%', display: 'flex' }}>
          {notFoundDetailTemplate}
        </div>
      ) : (
          <div className='task__details_cont' style={{ height: '90%' }}>
            {this.state && this.state.channelSid ? (
              <React.Fragment>
+                <MessagingCanvas autoInitChannel={true} sid={this.state.channelSid} key='rd-task-lookup-view-messaging-canvas' ref={this.messagingCanvasRef} showWelcomeMessage={false}></MessagingCanvas>
              </React.Fragment>
            ) : null}
          </div>
        )
    );
  }

  componentWillUnmount() {
    if (this.myChannel) {
      this.myChannel.removeMember(this.props.session.identity);
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment