6

I'm having a nightmare doing a lot of scenarios using Apps Script, but nothing works! I have a function that makes a GET request returns an array of cards. Now, sometimes I need this card refreshes again to fetch the new content.

function listTemplatesCards(){
  var getAllTemplates = getTemplates();
  var allTemplates = getAllTemplates.templates;
  var theUserSlug = getAllTemplates.user_slug;
  var templateCards = [];

  //There are templates
  if(allTemplates.length > 0){
    allTemplates.forEach(function(template){
      templateCards.push(templateCard(template, theUserSlug).build());
    });
    return templateCards;
  }
}

This function is called on onTriggerFunction. Now, if I moved to another card and I wanted to back again to the root but in clean and clear way, I use this but it doesn't work:

  //Move the user to the root card again
  var refreshNav = CardService.newNavigation().popToRoot();
  return CardService.newActionResponseBuilder().setStateChanged(true).setNavigation(refreshNav).build();

Simply, what I want is once the user clicks on Refresh button, the card refreshes/updates itself to make the call again and get the new data.

Mohammed AlBanna
  • 2,350
  • 22
  • 25

5 Answers5

6

The only way I've found to do this is to always use a single card for the root. In the main function (named in the appscript.json onTriggerFunction), return only a single card, not an array of cards. You can then use popToRoot().updateCard(...) and it works.

Glen Little
  • 6,951
  • 4
  • 46
  • 68
  • Is `udpateCard(...)` a custom method ? If yes what should it return ? A card ? – hhsb May 30 '18 at 06:26
  • 1
    Here's how I've seen it used. First, make a Card, then `return CardService.newActionResponseBuilder().setNavigation(CardService.newNavigation().popToRoot().updateCard(card)).build();` Nice and simple! :( – Glen Little Jun 04 '18 at 02:26
  • Cool. I'll give it a try. – hhsb Jun 04 '18 at 02:27
  • Works! This does not seem to be a refresh. This seem to destroy the card and recreate it. However, this is a nice workaround and it is working :) . – hhsb Jun 05 '18 at 05:14
  • 1
    This is what I've done! Thanks for your comment. Marked as the answer :) – Mohammed AlBanna Aug 24 '18 at 00:48
  • What if I have two triggers, how can I update the card generated from the first trigger. – Bruce Lee Mar 08 '19 at 07:45
5

I struggled with this for over a day, improving on Glen Little's answer so that its a bit more clear.

I have my root card to be refreshed defined in a funciton called: onHomepage.

I update the appscript.json manifest to set the homepageTrigger and onTriggerFunction to return the function that builds my root card.

"gmail": {
      "homepageTrigger": {
          "enabled": true,
          "runFunction":"onHomepage"
        },
      "contextualTriggers":[
        {
          "unconditional":{},
          "onTriggerFunction": "onHomepage"
        }
      ]  
      }

Then it is as simple as building a gotoRoot nav button function that will always refresh the root page.

  function gotoRootCard() {
    var nav = CardService.newNavigation()
    .popToRoot()
    .updateCard(onHomepage());
    
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();        
  }
discoStew
  • 225
  • 2
  • 8
2

As far as gmail addons are considered, cards are not refreshed but updated with new cards. And it is pretty simple.

//lets assume you need a form to be updated
function updateProfile() {
    //ajax calls
    //...

    //recreate the card again.
    var card = CardService.newCardBuilder();
    //fill it with widgets
    //....

    //replace the current outdated card with the newly created card.
    return CardService.newNavigation().updateCard(card.build());
}
hhsb
  • 560
  • 3
  • 23
1

A bad hack that works for my Gmail add-on:

return CardService.newActionResponseBuilder()
  .setStateChanged(true) // this doesn't seem to do much.  Wish it would reload the add-on
  .setNotification(CardService.newNotification()
                   .setText('Created calendar event')
  )
  // HACK!  Open a URL which closes itself in order to activate the RELOAD_ADD_ON side-effect
  .setOpenLink(CardService.newOpenLink() 
               .setUrl("https://some_site.com/close_yoself.html")
               .setOnClose(CardService.OnClose.RELOAD_ADD_ON))
  .build();

The contents of close_yoself.html is just:

<!DOCTYPE html>
<html><body onload="self.close()"></body></html>

So, it looks like Google has considered and solved this issue for an ActionResponse which uses OpenLink, but not for one using Navigation or Notification. The hack above is definitely not great as it briefly opens and closes a browser window, but at least it refreshes the add-on without the user having to do so manually.

blabes
  • 11
  • 2
0

Here is a full working example of a card updating after a button click with a random number showing the update:

Example App Screenshot

appscript.json

{
  "timeZone": "Etc/GMT",
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.addons.execute"
  ],
  "gmail": {
    "name": "Demo Card Refresh",
    "logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
    "homepageTrigger": {
      "runFunction": "myFunction"
    }
  },
  "runtimeVersion": "V8"
}

Code.gs

function myFunction(e) {
    return myCard().build();
}

function myCard() {
  return CardService
    .newCardBuilder()
    .setHeader(
      CardService.newCardHeader().setTitle('Refresh ID: ' + Math.random(9999999999))
    )
    .addSection(
      CardService.newCardSection()
        .setHeader('Section')
        .addWidget(
          CardService.newTextButton()
            .setText('Refresh')
            .setOnClickAction(
                CardService.newAction()
                  .setFunctionName('myAction')
            )
        )
    )
}

function myAction() {
  return CardService.newNavigation().updateCard(myCard().build());
}
eli geske
  • 596
  • 5
  • 7