Vue continue on successful callback - javascript

I have a Vue app with Vuex dependency. Program logic is that message should be sent after successful Facebook share.
This is the method getting triggered on button click for sending the message:
onSubmit() {
if(this.shouldShowShareModal){
this.$store.dispatch('openModal', 'SHARE_MODAL');
return;
}
this.$store.dispatch('sendMessage', {
conversation_id: this.conversationId,
message : this.messageText,
message_type_id: 1
}).then(() => {
this.messageText = '';
this.scrollToBottom();
});
},
openModal simply sets the value of the given modal to true, thus v-if shows the modal which has the button which upon share method triggers a Facebook share:
share() {
var self = this;
FB.ui({
method: 'share',
href: 'https://developers.facebook.com/docs/'
}, function(){
self.$store.dispatch('sharedThroughFacebook');
self.$store.dispatch('closeModal', 'SHARE_MODAL');
});
}
Now the issue I have is how can I continue sending the message after Facebook callback is successful? Currently with sharedThroughFacebook I am simply setting the store sharing flag to true, but I am not sure about the best approach for sending the message only after a successful callback? If I push the data to modal, that seems like a dirty solution and that modal should not be aware of message state. On the other hand putting the conversation ID, message, type and text on Vuex store seems like an overhead since this component is the only one using the data.

You can compose your action like this
actions: {
async share ({ dispatch, commit }) {
await dispatch('sharedThroughFacebook') // wait for `sharedThroughFacebook` to finish
await dispatch('closeModal') // close the modal before sending the message
commit('sendMesseage')
},
async sharedThroughFacebook (context, payload) {
FB.ui({
method: 'share',
href: 'https://developers.facebook.com/docs/'
})
}
}

Related

How to setup like a function should be called only one time even after reload

I'm trying to make a Post request on component Mount. But if user reloads the page or states changes, then the function is called again as I'm useEffect and it sends the request again. But I want any better thing where the Post request should be made once and if even the page refreshes the shouldn't be called again if it has been called.
I'm using the Function base component. and make Post requests using redux.
const Main = () => {
// ....
// Here I'm forcing user to login if there's user is logged in then want to make a silent post request, But it sends request everytime on state change.
useEffect(() => {
getLocalStorage()
if (!userInfo) {
setModalShow(true)
}
if (userInfo) {
dispatch(postRequest())
setModalShow(false)
}
}, [userInfo])
return (
<div>Some JSX </div>
)
}
export default Main
So need your help to fix that issue. Can we use localStorage to store the information either the post request is already have been made or any other better idea?
Best way is to use localstorage, not sure if my placements of setting ang getting value from localstorage are on the right spot.
const Main = () => {
// ....
// Here I'm forcing user to login if there's user is logged in then want to make a silent post request, But it sends request everytime on state change.
useEffect(() => {
getLocalStorage()
// Check if the value of logged is true initiali will be false until the
// first request if made
if (!!localStorage.getItem('logged')) {
setModalShow(true)
}
if (userInfo) {
dispatch(postRequest())
setModalShow(false)
// set the value when the request is finished
localStorage.setItem('logged', true)
}
}, [userInfo])
return (
<div>Some JSX </div>
)
}
export default Main
There is a package named redux-persist that you can save the state, for example in localStorage. You can use this package, and send post request if there is not any data in state.
Using localStorage for that purpose is pretty useful, you can save the information on post request whether it was made or not.
For a basic setup;
this could be like that:
const postRequestStatus = localStorage.getItem('postRequestMade') ? JSON.parse(localStorage.getItem('postRequestMade')) : null
useEffect(() => {
getLocalStorage()
if (!userInfo) {
setModalShow(true)
}
if (userInfo) {
setModalShow(false)
if (!postRequestStatus) {
dispatch(postRequest())
console.log('Post Request Made')
localStorage.setItem('postRequestMade', true)
}
}
}, [userInfo, postRequestStatus])
Here's a catch. As far there is information in localStorage, of postRequestMade true . The request won't be made. So some point on the site you should set any logic to clear it out where it is necessary.
Secondly, What if the request was not successful if there was an error from the server. Then, you should also consider error handling as well. As you mentioned you are using redux and I'm sure there would be Axios as well try the functionality like that:
useEffect(() => {
getLocalStorage()
if (!userInfo) {
setModalShow(true)
}
if (userInfo) {
setModalShow(false)
if (!postRequestStatus) {
dispatch(postRequest())
// That block will take care if request was successful
// After a successful request postRequestMade should be set to true.
if (success) {
console.log('Successful Request')
localStorage.setItem('postRequestMade', true)
}
}
}
}, [userInfo, postRequestStatus, success])

