1

I am updating a task property through a request to my backend for an assigned task, this request is made on a custom component, on a button click during the call.

I can see in my twilio console that the update went fine and the new value is shown there, but the task prop on my flex components are not being updated, maintaining the same attributes since the assignment.

Is there a way to "refresh" the task in flex? I would need this updated attribute in order to perform a conditional rendering on my custom component.

Thanks in advance to anyone that helps me out.

import React from 'react';
import { withTaskContext } from '@twilio/flex-ui';

class IsRecording extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        // this.task = this.props.tasks
            return (
                <div>
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#E50000">
                        <path
                            d="M16 16c0 1.104-.896 2-2 2h-12c-1.104 0-2-.896-2-2v-8c0-1.104.896-2 2-2h12c1.104 0 2 .896 2 2v8zm8-10l-6 4.223v3.554l6 4.223v-12z"/>
                        <animate
                            attributeType="XML"
                            attributeName="fill"
                            values="#800;#f00;#800;#800"
                            dur="1.5s"
                            repeatCount="indefinite"/>
                    </svg>
                </div>
            )
    }
};

export default withTaskContext(IsRecording);
anon_346
  • 13
  • 5

2 Answers2

3

Twilio developer evangelist here.

If you have access to the task object within Flex you do not need to do so via the back-end. Instead, you can call on task.setAttributes and that will update the task attributes directly and cause it to update the state everywhere within Flex.

However, the difficulty here is that a supervisor will not have a live view on tasks, so you need to do a little more work to have the supervisor listen for changes on the tasks. I had a play around and got this working. It uses the Sync liveQuery interface to subscribe to updates to workers' tasks.

We create the live query subscription on componentDidMount. The first argument when creating the live query is "tr-task" and this refers to all tasks in the system. The second argument is the query, in this case we are querying for all tasks for this worker.

Once we get the query we can load the current items and set them in the state. We then listen for additions, updates and removals and update the state accordingly.

In the render function we calculate whether any of the tasks have an isRecording attribute and display the icon accordingly.

Here's the <IsRecording> component:

import React from "react";
import { Manager } from "@twilio/flex-ui";

class IsRecording extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tasks: [],
    };
  }

  componentDidMount() {
    Manager.getInstance()
      .insightsClient.liveQuery(
        "tr-task",
        `data.worker_sid == "${this.props.worker.sid}"`
      )
      .then((query) => {
        this.liveQuery = query;
        const items = query.getItems();
        this.setState({ tasks: Object.values(items) });
        query.on("itemAdded", (item) => {
          this.setState((prevState) => ({
            tasks: [...prevState.tasks, item.value],
          }));
        });
        query.on("itemUpdated", (item) => {
          this.setState((prevState) => ({
            tasks: [
              ...prevState.tasks.filter((task) => task.sid !== item.value.sid),
              item.value,
            ],
          }));
        });
        query.on("itemRemoved", (item) => {
          this.setState((prevState) => ({
            tasks: prevState.tasks.filter(
              (task) => task.sid !== item.previousItemData.sid
            ),
          }));
        });
      })
      .catch((err) =>
        console.debug(`Error when subscribing to live updates for Tasks`, err)
      );
  }

  componentWillUnmount() {
    if (this.liveQuery) {
      this.liveQuery.removeAllListeners();
      this.liveQuery.close();
    }
  }

  render() {
    if (this.state.tasks.some((task) => task.attributes.isRecording)) {
      return (
        <div>
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#E50000">
            <path d="M16 16c0 1.104-.896 2-2 2h-12c-1.104 0-2-.896-2-2v-8c0-1.104.896-2 2-2h12c1.104 0 2 .896 2 2v8zm8-10l-6 4.223v3.554l6 4.223v-12z" />
            <animate attributeType="XML" attributeName="fill" values="#800;#f00;#800;#800" dur="1.5s" repeatCount="indefinite" />
          </svg>
        </div>
      );
    } else {
      return null;
    }
  }
}

export default IsRecording;

You attach it to the WorkersDataTable like this:

    flex.WorkersDataTable.Content.add(
      <ColumnDefinition
        key="is-recording"
        header={"Recording"}
        content={(items) => {
          return (
            <IsRecording
              key={`is-recording-${items.worker.sid}`}
              worker={items.worker}
            />
          );
        }}
      />
    );
