Handling Direct Update with navigation to native page in IBM Mobile first - javascript

I am developing a project, in which i need to call a native page in wlCommonInit()
function wlCommonInit(){
WL.NativePage.show(nativePageClassName, backFromNativePage, params);
}
I want my project to receive the direct update with persession mode. So to connect with the Mobile First Server, I have called WL.Client.connect()
function wlCommonInit(){
busyind = new WL.BusyIndicator;
busyind.show();
WL.Client.connect({onSuccess: connectSuccess, onFailure: connectFail});
WL.NativePage.show(nativePageClassName, backFromNativePage, params);
}
More over I want to handle the direct update so I have added the required code.
wl_directUpdateChallengeHandler.handleDirectUpdate = function(directUpdateData,
directUpdateContext) {
// custom WL.SimpleDialog for Direct Update
var customDialogTitle = 'Custom Title Text';
var customDialogMessage = 'Custom Message Text';
var customButtonText1 = 'Update Application';
var customButtonText2 = 'Not Now';
WL.SimpleDialog.show(customDialogTitle, customDialogMessage, [{
text: customButtonText1,
handler: function() {
directUpdateContext.start(directUpdateCustomListener);
}
}, {
text: customButtonText2,
handler: function() {
wl_directUpdateChallengeHandler.submitFailure();
}
}]);
};
var directUpdateCustomListener = {
onStart: function(totalSize) {},
onProgress: function(status, totalSize, completeSize) {},
onFinish: function(status) {
WL.SimpleDialog.show('New Update Available', 'Press reload button to update to new version', [{
text: WL.ClientMessages.reload,
handler: WL.Client.reloadApp
}]);
}
};
Here the problem is, the application is navigating to the native page
before it can go to the direct update handler function when the direct
update is available.
Is there any way to resolve it?