Nuxt handle redirect after deletion without errors : beforeUpdate direction not working?

So I have this nuxt page /pages/:id.
In there, I do load the page content with:
content: function(){
return this.$store.state.pages.find(p => p.id === this.$route.params.id)
},
subcontent: function() {
return this.content.subcontent;
}
But I also have an action in this page to delete it. When the user clicks this button, I need to:
call the server and update the state with the result
redirect to the index: /pages
// 1
const serverCall = async () => {
const remainingPages = await mutateApi({
name: 'deletePage',
params: {id}
});
this.$store.dispatch('applications/updateState', remainingPages)
}
// 2
const redirect = () => {
this.$router.push({
path: '/pages'
});
}
Those two actions happen concurrently and I can't orchestrate those correctly:
I get an error TypeError: Cannot read property 'subcontent' of undefined, which means that the page properties are recalculated before the redirect actually happens.
I tried:
await server call then redirect
set a beforeUpdate() in the component hooks to handle redirect if this.content is empty.
delay of 0ms the server call and redirecting first
subcontent: function() {
if (!this.content.subcontent) return redirect();
return this.content.subcontent;
}
None of those worked. In all cases the current page components are recalculated first.
What worked is:
redirect();
setTimeout(() => {
serverCall();
}, 1000);
But it is obviously ugly.
Can anyone help on this?
As you hinted, using a timeout is not a good practice since you don't know how long it will take for the page to be destroyed, and thus you don't know which event will be executed first by the javascript event loop.
A good practice would be to dynamically register a 'destroyed' hook to your page, like so:
methods: {
deletePage() {
this.$once('hook:destroyed', serverCall)
redirect()
},
},
Note: you can also use the 'beforeDestroy' hook and it should work equally fine.
This is the sequence of events occurring:
serverCall() dispatches an update, modifying $store.state.pages.
content (which depends on $store.state.pages) recomputes, but $route.params.id is equal to the ID of the page just deleted, so Array.prototype.find() returns undefined.
subcontent (which depends on content) recomputes, and dereferences the undefined.
One solution is to check for the undefined before dereferencing:
export default {
computed: {
content() {...},
subcontent() {
return this.content?.subcontent
👆
// OR
return this.content && this.content.subcontent
}
}
}
demo

How to post a button in postMessageToChannel to the slack channel and call a function on click of button?