philnash
  • 70,667
  • 10
  • 60
  • 88
  • Hello Phil, first thank you for your help, I was able to update the task through flex without touching the backend of my code. But my problem persists, the twilio task in the current user updates correctly, but on the Supervisor account the task attributes for this user never update. – anon_346 Jun 07 '21 at 09:40
  • The component I am making is for the supervisor view so I would need for it to be updated whenever the user performing the call updates it. This is how I am passing the task to my component: ```flex.WorkersDataTable.Content.add( }/>);``` And then access it like this inside the component: ```this.task = this.props``` – anon_346 Jun 07 '21 at 09:40
  • You are setting a `tasks` property and then trying to access the `task` property within the component, which isn't going to work. Can you edit your question with the code for the component that you are talking about here? It would help to see how it's implemented. – philnash Jun 07 '21 at 23:45
  • Sure, just did it. I need to conditionally render that div based on a task property "isRecording" being true or false, which is an attribute that changes during calls so it always starts as false. – anon_346 Jun 08 '21 at 06:17
  • I've edited my answer with how I think this should look. – philnash Jun 08 '21 at 07:24
  • this: ```const { task } = this.props;``` is always undefined Sure I dont need to pass nothing to my component? – anon_346 Jun 08 '21 at 07:43
  • Ah, then the component you’re injecting it into might not be within a task context provider. I’ll have to look into it a bit more to understand what’s going on. – philnash Jun 08 '21 at 07:52
  • Yes, that's my problem. WorkersDataTable doesn't seem to have access to updated tasks. – anon_346 Jun 08 '21 at 08:08
  • Right, a `WorkersDataTable` won't have a single task because it is dealing with potentially multiple workers with multiple tasks. Digging into the components, it looks to me as though you want to deal with the [`Supervisor.TaskCard`](https://assets.flex.twilio.com/docs/releases/flex-ui/1.24.0/Supervisor%25E2%2580%25A4TaskCard.html) which is the component that shows the actual task the worker is working on and thus has a task context. I updated my answer above to change where the component is being inserted. Let me know if it helps. – philnash Jun 08 '21 at 08:31
  • I am not being able to user Supervisor.Taskcard, ```Unresolved variable TaskCard ``` and in js console ```PluginManager: There was an error during 'plugin-name-flex' plugin initialization Original error: "Cannot read property 'Content' of undefined"``` Some options are available though like TaskInfoPanel, TaskCanvas, TaskCanvasHeader, etc... – anon_346 Jun 08 '21 at 08:54
  • Ok, I had been going about this incorrectly and I will explain why soon. I'm working on a component that you can use for this now. It might take a little while as it's a new corner of Flex development for me. – philnash Jun 09 '21 at 01:02
  • @anon_346 I've got it. Please see the edited answer above with my now working example. Turns out supervisors do not have a live view on all workers' tasks, so you have to subscribe to them intentionally. Let me know how you get on with that. – philnash Jun 09 '21 at 04:33
  • It's updating the task, thank you! It has an error about item.value.sid being undefined on itemRemoved but I am working on that. – anon_346 Jun 09 '21 at 06:55
  • Ah, sorry, I didn't actually test the `itemRemoved` event (though I decided it should be added for completeness). Looks like the event doesn't return the `item.data` but `item.previousItemData`. I've updated my answer. – philnash Jun 09 '21 at 06:59
0

I'm accomplishing this through reading from the redux store. Below has been modified so I don't reveal proprietary knowledge.

  1. Create a custom listener for the Twilio reservationCreated event
manager.workerClient.on("reservationCreated", this.acceptTask);
  1. The below fetches the updated task. If we used task directly it would be the stale object unless things have changed.
async acceptTask(reservation) {
  const updatedTask = this.manager.store.getState().flex.worker.tasks.get(reservation.task.sid);
  // consume it
  // get the updated attributes with updatedTask.attributes
}

Hope that works for you / helps.

beatyt
  • 98
  • 1
  • 9
  • Hello, thank you for your help. I am trying to implement this listener ```manager.workerClient.on("reservationCreated", this.acceptTask);``` on my flex plugin but without luck. It does not throw any error besides the lint "Unresolved function or method on() " but it breaks my CRM window and hides the video funcionality. – anon_346 Jun 07 '21 at 16:47
  • Where are you initializing the listener at? You may need to put it inside of an `init` function if you aren't. `export default class MyComponent extends FlexPlugin { init(flex, manager) { // add listener here } }` – beatyt Jun 07 '21 at 19:05
  • Inside the init function. I have other listeners there like for example ```manager.chatClient.on('channelJoined',...)```, but for workerClient it's not working. after initializing this listener I need to pass the manager to my supervisor view component like ```manager={manager}```? – anon_346 Jun 07 '21 at 19:33