Every time I am trying to use
AndroidIAB.androidIAB.consumeItem("coin_stack_2");
I get : failed:4040/The item 'coin_stack_2' was not previously loaded.
==> it's happening for all of my products that I'm trying to sell as a consumeItem
I have loaded the the function below at the start of the application :
AndroidIAB.androidIAB.loadPlayerInventory();
What am I doing wrong?
AndroidIAB.androidIAB.loadPlayerInventory is an asynchronous call, which means that you have to wait for it to finish. The full code should look similar to this:
// listen for inventory events
AndroidIAB.androidIAB.addEventListener(AndroidBillingEvent.INVENTORY_LOADED, onInventoryLoaded);
AndroidIAB.androidIAB.addEventListener(AndroidBillingErrorEvent.LOAD_INVENTORY_FAILED, onInventoryFailed);
function onInventoryLoaded(e:AndroidBillingEvent):void
{
for each(var purchase:AndroidPurchase in e.purchases)
{
trace("You own the item:"+purchase.itemId);
// this is where you'd update the state of your app to reflect ownership of the item
}
}
function onInventoryFailed(e:AndroidBillingErrorEvent):void
{
trace("Something went wrong loading inventory: "+e.text);
}
// load the player's current inventory
AndroidIAB.androidIAB.loadPlayerInventory();
In order to use consumeItem the user should purchase the item first and only than it is possible to use consumeItem
Related
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.
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.
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);
}
I want an observer to track changes in Quote like when removing products, updating, adding product from frontend and backend as well. This observer should run after magento native code.
There are many events like below which are executed during quote but I am not sure which observer will be fit for my requirement--
sales_quote_remove_item
sales_quote_add_item
sales_quote_product_add_after
sales_quote_item_collection_products_after_load
checkout_cart_add_product_complete
checkout_cart_save_before
checkout_cart_save_after
etc...
You may not be able to achieve this by using a single event , but instead you could write a single observer and call this observer for multiple events..
<sales_quote_product_add_after>
<observers>
<sales_quote_product_add>
<type>singleton</type>
<class>my_module/observer</class>
<method>myfunction</method>
</sales_quote_product_add>
</observers>
</sales_quote_product_add_after>
<checkout_cart_add_product_complete>
<observers>
<checkout_cart_add_product>
<type>singleton</type>
<class>my_module/observer</class>
<method>myfunction</method>
</checkout_cart_add_product>
</observers>
</checkout_cart_add_product_complete>
Events you can see
sales_quote_remove_item
sales_quote_item_qty_set_after
sales_quote_product_add_after
https://www.nicksays.co.uk/magento-events-cheat-sheet-1-9/
You can find events on your own
Modify Mage.php in app folder
public static function dispatchEvent($name, array $data = array())
{
Varien_Profiler::start('DISPATCH EVENT:'.$name);
$result = self::app()->dispatchEvent($name, $data);
Varien_Profiler::stop('DISPATCH EVENT:'.$name);
//log your event
Mage::log("Event name is $name",null,'event.log');
return $result;
}
Do your action, like add item to cart, remove / update quantity and then check event.log in var/log
I am not sure what the proper heading / title for this question should be. I am new to WinJS and am coming from a .NET webform and winclient background.
Here is my scenario. I have a navigation WinJS application. My structure is:
default.html
(navigation controller)
(settings flyout)
pages/Home.html
pages/Page2.html
So at the top of the default.js file, it sets the following variables:
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
It seems like I cannot use these variables anywhere inside my settings flyout or any of my pages:ready functions. They are only scoped to the default.js?
In the same regard, are there resources on the interwebs (links) that show how to properly share variables, events, and data between each of my "pages"?
The scenario that I immediately need to overcome is settings. In my settings flyout, I read and allow the user to optionally set the following application setting:
var applicationData = Windows.Storage.ApplicationData.current;
var localSettings = applicationData.localSettings;
localSettings.values["appLocation"] = {string set by the user};
I want to respond to that event in either my default.js file or even one of my navigation pages but I don't know where to "listen". My gut is to listen for the afterhide event but how do I scope that back to the page where I want to listen from?
Bryan. codefoster here. If you move the lines you mentioned...
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
...up and out of the immediate function, they'll be in global scope and you'll have access to them everywhere. That's one of the first things I do in my apps. You'll hear warnings about using global scope, but what people are trying to avoid is the pattern of dropping everything in global scope. As long as you control what you put in there, you're fine.
So put them before the beginning of the immediate function on default.js...
//stuff here is scoped globally
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
(function () {
//stuff here is scoped to this file only
})();
If you are saving some data and only need it in memory, you can just hang it off the app variable instead of saving it into local storage. That will make it available to the whole app.
//on Page2.js
app.myCustomVariable = "some value";
//on Page3.js
if(app.myCustomVariable == "some value") ...
Regarding your immediate need:
like mentioned in the other answer, you can use datachanged event.
Regards sharing variables:
If there are variables that you would like to keep global to the application, they can be placed outside the anonymous function like mentioned in the Jeremy answer. Typically, that is done in default.js. Need to ensure that scripts using the global variables are placed after the script defining the global variable - in default.html. Typically - such variable will point to singleton class. For example: I use it in one of my apps to store authclient/serviceclient for the backend service for the app. That way - the view models of the multiple pages need not create instance of the object or reference it under WinJS namespace.
WinJS has also concept of Namespace which lets you organize your functions and classes. Example:
WinJS.Namespace.define('Utils.Http',
{
stringifyParameters: function stringifyParameters(parameters)
{
var result = '';
for (var parameterName in parameters)
{
result += encodeURIComponent(parameterName) + '=' + encodeURIComponent(parameters[parameterName]) + '&';
}
if (result.length > 0)
{
result = result.substr(0, result.length - 1);
}
return result;
},
}
When navigating to a page using WinJS.Navigation.navigate, second argument initialState is available as options parameter to the ready event handler for the page. This would be recommended way to pass arguments to the page unless this it is application data or session state. Application data/session state needs to be handled separately and needs a separate discussion on its own. Application navigation history is persisted by the winjs library; it ensures that if the app is launched again after suspension - options will be passed again to the page when navigated. It is good to keep the properties in options object as simple primitive types.
Regards events:
Typically, apps consume events from winjs library. That can be done by registering the event handler using addEventListener or setting event properties like onclick etc. on the element. Event handlers are typically registered in the ready event handler for the page.
If you are writing your own custom control or sometimes in your view model, you may have to expose custom events. Winjs.UI.DOMEventMixin, WinJS.Utilities.createEventProperties can be mixed with your class using WinJS.Class.mix. Example:
WinJS.Class.mix(MyViewModel,
WinJS.Utilities.createEventProperties('customEvent'),
WinJS.UI.DOMEventMixin);
Most often used is binding to make your view model - observable. Refer the respective samples and api documentation for details. Example:
WinJS.Class.mix(MyViewModel,
WinJS.Binding.mixin,
WinJS.Binding.expandProperties({ items: '' }));
Here is what I ended up doing which is kinda of a combination of all the answers given:
Created a ViewModel.Settings.js file:
(function () {
"use strict";
WinJS.Namespace.define("ViewModel", {
Setting: WinJS.Binding.as({
Name: '',
Value: ''
}),
SettingsList: new WinJS.Binding.List(),
});
})();
Added that file to my default.html (navigation container page)
<script src="/js/VMs/ViewModel.Settings.js"></script>
Add the following to set the defaults and start 'listening' for changes
//add some fake settings (defaults on app load)
ViewModel.SettingsList.push({
Name: "favorite-color",
Value: "red"
});
// listen for events
var vm = ViewModel.SettingsList;
vm.oniteminserted = function (e) {
console.log("item added");
}
vm.onitemmutated = function (e) {
console.log("item mutated");
}
vm.onitemchanged = function (e) {
console.log("item changed");
}
vm.onitemremoved = function (e) {
console.log("item removed");
}
Then, within my application (pages) or my settings page, I can cause the settings events to be fired:
// thie fires the oniteminserted
ViewModel.SettingsList.push({
Name: "favorite-sport",
Value: "Baseball"
});
// this fires the itemmutated event
ViewModel.SettingsList.getAt(0).Value = "yellow";
ViewModel.SettingsList.notifyMutated(0);
// this fires the itemchanged event
ViewModel.SettingsList.setAt(0, {
Name: "favorite-color",
Value: "blue"
});
// this fires the itemremoved event
ViewModel.SettingsList.pop(); // removes the last item
When you change data that needs to be updated in real time, call applicationData.signalDataChanged(). Then in the places that care about getting change notifications, listen to the datachanged on the applicationData object. This is also the event that is raised when roaming settings are synchronized between computers.
I've found that many times, an instant notification (raised event) is unnecessary, though. I just query the setting again when the value is needed (in ready for example).