I think what you should do instead if use the API [WL.Client.checkForDirectUpdate.
This way you will have the ability to first check for direct update - handle it if there is an update and then execute the function for opening the native page.
The code that is running is async, so you can't control it if you're not following the above suggestion.

Related

How to disable toast message for Newsletter module on Odoo (v14) website

For the website I develop, I use Newsletter module to create a mailing list. It's quite enough for basic needs. When you insert an e-mail and click to subscribe button, it shows (replace) "Thanks" message and hide the "Subscribe" button. It also shows a toast message: "Thanks for subscribing!" on the top right side of the page.
I don't want to show toast messages for newsletter subscriptions. Unfortunately, there is no option to enable/disable it.
If I disable/remove that part below from website_mass_mailing.js file it doesn't show the toast message.
self.displayNotification({
type: toastType,
title: toastType === 'success' ? _t('Success') : _t('Error'),
message: result.toast_content,
sticky: true,
});
I don't want to touch this file (website_mass_mailing.js) but instead, inherit it and remove that part but I couldn't succeed. Any suggestion on how to do it?
You should create a new module which depends on website_mass_mailing and extends mass_mailing.website_integration via a dedicated javascript module.
For example:
odoo.define('my_module.mass_mailing_website_integration', function (require) {
var website_integration = require('mass_mailing.website_integration');
website_integration.extend({
// Your Logic Here
});
}
Find mass_mailing method who's calling displayNotification and override it.
Unfortunately i see no alternative to copy-pasting it entirely from source and then removing desired behaviours.
Do not forget to include your javascript in web_assets template.
After suggestions of #icra I've tried to figure it out and here is the code that worked for me.
Thanks to Cybrosys Techno Solutions Pvt.Ltd as well to achieve the solution.
Here is the code:
odoo.define('your_module.name', function (require){
var publicWidget = require('web.public.widget');
var _t = core._t;
publicWidget.registry.subscribe.include({
_onSubscribeClick: async function () {
var self = this;
var $email = this.$(".js_subscribe_email:visible");
if ($email.length && !$email.val().match(/.+#.+/)) {
this.$target.addClass('o_has_error').find('.form-control').addClass('is-invalid');
return false;
}
this.$target.removeClass('o_has_error').find('.form-control').removeClass('is-invalid');
let tokenObj = null;
if (this._recaptcha) {
tokenObj = await this._recaptcha.getToken('website_mass_mailing_subscribe');
if (tokenObj.error) {
self.displayNotification({
type: 'danger',
title: _t("Error"),
message: tokenObj.error,
sticky: true,
});
return false;
}
}
const params = {
'list_id': this.$target.data('list-id'),
'email': $email.length ? $email.val() : false,
};
if (this._recaptcha) {
params['recaptcha_token_response'] = tokenObj.token;
}
this._rpc({
route: '/website_mass_mailing/subscribe',
params: params,
}).then(function (result) {
let toastType = result.toast_type;
if (toastType === 'success') {
self.$(".js_subscribe_btn").addClass('d-none');
self.$(".js_subscribed_btn").removeClass('d-none');
self.$('input.js_subscribe_email').prop('disabled', !!result);
if (self.$popup.length) {
self.$popup.modal('hide');
}
}
// make the changes you need accordingly or comment out the below code.
self.displayNotification({
type: toastType,
title: toastType === 'success' ? _t('Success') : _t('Error'),
message: result.toast_content,
sticky: true,
});
});
},
})
})

ng-grid not binding with latest data after async XMLHttpRequest's response

I have a AngularJS web application, I'm trying to upload a file to a server and while the upload is complete, I have to update ng-grid with the last uploaded file's entry. The following is my grid html,
<div class="gridholder" data-ng-grid="viewmodel.gridOptions">
</div>
The following is my controller logic.
vm.gridOptions = {
data: 'gridData',
enableColumnResize: true,
enablePaging: true,
columnDefs: [
{ field: 'FileName', displayName: 'File Name', width: 250 }
{ field: 'UploadedDate', displayName: 'Uploaded Date'}
],
multiSelect: false,
enableSorting: true,
showFooter: true,
};
The requirement is that I show the progress of file upload and the entire application to be responsive when upload is in progress, I have achieved this but my ng-grid not is updating in a particular scenario.
If I remain in the same page until the file is uploaded and the response comes, the grid is refreshing but when I move to another page of my application and come back to the file upload page, and the response comes after, my grid is not getting refreshed.
This is my file upload js code,
var data = new FormData();
data.append('file', file);
var xhrRequest = Factory.uploadFileRequest('UploadFile');
xhrRequest.upload.addEventListener("progress", progressHandler, false);
xhrRequest.onreadystatechange = function (e) {
};
xhrRequest.onload = function (e) {
if (JSON.parse(e.currentTarget.responseText).Success == true) {
$timeout(function () {
$scope.LoadGrid();
//showing success message here
}, 2000);
}
else
{
//showing error message here
}
};
xhrRequest.onerror = function (e) {
//showing error message here
};
xhrRequest.send(data);
$scope.LoadGrid = function () {
Factory.callGet("Files").then(function (d) {
$scope.gridData = d.data;
}
$scope.totalItems = $scope.gridData.length;
}, function error(err) {
//Error Message
});
}
gridData is my data-ng-grid value. I'm calling my LoadGrid method inside a $timeout already but still the grid is not refreshing with latest data. Any help would be much appreciated.
Possible Problem
You implemented upload logic inside the controller. When you switch to another view, angularjs destroys your controller and therefore no one listens on file upload response.
Possible solution:
1) Use a service (or Factory) kind of singleton to manage upload process there.
For example MyService.upload(data).then(function (response) {/**/});
2) By default MyService.upload(data) returns a promise on a regular basis but also stores the result inside the Service, for example, upload_results:
app.service('MyService',['$q',function ($q) {
var self = this;
var upload_results = [];
self.upload = function (_data) {
return // <YOUR_PROMISE>
.then(function (response) {
upload_results.push({
id: new Date().getTime(),
data: response.data
})
}
, function (error) {
console.error(error);
return $q.reject(error);
});
};
self.getResults() = function(){
return upload_results;
}
self.resetResults() = function(){
upload_results = [];
}
}
When you initialize the controller on start or go back to the previous controller, you ask the service if it has something for you:
var results = MyService.getResults();
if(results.length > 0){
$scope.gridData = results[0].data; // or use timestamp to manage it
MyService.resetResults();
}
Hope it will give you some insight,

Why doesn't Vuejs register enabling or disabling my button?

I'm building a simple Vuejs website in which you can write notes about meetings. Upon loading it takes the meeting notes from the server and displays them. When the user then writes something he can click the "Save" button, which saves the text to the server. When the notes are saved to the server the Save-button needs to be disabled and display a text saying "Saved". When the user then starts writing text again it should enable the button again and display "Save" again. This is a pretty basic functionality I would say, but I'm having trouble with it.
Here's my textarea and my save button:
<textarea v-model="selectedMeeting.content" ref="meetingContent"></textarea>
<button v-on:click="saveMeeting" v-bind:disabled="meetingSaved">
{{ saveMeetingButton.saveText }}
</button>
In my Vue app I first initiate my data:
data: {
selectedMeeting: {},
meetings: [],
meetingSaved: true,
saveMeetingButton: {saveText: 'Save Meeting', savedText: 'Saved', disabled: true},
},
Upon creation I get the meeting notes from the server:
created() {
axios.get('/ajax/meetings')
.then(response => {
this.meetings = response.data;
this.selectedMeeting = this.meetings[0];
this.meetingSaved = true;
});
},
I've got a method to save the notes:
methods: {
saveMeeting: function () {
axios.post('/ajax/meetings/' + this.selectedMeeting.id, this.selectedMeeting)
.then(function (response) {
this.selectedMeeting = response.data;
console.log('Now setting meetingSaved to true');
this.meetingSaved = true;
console.log('Done setting meetingSaved to true');
});
},
},
And I've got a watcher in case something changes to the text which saves the text immediately (this saves with every letter I type, which I of course need to change, but this is just to get started.
watch: {
'selectedMeeting.content': function () {
this.meetingSaved = false;
console.log('Changed meeting ', new Date());
this.saveMeeting();
}
},
If I now type a letter I get this in the logs:
Changed meeting Tue Dec 04 2018 19:14:43 GMT+0100
Now setting meetingSaved to true
Done setting meetingSaved to true
The logs are as expected, but the button itself is never disabled. If I remove the watcher the button is always disabled however. Even though the watcher first sets this.meetingSaved to false, and then this.saveMeeting() sets it to true, adding the watcher somehow never disables the button.
What am I doing wrong here?
Edit
Here's a paste of the whole page: https://pastebin.com/x4VZvbr5
You've got a few things going on that could use some changing around.
Firstly the data attribute should be a function that returns an object:
data() {
return {
selectedMeeting: {
content: null
},
meetings: [],
meetingSaved: true,
saveMeetingButton: {
saveText: 'Save Meeting',
savedText: 'Saved',
disabled: true
},
};
}
This is so Vue can properly bind the properties to each instance.
Also, the content property of the selectedMeeting didn't exist on the initial render so Vue has not added the proper "wrappers" on the property to let things know it updated.
As an aside, this can be done with Vue.set
Next, I would suggest you use async/await for your promises as it makes it easier to follow.
async created() {
const response = await axios.get('/ajax/meetings');
this.meetings = response.data;
this.selectedMeeting = this.meetings[0];
this.meetingSaved = true;
},
For your method I would also write as async/await. You can also use Vue modifiers like once on click to only call the api if there is no previous request (think a fast double-click).
methods: {
async saveMeeting () {
const response = await axios.post('/ajax/meetings/' + this.selectedMeeting.id, this.selectedMeeting);
this.selectedMeeting = response.data;
console.log('Now setting meetingSaved to true');
this.meetingSaved = true;
console.log('Done setting meetingSaved to true');
},
},
The rest of the code looks okay.
To summarize the main problem is that you didn't return an object in the data function and it didn't bind the properties reactively.
Going forward you are going to want to debounce the text input firing the api call and also throttle the calls.
this.meetingSaved = true;
this is referencing axios object. Make a reference to vue object outside your call and than use it. Same happens when you use jQuery.ajax().
created() {
var vm = this;
axios.get('/ajax/meetings')
.then(response => {
vm.meetings = response.data;
vm.selectedMeeting = vm.meetings[0];
vm.meetingSaved = true;
});
},

Handle Push notification in Nativescript

I am working on application in Nativescript which implements push notification. Lets say server sends push notification and based on action mentioned in payload of notification i will have to redirect in application. This redirection should be performed if user taps on notification from drawer and application is in background. Other case when application should not redirect if its in foreground. I have managed a flag for that as follow
app.js
application.on(application.launchEvent, function (args) {
appSettings.setBoolean('AppForground', true);
});
application.on(application.suspendEvent, function (args) {
appSettings.setBoolean('AppForground', false);
});
application.on(application.resumeEvent, function (args) {
appSettings.setBoolean('AppForground', true);
});
application.on(application.exitEvent, function (args) {
appSettings.setBoolean('AppForground', false);
});
application.on(application.lowMemoryEvent, function (args) {
appSettings.setBoolean('AppForground', false);
});
application.on(application.uncaughtErrorEvent, function (args) {
appSettings.setBoolean('AppForground', false);
});
And on Push notification listener
var settings = {
// Android settings
senderID: '1234567890', // Android: Required setting with the sender/project number
notificationCallbackAndroid: function(data, pushNotificationObject) { // Android: Callback to invoke when a new push is received.
var payload = JSON.parse(JSON.parse(pushNotificationObject).data);
if (appSettings.getBoolean('AppForground') == false){
switch (payload.action) {
case "APPOINTMENT_DETAIL":
frame.topmost().navigate({
moduleName: views.appointmentDetails,
context: {
id: payload.id
}
});
break;
case "MESSAGE":
frame.topmost().navigate({
moduleName: views.appointmentDetails,
context: {
id: payload.id,
from: "messages"
}
});
break;
case "REFERENCES":
frame.topmost().navigate({
moduleName: views.clientDetails,
context: {
id: payload.id,
name: ""
}
});
break;
default:
}
}
},
// iOS settings
badge: true, // Enable setting badge through Push Notification
sound: true, // Enable playing a sound
alert: true, // Enable creating a alert
// Callback to invoke, when a push is received on iOS
notificationCallbackIOS: function(message) {
alert(JSON.stringify(message));
}
};
pushPlugin.register(settings,
// Success callback
function(token) {
// if we're on android device we have the onMessageReceived function to subscribe
// for push notifications
if(pushPlugin.onMessageReceived) {
pushPlugin.onMessageReceived(settings.notificationCallbackAndroid);
}
},
// Error Callback
function(error) {
alert(error);
}
);
Now the problem, is that if application is in killed state and notification arrives. Then it sets flag to true as application is launched which it should not. So due to that redirection is not performed and in other cases when application is in foreground state then also its navigating through pages (which should not be) on receiving notification.
I doubt about flag management is causing the problem but not sure. Would you please guide me if anything is wrong with what i did ?
UPDATE
I am using push-plugin.
Thanks.
I use this for notifications
https://github.com/EddyVerbruggen/nativescript-plugin-firebase
This plugin use FCM, it adds to datas received from notifications foreground parameter so from payload you can determine if app was background(foreground==false, app is not active or was started after notification arrived) or foreground(foreground==true, app is open and active), but you need to some changes to code as they are different plugins
You can use pusher-nativescript npm module.
import { Pusher } from 'pusher-nativescript';
/*Observation using the above.
- Project gets build successfully.
- on run -> ERROR TypeError: pusher_nativescript__WEBPACK_IMPORTED_MODULE_6__.Pusher is not a constructor
- Use: import * as Pusher from 'pusher-nativescript';
- Make sure to install nativescript-websocket with this package.
*/
var pusher = new Pusher('Your_app_key', { cluster: 'your_cluster_name' });
var channel = pusher.subscribe('my-channel');
channel.bind('my-event', function(data) {
alert(JSON.stringify(data));
});

Providing a template for $confirm dialogs

I'm using angular-confirm to display confirmation messages in a lot of places in my app. For example:
$confirm({
text: 'content',
title: 'title text',
ok: 'Yes',
cancel: 'No'})
.then(function() {
doSomething();
});
I want to globally change the layout in which these dialogs appear in the app. I know that angular-confirm allows you to make a global change like this:
$confirmModalDefaults.templateUrl = 'path/to/your/template';
However, this also overrides the template for all $modal.open() calls, which is not what I want.
I think that the way to do this is by a using a provider to append a template url to every $confirm call in the app but I'm not sure exactly how to do that.
How do I create a provider for $confirm and append a templateUrl parameter to every call?
Figured it out:
function config($provide) {
$provide.decorator('$confirm', confirmProvider);
}
/* #ngInject */
function confirmProvider($delegate) {
return function (data, settings) {
if (settings && !settings.template && !settings.templateUrl) {
settings.template = '<div>my confirm content</div>';
} else {
settings = {
template: '<div>my confirm content</div>'
}
}
return $delegate(data, settings);
};
So now, when $confirm() gets called with data and/or settings, it will apply my custom template unless it's been specified by the caller

Categories

Resources