bolt-js
bolt-js copied to clipboard
Views.update upon static select action not updating modal
https://user-images.githubusercontent.com/86734693/185929505-d678befb-a623-44e0-abd3-ce037ff542cf.mov
Description
Hello,
I've been working with Slack support via email and was directed here to check into my issue.
I have a modal with several static selects (1 for each day of the week) and depending on which option the user selects, it will update and populate the modal with more static selects. My issue is that sometimes when the user is selecting an option, it will not correctly update the modal. It acknowledges the request, no error is thrown, and it just stays the same. If you go on to the next day it will often "push through" the correct blocks with the next views.update call. The biggest headache is that this issue is intermittent. It doesn't happen always but frequently enough to cause regular issues for users.
I've tried to alter my code in several ways and still haven't found the issue.
** I should note I did find issue #1080 but it was closed without a solution. I've done much of everything suggested but to no avail. :(
Any help is much appreciated!
What type of issue is this? (place an x
in one of the [ ]
)
- [ x] bug
- [ ] enhancement (feature request)
- [ ] question
- [ ] documentation related
- [ ] example code related
- [ ] testing related
- [ ] discussion
Requirements (place an x
in each of the [ ]
)
- [ x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
- [x ] I've read and agree to the Code of Conduct.
- [ x] I've searched for any related issues and avoided creating a duplicate issue.
Bug Report
Filling out the following details about bugs will help us solve your issue sooner.
Reproducible in:
package version: Bolt 3.12.1
node version: v18.2.0
OS version(s): MacOS Montery 12.2.1
Steps to reproduce:
- Select an option with a static select.
- If the view updates, good. If not,
- Select another option or continue to the next day
- If the next select was used and it updates, it will push both updates (current block and previous unupdated block)
Expected result:
I expect that the modal will update on each action of the first static select. There should appear either 1 more static select, or 3, depending on which option is first selected.
Actual result:
Intermittently the view will not show any updates. The method is called and the blocks pushed are correct. It just doesn't actually show any update to the modal.
Attachments:
Here's a code snippet which receives the static select action and based on the selection it updates the modal with the blocks it creates.
if(inCheck === 'in') {
let plt = payload.selected_option.text.text;
let plv = payload.selected_option.value;
if(payload.selected_option.value === 'Banked Time') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), bankedHours, workedBlock, workedHours); //these elements are premade with options depending on selected option
//Fix the header photo; remove and re-add to clear extra fields
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view with the new elements pushed into the block
}
else if(payload.selected_option.value === 'Personal (Paid)' || payload.selected_option.value === 'Annual Leave') { //test selected option for a time off type and if so, update the block with additional static selects
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), ptoHours, workedBlock, workedHours); //these elements are premade with options depending on selected option
//Fix the header photo; remove and re-add to clear extra fields
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view with the new elements pushed into the block
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else if (payload.selected_option.value === 'Off') {
//Pop off all blocks except the start time block (first element)
while(editingBlock.elements.length > 1) {
editingBlock.elements.pop();
};
//Fix the header photo; remove and re-add to clear extra fields
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view with the new elements pushed into the block
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else if (payload.selected_option.value === 'Stat') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), ptoSpecial, workedBlock, workedHours);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else if (payload.selected_option.value === 'Bereavement' || payload.selected_option.value === 'Cultural Leave Day') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), ptoSpecial);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash)
} else if (payload.selected_option.value === 'Personal (Unpaid)') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), unpaidBlock, workedBlock, workedHours);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash)
} else {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), afternoonBlock);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
}
}
}
Hello! Are you able to share more of your application code? Having the full app code for generating the modal initially, attaching all the relevant handlers, updating the modal, etc. would make reproducing the issue in the first place much easier.
Also understanding what receiver you are using in your application (HTTPReceiver, SocketModeReceiver) is also helpful.
Finally, what kind of plan the workspace you are seeing this issue on would also help.
My first step towards helping will be to reproduce the issue, so if you can help me get there, that would speed up any assistance we can provide.
Cheers!
Hi!
I am using the HTTPReceiver with some custom Express routes. Well, they're enabled but we don't have any actually setup currently. WIP.
It is a Slack for non-profits, so whichever type that equates to. I believe it's the lowest tier paid plan.
And for the code:
This is the function which generates the timesheet blocks a user initially interacts with:
//
// Generate manual fill sheet blocks
// Used for payroll modals
//
//
function generateTimesheetInputBlocks(timesheet, startNum, endNum) {
try {
let periodDays = new PeriodDays(timesheet.p_start); //takes a start day and extrapolates it into 14 dates (period)
let dates = [];
for(let x in periodDays) {
dates.push(periodDays[x]);
};
let initialStart = {
"text": {
"type": "plain_text",
"text": "Off",
"emoji": false
},
"value": "Off"
};
let blocks = [];
blocks.push(bB.div);
for(let i = startNum; i<endNum; i++) {
let title = {
"type": "section",
"text":
{
"type": "mrkdwn",
"text": `*${format(dates[i], 'PPPP')}*`
}
}//SUNDAY MORNING START WEEK 1
let inputs = {
"type": "actions",
"block_id": `day${i}hours`,
"elements": [
{
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "Start time...",
"emoji": false
},
"action_id": `day${i}in`,
"initial_option": initialStart,
"option_groups": [
{
"label": {
"type": "plain_text",
"text": "Common Starts"
},
"options": commonStarts,
},
{
"label": {
"type": "plain_text",
"text": "Time Off"
},
"options": offTypes,
},
{
"label": {
"type": "plain_text",
"text": "Other Hours"
},
"options": buildTimeOptionsArrayAM().concat(buildTimeOptionsArrayPM()),
}
]
}
]
}
let notes = {
"type": "input",
"block_id": `day${i}notes`,
"optional": true,
"element": {
"type": "plain_text_input",
"action_id": `day${i}notes`,
"max_length": 50
},
"label": {
"type": "plain_text",
"text": "Notes",
"emoji": false
}
}
blocks.push(title, inputs, notes, bB.div);
};
return blocks;
} catch (error) {
logger.error(error);
}
};
This is the handler for when a user chooses a selection option from the modal above (start time or time off type)
//Handler for when a start type is selected for each day
app.action(/^day(\d|1[0-3])(in|out)$/, async ({ ack, payload, body, client}) => {
try {
await ack();
if(payload.selected_option) {
async function buildBlocks (ack, payload, body, client) {
let mfts = JSON.parse(body.view.private_metadata); //original timesheet object for holding data
//contains dates for each day of the pay period and pushes those dates to an array for each date
let periodDays = new PeriodDays(mfts.p_start);
let daysArray = [];
for(let x in periodDays) {
daysArray.push(periodDays[x]);
};
//this whole section is for finding the which day/block is being updated. Finds the block id, action_id and splits it into the day to make sure it's an 'in' block (first dropdown) and not an out block (second dropdwon)
let blockValues = body.view.state.values[`${body.actions[0].block_id}`];
let actionIds = [];
for(let x in blockValues) {
actionIds.push(x);
};
let splitDay = actionIds[0].split('day')[1];
let dayNum = parseInt(splitDay.split('in')[0]);
let inCheck = payload.action_id.split(/^day(\d|1[0-3])(in)$/)[2];
//
// Premade options
//
//PTO hours if a PTO with variable hours is selected
let ptoHours =
{
"type":"static_select",
"action_id":`day${dayNum}ptoused`,
"placeholder":{
"type":"plain_text",
"text":"PTO Hours Used...",
"emoji":false
},
"initial_option": {
"text": {
"type": "plain_text",
"text": "8 PTO Hours",
"emoji": false
},
"value": "8"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "4 PTO Hours",
"emoji": false
},
"value": "4"
},
{
"text": {
"type": "plain_text",
"text": "8 PTO Hours",
"emoji": false
},
"value": "8"
}
]
};
function createBankedHours() {
let selects = [];
for (let x = 0.5; x<=8.0; x = x+0.5) {
selects.push(
{
"text": {
"type": "plain_text",
"text": `${x.toString()} Banked Hours`,
"emoji": false
},
"value": x.toString()
},
)
}
return {
"type":"static_select",
"action_id":`day${dayNum}bankedused`,
"placeholder":{
"type":"plain_text",
"text":"Banked Hours Used...",
"emoji":false
},
"initial_option": {
"text": {
"type": "plain_text",
"text": "8 Banked Hours",
"emoji": false
},
"value": "8"
},
"options": selects
};
}
let bankedHours = createBankedHours();
//Worked hours if they worked in addition to taking PTO
let workedHours =
{
"type":"static_select",
"action_id":`day${dayNum}workedextra`,
"placeholder":{
"type":"plain_text",
"text":"Additional hours worked...",
"emoji":false
},
"initial_option": {
"text": {
"type": "plain_text",
"text": "0 Worked Hours",
"emoji": false
},
"value": "0"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "0 Worked Hours",
"emoji": false
},
"value": "0"
},
{
"text": {
"type": "plain_text",
"text": "1 Worked Hours",
"emoji": false
},
"value": "1"
},
{
"text": {
"type": "plain_text",
"text": "2 Worked Hours",
"emoji": false
},
"value": "2"
},
{
"text": {
"type": "plain_text",
"text": "3 Worked Hours",
"emoji": false
},
"value": "3"
},
{
"text": {
"type": "plain_text",
"text": "4 Worked Hours",
"emoji": false
},
"value": "4"
},
{
"text": {
"type": "plain_text",
"text": "5 Worked Hours",
"emoji": false
},
"value": "5"
},
{
"text": {
"type": "plain_text",
"text": "6 Worked Hours",
"emoji": false
},
"value": "6"
},
{
"text": {
"type": "plain_text",
"text": "7 Worked Hours",
"emoji": false
},
"value": "7"
},
{
"text": {
"type": "plain_text",
"text": "8 Worked Hours",
"emoji": false
},
"value": "8"
},
{
"text": {
"type": "plain_text",
"text": "9 Worked Hours",
"emoji": false
},
"value": "9"
},
{
"text": {
"type": "plain_text",
"text": "10 Worked Hours",
"emoji": false
},
"value": "10"
},
{
"text": {
"type": "plain_text",
"text": "11 Worked Hours",
"emoji": false
},
"value": "11"
},
{
"text": {
"type": "plain_text",
"text": "12 Worked Hours",
"emoji": false
},
"value": "12"
},
{
"text": {
"type": "plain_text",
"text": "13 Worked Hours",
"emoji": false
},
"value": "13"
},
{
"text": {
"type": "plain_text",
"text": "14 Worked Hours",
"emoji": false
},
"value": "14"
},
{
"text": {
"type": "plain_text",
"text": "15 Worked Hours",
"emoji": false
},
"value": "15"
},
{
"text": {
"type": "plain_text",
"text": "16 Worked Hours",
"emoji": false
},
"value": "16"
}
]
};
//Standard end times for shifts if the staff worked
let afternoonBlock = {
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "End time...",
"emoji": false
},
"action_id": `day${dayNum}out`,
"initial_option": {
"text": {
"type": "plain_text",
"text": "N/A",
"emoji": false
},
"value": 'N/A'
},
"option_groups": [
{
"label": {
"type": "plain_text",
"text": "Common Ends"
},
"options": commonEnds,
},
{
"label": {
"type": "plain_text",
"text": "Other Hours"
},
"options": buildTimeOptionsArrayAM().concat(buildTimeOptionsArrayPM()),
}
]
}
//True/False block to check if staff worked or not along with PTO
let workedBlock = {
"type":"static_select",
"action_id":`day${dayNum}worked`,
"placeholder":{
"type":"plain_text",
"text":"Did you work as well?",
"emoji":false
},
"initial_option": {
"text": {
"type": "plain_text",
"text": "I did not work",
"emoji": false
},
"value": 'false'
},
"options": [
{
"text": {
"type": "plain_text",
"text": "I worked",
"emoji": false
},
"value": 'true'
},
{
"text": {
"type": "plain_text",
"text": "I did not work",
"emoji": false
},
"value": 'false'
}
]
};
//Block with 8 Hours as the only option in the event they choose certain PTO (Bereavement)
let ptoSpecial = {
"type":"static_select",
"action_id":`day${dayNum}ptospecial`,
"placeholder":{
"type":"plain_text",
"text":"PTO Hours Used...",
"emoji":false
},
"initial_option": {
"text": {
"type": "plain_text",
"text": "8 PTO Hours",
"emoji": false
},
"value": '8'
},
"options": [
{
"text": {
"type": "plain_text",
"text": "8 PTO Hours",
"emoji": false
},
"value": '8'
}
]
};
//No hours block for when Unpaid day is selected
let unpaidBlock = {
"type":"static_select",
"action_id":`day${dayNum}ptounpaid`,
"placeholder":{
"type":"plain_text",
"text":"Hours...",
"emoji":false
},
"initial_option": {
"text": {
"type": "plain_text",
"text": "0 Hours",
"emoji": false
},
"value": '0'
},
"options": [
{
"text": {
"type": "plain_text",
"text": "0 Hours",
"emoji": false
},
"value": '0'
}
]
};
function inBlock(dayNum, text = 'N/A', value = 'N/A') {
return {
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "Start time...",
"emoji": false
},
"action_id": `day${dayNum}in`,
"initial_option": {
"text": {
"type": "plain_text",
"text": text,
"emoji": false
},
"value": value,
},
"option_groups": [
{
"label": {
"type": "plain_text",
"text": "Common Starts"
},
"options": commonStarts,
},
{
"label": {
"type": "plain_text",
"text": "Time Off"
},
"options": offTypes,
},
{
"label": {
"type": "plain_text",
"text": "Other Hours"
},
"options": buildTimeOptionsArrayAM().concat(buildTimeOptionsArrayPM()),
}
]
}
}
//
// end of premade options
//
//logic for how to decide which dropdowns to add based on the input from the first dropdown of the
//old view is the view when the static select action is taken
//use the old view to find the associated day and update those blocks as neccesary
let oldView = body.view;
let blockNum = payload.block_id //block id for the day we are updating
let editingBlock = {}; //the block for the day we are updating
//go through the blocks, find the block, and set it to the editingBlock for updates
for(let i = 0; i<oldView.blocks.length; i++) {
for (let x in oldView.blocks[i]) {
if (oldView.blocks[i][x] === blockNum) {
editingBlock = oldView.blocks[i];
break;
}
}
}
//make sure it's the first static select element in the block (ie. day0in and not day0out)
//then check what the selected option is, add the additional selects, and update the view
if(inCheck === 'in') {
let plt = payload.selected_option.text.text;
let plv = payload.selected_option.value;
if(payload.selected_option.value === 'Banked Time') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), bankedHours, workedBlock, workedHours); //these elements are premade with options depending on selected option
//Fix the header photo; remove and re-add to clear extra fields
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view with the new elements pushed into the block
}
else if(payload.selected_option.value === 'Personal (Paid)' || payload.selected_option.value === 'Annual Leave') { //test selected option for a time off type and if so, update the block with additional static selects
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), ptoHours, workedBlock, workedHours); //these elements are premade with options depending on selected option
//Fix the header photo; remove and re-add to clear extra fields
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view with the new elements pushed into the block
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else if (payload.selected_option.value === 'Off') {
//Pop off all blocks except the start time block (first element)
while(editingBlock.elements.length > 1) {
editingBlock.elements.pop();
};
//Fix the header photo; remove and re-add to clear extra fields
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view with the new elements pushed into the block
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else if (payload.selected_option.value === 'Stat') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), ptoSpecial, workedBlock, workedHours);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else if (payload.selected_option.value === 'Bereavement' || payload.selected_option.value === 'Cultural Leave Day') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), ptoSpecial);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash)
} else if (payload.selected_option.value === 'Personal (Unpaid)') {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), unpaidBlock, workedBlock, workedHours);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash)
} else {
//Pop off all blocks except the start time block (first element)
editingBlock.elements = [];
//Add additional elements to the block to be updated
editingBlock.elements.push(inBlock(dayNum, plt, plv), afternoonBlock);
//Fix the header photo; remove and re-add
oldView.blocks.shift();
oldView.blocks.unshift(bB.image(appImages.payrollMenu, 'FL Logo'))
return oldView;
//Update view
//await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
}
}
}
let oldView = await buildBlocks(ack, payload, body, client);
await sleep(1000)
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
await updateViewAction(client, body, bB.modal(oldView.callback_id, oldView.private_metadata, oldView.title.text, oldView.submit.text, oldView.close.text, oldView.blocks), oldView.hash);
} else {
await ack();
pushView(client, body.trigger_id, bB.modal('v_prErrors', '', 'Empty Start Value', 'void', 'Close', [bB.text('void', 'Ensure you select a start time or time off type. Starting selections cannot be empty.')]))
}
} catch (err) {
logger.error(err)
}
});
If there is any other code that would be helpful please let me know! I apologize for the mess!
Hello! Wondering if there's been any headway with this issue. I've been tweaking my code but can't figure it out. It seems like as the modal fills up more and more the blocks are less and less responsive. I've tried logging out which blocks are pushed and the call to update is correct it just isn't manifesting on the modal itself.
👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.
Just commenting to keep this active. Still having this issue. I've noticed it gets progressively worse the more static selects I fill in the modal. The first couple works fine but then they get more and more prone to not updating as users continue to fill in the selects.
Sorry for the lack of response on my end.
Can you share code for the updateViewAction
method? I see you are using the view hash
in some form. I'd like to see how you are using that.
At a high level, this smells like maybe a race condition. Using the view hash is a means around that. Are you seeing any relevant errors pop up in the error log at all, by the way?
Hi there,
Here is the code!
async function updateViewAction(client, body, modalWithBlocks, hash) {
try {
let result = await client.views.update({
token: process.env.SLACK_BOT_TOKEN,
view: modalWithBlocks,
view_id: body.view.id,
hash
});
} catch (err) {
logger.error(err);
}
}
The only error I get is 'error reading value of undefined' when the view submission listener tries to parse the state of the submission. This is of course because the view does not contain the blocks it was supposed to because the view didn't update as the code expected it to.
Hey @mjcbfl , this is not the complete code. I am trying to put together a small app mirroring this behaviour; without a reproduction case it is very hard for me to help you. Trying out simple apps with a few select menus seem to work fine on my end.
Your code is quite complex. There are many branches in the logic, the view payloads are quite large, and the form and modal inputs are big. The code you provide is missing many definitions (PeriodDays
, bB
, buildTimeOptionsArrayAM
, buildTimeOptionsArrayPM
- among many others). Without the complete code, I cannot help you. Could you assemble a new GitHub project or gist, that has the entire bolt project, that I can clone down and run locally?
Hi, @filmaj! I have created a clone of the project for you to play with and have added you as a collaborator. If there are any issues, let me know! I am far from an experienced developer so I am sure you will find many shocking discoveries in my code, but it's built with a goal in mind and that goal wasn't to be pretty! 😂
Great, I have accepted the invite and will take a look now... 👀
Just a quick update: took me a while to get the app setup (had to craft a few throwaway spreadsheets with specific column header names, specific number of sub-sheets, etc.) but I have the app up and running! Additionally, I can confirm I was able to reproduce the issue you posted above in your original video!
Now, to start figuring out how the timesheet modal logic you have in place fits together and see what can be done to mitigate the issue...
Excellent news! It's entirely custom to our workspace so I can imagine it was a pain to get running but I very much appreciate your effort!
I have a few other tasks to take on today but I will definitely dive deeper into this issue tomorrow.
Just a few updates in case anyone is following this issue:
- I confirmed that the
hash
parameter to the views.update API is being used properly, so that earlier hypothesis of mine is not relevant for this issue. - All calls to the views.update API succeed - the response always contains an
ok: true
field. - When reproducing the issue, in my experience, if I select the drop-down menu items quickly (similarly to how @mjcbfl does it in the video in the original post in this issue), it is easier to reproduce. If I introduce some pauses between interacting with the select menu drop downs (say, 5 seconds between selections), then the issue is much harder to reproduce. This tells me that there are performance characteristics at play here.
- Interestingly, if I wait long enough after selection and no view update (say, 30 seconds or so), the view does eventually update! So it seems like maybe the backend is getting 'clogged up' somehow in updating the view. Another clue that this may be performance related.
- The block payloads are huge, mainly because selection for time-of-day is implemented using static select Block Kit elements. I suggested to @mjcbfl to change the implementation here to use the Timepicker element instead of manually generating a separate static option select element per 15 min time chunk. This should make the block kit payloads much leaner. It should be easier to manage in the code, too, but I'm also hoping this may have a positive influence on how hard it is to reproduce the issue.
👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.
As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.