question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Re-design actions to better support the reality of how actions are used

See original GitHub issue

GOALS

  • Make it possible for actions to be universal while still supporting custom actions
  • Make supporting custom actions easy since it’s a very common scenario
  • Rationalize actions and selectAction so that selectAction doesn’t have properties specific to action actions, like title

actions and selectAction rationalization Today, selectAction takes a type of Action. However, actions have properties like title (and in the future, image, etc). The title is meaningless to a selectAction, which by definition is just a touch target and therefore has no title to display.

Therefore, there needs to be a core invoke action that both of these share. Let’s call it Command. An Action has a title and a command, and a container just simply has a command property.

Proposal to rationalize selectAction Actions becomes one single type that contains a Command…

Property Type Required Description
type “Action” false
title string true Label for button or link that represents this action
speak Speak false Specifies what should be spoken for this entire element. This is simple text or SSML fragment
command IAdaptiveCommand true Command to be invoked when button is clicked

selectAction on container/image/etc becomes command…

Property Type Required Description
type “Container” true
items CardElement[] true The items that are to be displayed in this container.
actions Action[] false Actions associated with this container
selectAction Action false Action to perform for a tap on this container, (this allows entire container to act as an action)
command IAdaptiveCommand false Command to be invoked when this container is clicked
style normal, emphasis normal a container can group elements together in a normal or emphasized style

And then we introduce the IAdaptiveCommand type… The only properties are type and fallbackCommand. The inheriting command types get to define their properties…

Property Type Required Description
type string true The type of command. OpenUrl and ShowCard are natively supported. Hosts can have their own custom commands.
fallbackCommand IAdaptiveCommand false A command to be invoked if the specified command is not supported (more on this further down below)

The two system-supported commands that will implement IAdaptiveCommand out of the box…

Property Type Required Description
type “OpenUrl” true An OpenUrl command.
url string true Default (browser) url to use
Property Type Required Description
type “ShowCard” true A ShowCard command.
card AdaptiveCard true Card to be shown when this action is invoked. It is up to client to decide how to show this card (inline, popup, etc).

WHAT A DEVELOPER SENDS

{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "Image",
            "url": "https://unsplash.it/200",
            "size": "auto",
            "command": {
                "type": "OpenUrl",
                "url": "http://contoso.com/tomspie"
            }
        }
    ],
    "actions": [
        {
            "type": "Action",
            "title": "More Info",
            "command": {
                "type": "OpenUrl",
                "url": "http://contoso.com/moreinfo"
            }
        }
    ]
}

RATIONALIZING UNIVERSAL AND CUSTOM ACTIONS Custom actions are a very common scenario that almost all hosts have. But we have a strong desire for these cards to be portable and work everywhere. Let’s look at how we can achieve both of these things…

WHAT WE HAVE TODAY

Toast notifications OWA Skype
Action.Http NOT SUPPORTED ? NOT SUPPORTED
Action.OpenUrl Need additional properties like PFN, maybe via extensions Yes? Yes, but handled by Skype app
Action.ShowCard Yes Yes? Yes
Action.Submit NOT SUPPORTED Yes? Yes, but uses custom data properties to determine submit types
Custom actions Foreground activation Background activation Dismiss Snooze Invoke Add-In command Technically using Action.Submit as a custom action by adding custom properties in the data field

Here’s some of my thoughts on the existing actions…

Action Purpose Proposed change
Action.Http Not widely adopted, few hosts support it, therefore not universal. Remove Action.Http. Hosts that want to support that can use a custom action.
Action.OpenUrl Universal action that can be supported by all hosts. Enables the same card to function correctly across multiple hosts. Implement this by default in the renderers so universal and supported out-of-box (and allow extensibility if hosts want to handle it themselves)
Action.ShowCard Universal action that can be supported by all hosts. Implement this by default in the renderers (using show inline) so it’s universal and supported out-of-box (and allow extensibility if hosts want to handle it themselves)
Action.Submit Hosts do their own thing, some provide special activations, etc Make this extensible to reflect what hosts are actually doing, like “WindowsAction.Dismiss” or “SkypeAction.InlineReply”, etc. Action.Submit on its own simply isn’t portable and universal.