I want to send a button in bot.postMessageToChannel() and want to call a function(handleMessage) on Click of that button.
//start handler
bot.on('start', () => {
const params = {
icon_emoji: ':information_source:'
}
//here i want to post a button to a channel.
bot.postMessageToChannel(
'ABC', //channel name
'*Welcome to AB*...',
params
)
})
function i want to call,
handleMessage = (message) => {
console.log("hello")
}
I have tried the slack API.
Message Buttons with Node.js but it is from the slash commands.
interactive message button it is just giving me similar JSON data.
I wanted to confirm what you were looking for before I answered. I did something similar recently - where I would return a button off of a command, mainly so that when the user clicked on the button I would have a trigger_id that I could use to send a modal form for them to provide more info. An example of how I handled this:
First go to the Event Subscriptions section of your app settings page, and enable it. Provide the url that will listen for said events, then you need to select the events that your bot will be listening for. I subscribed to app_mention ( which will fire when your bot is #), and message.channels, which will fire on every message sent to the channel your bot is in.
Once you have done that you can subscribe to events... (by using the slack SDK https://slack.dev/node-slack-sdk/)
slackEvents.on('message', (event, body) => {
// do something here
console.log(`Received a message event: user ${event.user} in channel ${event.channel} says ${event.text}`);
});
In my case, once I received that message (I parse through the text to handle multiple commands) I would then use blocks to create a message with a button and send it back...
Payload:
channel: context.channelId,
text: "",
blocks: JSON.stringify([
{
type: "section",
text: {
type: "mrkdwn",
text: "So, you say you want to create a new task?\n*Great!*\nI just need your permission first, in order to initiate this action.\nPlease click the button below to launch this process, or cancel if you have changed your mind"
}
},
{
type: "actions",
block_id: "processAddTask",
elements: [
{
type: "button",
text: {
type: "plain_text",
emoji: true,
text: "Process"
},
style: "primary",
value: "1"
},
{
type: "button",
text: {
type: "plain_text",
emoji: true,
text: "Cancel"
},
style: "danger",
value: "0"
}
]
}
])
Then send that payload back to channel...
const response = await web.chat.postMessage(responseMessage);
Once all this was done the user would be presented with a couple of buttons, and when they clicked on the "go" button it will fire another event that you can catch:
slackInteractions.action({ callbackId: 'addTaskRequest' }, (payload, respond) => {
console.log('payload', payload);
let triggerId = payload.trigger_id;
let view = payloads.addTaskModal({ triggerId });
(async () => {
try {
let result = await slackClient.callAPIMethod("views.open", view);
respond("");
} catch (error) {
if (error.code === ErrorCode.PlatformError) {
respond(error.data);
} else {
respond('Well, that was unexpected.');
}
}
})();
});

sailsjs standalone action to view (Sailsjs V1.0.0)

I have a standalone action which I call from my front end via a button click using CloudSDK, I get my value but the action does not redirect to the view. I have specified the responseType as view in my success exit but this does not seem to work.
Button code in page.js file:
clickVerifyBtn: async function(uid) {
await Cloud.userIdVerify(uid);
},
the action
module.exports = {
friendlyName: 'User id verify',
description: '',
inputs: {
uid: {
description: 'Id of the user',
type: 'number'
},
},
exits: {
success:{
responseType: 'view',
viewTemplatePath: 'pages/admin/verification-doc',
}
},
fn: async function (inputs, exits) {
// All done.
var userdoc = await Photo.findOne({ownerId: inputs.uid, isRemoved:false, photoType:0})
var imageSrc = '/images/useruploads/'+userdoc.imageFileName;
return exits.success({imageSrc: imageSrc});
}
};
What is the correct way to achieve this ? Should I submitting my value to the action via the ajax-form component bundled in Sails?
Any help is greatly appreciated.
The action2 returns the variables not to the view directly but to the view's page instance (your_view.page.js) submitted method.

Vuex computed property only updates if route is changed and then changed back

In main.js of my app I dispatch an action that gets companies from my API and selects the first. This action dispatches every time, doesn't seem to fail here.
new Vue({
el: '#app',
store,
router,
render: h => h(App),
created: function () {
this.$store.dispatch('getCompaniesAndSelectFirst');
}
})
In another view I need to retrieve campaigns that belong to the selected company, so I do this on mount with vue-resource.
mounted: function () {
if (this.selectedCompanyId) {
this.$http.get('Campaigns', {params: {companyId: this.selectedCompanyId}}).then(response => {
// success callback
this.numbersTableData = response.body;
}, response => {
// error callback
});
}
}
selectedCompanyId is my computed property that returns the id of the selected company in Vuex.
selectedCompanyId: function () {
return this.$store.getters.selectedCompanyId;
}
The issue is selectedCompanyId is undefined at this point if this is the first view that's loaded.
If I refresh the page selectedCompanyId is still undefined and my get request fails.
If I open the application on this view, the selectedCompanyId is undefined and the get request fails, but if I route to another view and then route back selectedCompanyId has loaded and the request is successful.
If I open the application on another view, and then route to the view selectedCompanyId is defined and the get request is successful.
As I understand it this is because my get request that gets companies and selects the first one needs time to complete.
How can I solve this? Is there a better way to do what I'm trying to do?
I stopped dispatching the getCompaniesAndSelectFirst action in the created function of the Vue instance.
I modified the getCompaniesAndSelectFirst action to return a promise:
export const getCompaniesAndSelectFirst = ({ commit, state }) => {
return Vue.http.get('Companies').then(response => {
// get body data
var companies = response.body;
commit('getCompanies', companies);
commit('selectCompany', companies[0].Id);
}, response => {
// error callback
});
}
I created a navigation guard that dispatches the action if state does not contain a truthy value for selectedCompanyId, and only continues to the route once the promise returned from the action is resolved.

Categories