utils.getDataByObjectURL works in editor but not on mobile

hey guys, im running into an issue where when running retool mobile the function utils.getDataByObjectURL doesnt seem to work correctly when running on a phone, but on the editor it works just right, is anything i can do?

Can you share some example code of how you're using getDataByObjectURL?

I have a JS query that runs the following code:

const imageComponent =
  selectAlternativeType.value === "color"
    ? imageInput4
    : selectAlternativeType.value === "size"
    ? imageInput5
    : selectAlternativeType.value === "other"
    ? imageInput6
    : undefined;

await GENERATED_IMAGE_UUID.setValue(uuid.v4());
await downloadAlternativeImage.trigger({
  additionalScope: { url: imageComponent.value[0] },
});
await uploadAlternativeImage.trigger({
  additionalScope: { type: imageComponent.files[0].type },
});

downloadAlternativeImage contains just the following line:

return await utils.getDataByObjectURL(url);

uploadAlternativeImage is a REST request such as:

I tried hardcoding the imageComponent without the tertiary validation, and it still doesnt work.

Can you test out this app and see if it works for you?
dec 12 image input test.json (9.8 KB)

Here's what you should see:

Your example works on mobile, i tried replicating what you did on my app (the text components printing the results of the queries) and it displays the correct values on my phone, but the rest of the code seems to ignore the result of the upload and it displays null, here's the rest of my createAlternative code. Could there be a race condition i'm not seeing?

const imageComponent =
  selectAlternativeType.value === "color"
    ? imageInput4
    : selectAlternativeType.value === "size"
    ? imageInput5
    : selectAlternativeType.value === "other"
    ? imageInput6
    : undefined;

await GENERATED_IMAGE_UUID.setValue(uuid.v4());
await downloadAlternativeImage.trigger({
  additionalScope: { url: imageComponent.value[0] },
});
await uploadAlternativeImage.trigger({
  additionalScope: { type: imageComponent.files[0].type },
});

const imageUrl = uploadAlternativeImage.data

if (selectAlternativeType.value === "color") {
  for (const color of multiselect1.value) {
    const selectedColor = colorLibrary.value.find((item) => item.hex === color);

    const productSnapshot = {
      color: { primary: [selectedColor], secondary: { name: "", hex: "" } },
      imageUrl,
      name: SELECTED_ORDER_PRODUCT_VARIANT.value.productName,
      size: SELECTED_ORDER_PRODUCT_VARIANT.value.productSize,
      price: SELECTED_ORDER_ITEM.value.total,
      merchantId: SELECTED_ORDER_PRODUCT_VARIANT.value.merchantId,
      categoryId: SELECTED_ORDER_PRODUCT_VARIANT.value.categoryId,
    };
    const alternativeId = uuid.v4();
    await postOrderItemAlternative.trigger({
      additionalScope: { id: alternativeId, productSnapshot },
    });
    postRefOrderItemAlternative.trigger({
      additionalScope: {
        alternativeId,
        orderItemId: SELECTED_ORDER_ITEM.value.id,
      },
    });
  }
}
if (selectAlternativeType.value === "size") {
  for (const size of multiselect2.value) {
    const productSnapshot = {
      size: size,
      color: SELECTED_ORDER_PRODUCT_VARIANT.value.productVariantColorData,
      imageUrl,
      name: SELECTED_ORDER_PRODUCT_VARIANT.value.productName,
      price: SELECTED_ORDER_ITEM.value.total,
      merchantId: SELECTED_ORDER_PRODUCT_VARIANT.value.merchantId,
      categoryId: SELECTED_ORDER_PRODUCT_VARIANT.value.categoryId,
    };
    const alternativeId = uuid.v4();
    await postOrderItemAlternative.trigger({
      additionalScope: { id: alternativeId, productSnapshot },
    });
    postRefOrderItemAlternative.trigger({
      additionalScope: {
        alternativeId,
        orderItemId: SELECTED_ORDER_ITEM.value.id,
      },
    });
  }
}
if (selectAlternativeType.value === "other") {
  const selectedColor = colorLibrary.value.find(
    (color) => color.hex === select8.value
  );
  const productSnapshot = {
    color: { primary: [selectedColor], secondary: { name: "", hex: "" } },
    imageUrl,
    name: textInput1.value,
    size: select9.value,
    price: parseInt(numberInput3.value),
    merchantId: SELECTED_ORDER_PRODUCT_VARIANT.value.merchantId,
    categoryId: SELECTED_ORDER_PRODUCT_VARIANT.value.categoryId,
  };
  const alternativeId = uuid.v4();
  await postOrderItemAlternative.trigger({
    additionalScope: { id: alternativeId, productSnapshot },
  });
  postRefOrderItemAlternative.trigger({
    additionalScope: {
      alternativeId,
      orderItemId: SELECTED_ORDER_ITEM.value.id,
    },
  });
}

Try adding a text input to your app and do something like this:

await downloadAlternativeImage.trigger({
  ...
});

debuggingTextInput.setValue(`downloadAlternativeImage.data is ${downloadAlternativeImage.data}`)

await uploadAlternativeImage.trigger({
  ...
});

Thats what i did, as shown here. I tried accessing the url from the component but nothing happens.

I also tried adding a timeout to wait for the query data to be not null and nothing happens

await uploadAlternativeImage.trigger({
  additionalScope: { type: imageComponent.files[0].type },
});

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

while(!uploadAlternativeImage.data){
  await wait(100)
}
const imageUrl = uploadAlternativeImage.data;

When running the app in preview mode in the desktop, imageURL is still null, but i managed to find this error message on the console when running downloadAlternativeImage

[Report Only] Refused to connect to 'blob:https://<MY DOMAIN>.retool.com/56752c46-8918-4012-9a36-6e3e35ca5c7b' because it violates the following Content Security Policy directive: "connect-src * 'self' p.retool.com rum-http-intake.logs.datadoghq.com wss://*.intercom.io rs.fullstory.com retool-edge.com api.mapbox.com". Note that '*' matches only URLs with network schemes ('http', 'https', 'ws', 'wss'), or URLs whose scheme matches "self"'s scheme. The scheme 'blob:' must be added explicitly.

I'm confused, aren't we trying to debug downloadAlternativeImage? Your examples seem to focus on uploadAlternativeImage.

One other thing is you might want to use this pattern to call queries:

const base64Data = await downloadAlternativeImage.trigger({
  ...
})

Should be the same as reading the data property though as long as the query isn't being fired many times.

Doing the query calling pattern you suggested worked! Thank you for helping me solve the issue, i didn't know you could instance query results into a variable when calling them, its nice to know.

1 Like

Great to hear! It sounds like what may have been happening is the query was being triggered from multiple places, causing the data property to get overwritten somehow to something unexpected.

1 Like