WHY ACTION.SUBMIT ISN’T UNIVERSAL Action.Submit is, by definition, local to the host. On Skype, it’ll submit an action to your bot host - you need to have a connection to that bot wired up to submit that action. A Windows toast notification would have no way of submitting something to a Skype bot. So an Action.Submit written for a Skype bot would never just “magically work” on a toast notification.

PROPOSAL FOR UNIVERSAL ACTIONS AND BETTER CUSTOM ACTIONS Remove “Action.Submit” and allow any custom command. We’ll pass through all the properties back to the app. Devs can provide a fallbackCommand that can be universally supported.

  "actions": [
    {
      "type": "Action",
      "title": "Website",
      "command": {
        "type": "OpenUrl",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
      }
    },
    {
      "type": "Action",
      "title": "Reviews",
      "command": {
        "type": "Toast.ForegroundActivation",
        "arguments": "action=viewReviews&restaurant=TomsPie",
        "fallbackCommand": {
          "type": "OpenUrl",
          "url": "https://yelp.com/TomsPie/reviews"
        }
      }
    }
  ]

REQUIREMENTS

  • Default universal commands are supported out of box (OpenUrl, ShowCard)
  • Custom commands are able to be defined by hosts
  • Custom commands support a fallbackCommand
  • Action or touch target only rendered if it command be invoked (therefore host needs to support the custom command or there needs to be a universal fallback provided)
  • fallbackCommand can be provided on a fallbackCommand, therefore allowing a series of fallbacks

Host provides their command handlers when configuring their renderer…

_renderer = new XamlCardRenderer();
_renderer.SetCommandHandler("Toast.BackgroundActivation", new ToastBackgroundActivationHandler());
_renderer.SetCommandHandler("Toast.ForegroundActivation", new ToastForegroundActivationHandler());
_renderer.SetCommandHandler("Toast.Snooze", new ToastSnoozeHandler());
_renderer.SetCommandHandler("Toast.Dismiss", new ToastDismissHandler());

private class ToastBackgroundActivationHandler : IAdaptiveCommandHandler
{
    public void HandleCommand(IAdaptiveCommand command)
    {
        // TODO: Gather inputs and activate background task of app
    }
}

When rendering, renderer checks whether handler is registered, if so, renders button… if not looks for fallback… if not drops button completely

private UIElement RenderAction(IAdaptiveActionElement action)
{
    // If we have command handler
    if (HasCommandHandler(action.Command.Type))
    {
        // Render button
        return new Button() { Content = action.Title };
    }

    // Else if fallback provided
    else if (action.Command.FallbackAction != null)
    {
        // Use the fallback command (note probably shouldn't actually modify object model)
        action.Command = action.Command.FallbackAction;

        // Try rendering with fallback
        return RenderAction(action.FallbackAction);
    }

    // Otherwise drop the button
    return null;
}

Then when the button is clicked…

private void InvokeCommand(IAdaptiveCommand command)
{
    // Get the handler (default or custom)
    IAdaptiveCommandHandler handler = GetCommandHandler(command.Type);

    // And have it handle the command invokation
    handler.HandleCommand(command);
}

image

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
dclauxcommented, Sep 11, 2017

Ah yes, that’s definitely a good point.

Personally, I am content with the current model and think that splitting things into command/action would be complicating things unnecessarily for card authors.

That said, I agree with your arguments, and fundamentally you are probably right. If there is a consensus around doing it, I’m ok with it.

0reactions
andrewleadercommented, Sep 11, 2017

Whoops, what I meant to say is that tooltips aren’t exclusively on clickable things. Therefore if we added them, we would add them to the generic inline AdaptiveElement rather than adding it to actions. That way devs could use them on both interactive and non-interactive elements.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Redesigning How We Work
But to manage that transition successfully, almost every organization will need to take two important steps. Give more support to teams and their...
Read more >
Getting organizational redesign right
A successful organizational redesign should better focus the resources of a company on its strategic priorities and other growth areas, reduce ...
Read more >
Implementation of Systems Redesign: Approaches to ...
This article will discuss our implementation strategy, provide examples of Lean and systems engineering tool applications, and provide an assessment of spread ...
Read more >
Job Redesign: A Practical Guide…
Job redesign is the process of rearranging tasks and responsibilities to better align roles with the changing environment inside and outside the ...
Read more >
Chapter: 1 Organizational Change and Redesign
As a result, organizations may shift focus, modify goals, restructure roles and responsibilities, and develop new forms. Adaptive efforts such as these may...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found