Show notification on card update Google Script - google-apps-script

How do I make a notification appear on card load?
In the docs I found this: https://developers.google.com/apps-script/reference/card-service/notification?hl=en
But I'm not sure how to use it correctly in my situation.
Wanted result
When the user clicks a button a card is updated via CardService.newNavigation().updateCard(card) and once the card is updated a notification is shown.
edit
The error shown happens when i try to return a card that fetches some data and the notification as an array [card, notification]. When I return only one of the above it works as expected.
The error is shown bellow.
The value returned from Apps Script has a type that cannot be used by the add-ons platform. Also make sure to call build on any builder before returning it. Value:values {
proto_value {
type_url: "type.googleapis.com/caribou.api.proto.addons.templates.publicapi.ContextualAddOnMarkup.Card"
value: "\n\017\n\rVyber projekt\022\375\001\022\372\001B\367\001\n\020dropdown_project\020\003\032\017\n\aVyberte\020\001\032\002-1\032\023\n\005Await\020\000\032\b105966.0\032!\n\024\342\233\265\357\270\217 Freelo addons\020\000\032\a31512.0\032\031\n\vgmail addon\020\000\032\b104757.0\032\020\n\002ll\020\000\032\b105967.0\032\022\n\004test\020\000\032\b104596.0\032\023\n\005test2\020\000\032\b104741.0\032\022\n\004null\020\000\032\b105969.0\032\022\n\004null\020\000\032\b105968.0\"\021\n\rshowToDoLists \000*\aProjekt\022G\022+\")\n\'\n\021Vytvo\305\231it Projekt\022\022\022\020\n\016newProjectForm\022\030\"\026\n\024\n\006Logout\022\n\022\b\n\006logOut\"\fprojectsCard"
}
}
values {
proto_value {
type_url: "type.googleapis.com/caribou.api.proto.addons.templates.publicapi.SubmitFormResponseMarkup"
value: "\022\027\022\025Projekt byl vytvo\305\231en"
}
}

Related

Inject Folder ID from selection in GDrive Addon and suppress cardflip

I am trying to get the scope of gdrive to create a file from some form values with my addon.
To achieve this, I added a handler to the manifest and implemented the corresponding function.
"onItemsSelectedTrigger": {
"runFunction": "onDriveItemsSelected"
}
In the function I can use the following as ID of the first selected item. (I currently check Mimetype to keep it simple...)
createFolderID = e['drive']['selectedItems'][0].id;
Now I have two problems:
1.
When clicking a folder within the gdrive - the event function seems to await a built card as return value. I just want to use the selected folder (or ideally the folder where I am currently "in", via getparent?), without needing an additional card. If I return null, the card is created anyway above my addon card and shown with "No content shown for this message".
Is there away to avoid this?
2.
I need to inject the folder ID of the selected folder into my form (which I created with CardService at start of the addon). Declaring a "global" var does not seem to work,I assume that the cloud context will not preserve the variable value. The value is needed as parameter to a created document of my addon.
Can anyone point me into the right direction to store this folder Id until the user runs the addons action?
EDIT:
/**
* Get the selected folder to create the offer in
*/
function onDriveItemsSelected(e) {
// We check only the first selection
if (e['drive']['selectedItems'][0].mimeType == "application/vnd.google-apps.folder")
{
createFolderTitle = e['drive']['selectedItems'][0].title;
createFolderID = e['drive']['selectedItems'][0].id;
Logger.log(e['drive']['selectedItems'][0].title + " selected. ID: " + createFolderID)
PropertiesService.getUserProperties().setProperty('selectedFolderId', createFolderID);
}
}
The following snippet is contained in the manifest to link the function to selection events.
"drive": {
"homepageTrigger": {
"runFunction": "initForm"
},
"onItemsSelectedTrigger": {
"runFunction": "onDriveItemsSelected"
}
}
I use this for catching the selection event. But the card on the right side is then overlayed with an empty card with the message I already mentioned.
You can always pass a default parameter to a function.
Example:
function createForm(selectedFolderId = PropertiesService.getUserProperties().getProperty("selectedFolderId")) {
let form;
// Create the form
return form;
}
References:
PropertiesService
Update:
When you use OnItemsSelectedTrigger you must return an array of Card objects.

FullCalendar-Scheduler Google Calendars ResourceIDs Query

Reference: FullCalendar 3.9.0, FullCalendar-Scheduler 1.9.4
Can anyone confirm whether or not it is possible to group Google calendar events by resource? Adding a resourceId parameter to a calendar source as follows:
var myCalSrc = {
id: 1,
googleCalendarId: '<myCalSrcURL>',
color: '<myCalSrcColor>',
className: '<myCalSrc-events>'
};
results in a blank display. The following note in the FullCalendar-Scheduler gcal.html file located in the demos directory states:
/*
NOTE: unfortunately, Scheduler doesn't know how to associated events from
Google Calendar with resources, so if you specify a resource list,
nothing will show up :( Working on some solutions.
*/
However, the following threads appear to suggest there may have been a fix for this:
GitHub - Add ResourceId Parameter to gcal.js (fix supplied)
GitHub - Specify resourceId in Event Source settings
However, checking the gcal.js file reveals the fix has not been added to that file.
Is it possible to manually assign a resourceId to each of the Google Calendar feeds in order to replicate the Resources and Timeline view indicated by the FullCalendar Timeline View documentation?
Any guidance would be greatly appreciated.
As per the issue in your second GitHub link (which your first one was merged with), https://github.com/fullcalendar/fullcalendar-scheduler/issues/124, the fix you mentioned is still awaiting testing (as of 11 Mar 2018). So if you're patient it will likely be added to a future release, assuming it passes the tests. In the meantime, here is a potential workaround:
In fullCalendar it's possible to define a separate eventDataTransform for every event source.
Therefore I think you should be able to use this to set a resource ID for each event depending on the Google Calendar it came from:
eventSources: [
{
googleCalendarId: 'abc#group.calendar.google.com',
color: 'blue',
eventDataTransform: function(event) {
event.resourceId = 1;
return event;
}
},
{
googleCalendarId: 'def#group.calendar.google.com',
color: 'green',
eventDataTransform: function(event) {
event.resourceId = 2;
return event;
}
},
{
googleCalendarId: 'ghi#group.calendar.google.com',
color: 'red' ,
eventDataTransform: function(event) {
event.resourceId = 3;
return event;
}
}
]
I'm not able to test this right now but it looks like it should work. Hopefully this will take place before it's rendered on the calendar and needs to belong to a resource.

How can I relaunch/update/refresh the card again using Apps Script

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.
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.
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();
}
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());
}
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.

Only one type of action can execute?

I going to show my problem with an example:
I have a button. When clicked, it creates a mail draft based on the TextInputFields in the add-on.
I have a validate function, which can say if the fields filled right or not.
If I want to notify the user somehow about the wrong fields, I have to create a notify or rebuild the card with error information. These actions can be returned in a normal Action, but not with a composeAction (because composeAction has to return with builded draft), so I have to register a composeAction and a simple action to the button.
When I clicked this kind of button, only one of the action execute and the other do nothing.
Some code about how I tried to implement:
section.addWidget(CardService.newTextButton()
.setText('Validate and Create')
.setComposeAction(CardService.newAction().setFunction('doIt'), CardService.ComposedEmailType.STANDALONE_DRAFT)
.setOnClickAction(CardService.newAction().setFunction('notify')));
ActionFunctions:
function doIt(event){
validate the event['formInput'] object;
if(valid the formInput)
create andr return the draft;
else
return null;
}
function notify(event){
validate the event['formInput'] object;
if(valid the formInput)
return null;
else
return notify or rebuilded card with error info;
}
Mostly the simple action run, and the compose do nothing. If I place Logger.log() functions in the callback function, only one appears on api log.
Anyone have tried before validate and create draft at the same click?
How about this:
var action=CardService.newAction().setFunctionName('myFunction');
var validateCreateButton=CardService.newTextButton()
.setText('Validate & Create')
.setOnClickAction(action);
section.addWidget(validateCreateButton);
function myFunction(e) {
doit(e);
notify(e);
}

Dynamic embedded dashboard height in GoodData

I am trying to embed GoodData dashboard to an iframe in my application and it works well but each tab on that dashboard has different number of reports on it and I'd like to make the iframe height dynamic based on the actual dashboard content.
Is there a way how to do it? Does GoodData somehow propagate the space needed to render the dashboard?
Thank you.
In fact there is a postMessage() sent event called 'ui.frameinfo' which you could use to detect the dashboard tab height (when using dashboard.html). It is sent every time the tab changes its height.
The following listener should print out the iframe's internal height:
window.addEventListener('message', function(e) {
var message;
try {
message = JSON.parse(e.data);
} catch (e) {
// valid messages are JSON
message = {};
}
// drop other than GoodData events
if (!message.gdc) return;
if (message.gdc.name === 'ui.frameinfo') {
console.log('frame height:', message.gdc.data.height);
}
}
Note that this is not an official feature (yet) and potentially subject to change.