Recently, I wrote a tool for encrypting/decrypting information, displayed via a ListWidget. However, I noticed a strange phenomenon: clicking a button to call a URLScheme triggers the request twice, causing my pagination feature to fail. Has anyone encountered a similar issue, and if so, how did you resolve it?
this is my code:
const widget = new ListWidget();
widget.spacing = 1;
let hasNextPage = false;
let hasPrevPage = false;
let nextPageUrl = null;
let prevPageUrl = null;
let currentApiUrl = null;
let deafault_url = `http://172.20.10.2:8000/message/?creator=tomzang&page=1`
async function main() {
try {
const params = args.queryParameters;
const isFromURIScheme = params?._trigger === "button";
if (!isFromURIScheme && !config.runsInWidget) {
currentApiUrl = deafault_url;
}
else if (isFromURIScheme) {
currentApiUrl = params?.current_url;
}
currentApiUrl = params?.current_url ?? deafault_url;
const request = new Request(currentApiUrl);
request.method = "GET";
request.headers = { "Content-Type": "application/json" };
const response = await request.loadJSON();
hasNextPage = !!response.next;
hasPrevPage = !!response.previous;
nextPageUrl = response.next;
prevPageUrl = response.previous;
const contents = response.results.map(item => ({
content: item.content,
createdAt: formatDate(item.created_at)
}));
const parsedContents = contents.map(item => ({
text: xorDecrypt(item.content),
date: item.createdAt
}));
displayData(parsedContents);
addActionButtons();
} catch (error) {
widget.addText("⚠️ error");
widget.addText(error.message);
}
Script.setWidget(widget);
}
function xorDecrypt(message) {
token = "1223334"
const bytes = [];
for (let i = 0; i < message.length; i += 2) {
const byte = parseInt(message.substr(i, 2), 16);
bytes.push(byte);
}
const keyBytes = [];
for (let i = 0; i < token.length; i++) {
const charCode = token.charCodeAt(i);
if (charCode < 128) {
keyBytes.push(charCode);
} else if (charCode < 2048) {
keyBytes.push(0xC0 | (charCode >> 6));
keyBytes.push(0x80 | (charCode & 0x3F));
} else {
keyBytes.push(0xE0 | (charCode >> 12));
keyBytes.push(0x80 | ((charCode >> 6) & 0x3F));
keyBytes.push(0x80 | (charCode & 0x3F));
}
}
const result = [];
let keyIndex = 0;
for (const byte of bytes) {
const decryptedByte = byte ^ keyBytes[keyIndex];
result.push(decryptedByte);
keyIndex = (keyIndex + 1) % keyBytes.length;
}
let str = "";
let i = 0;
while (i < result.length) {
const byte1 = result[i++];
if (byte1 < 128) {
str += String.fromCharCode(byte1);
} else if (byte1 >= 192 && byte1 < 224) {
const byte2 = result[i++];
str += String.fromCharCode(((byte1 & 0x1F) << 6) | (byte2 & 0x3F));
} else {
const byte2 = result[i++];
const byte3 = result[i++];
str += String.fromCharCode(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F));
}
}
return str;
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString('en', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).replace(/\//g, '-');
}
function displayData(contents) {
const title = widget.addText(`📊 result`);
title.font = Font.boldSystemFont(16);
if (contents.length === 0) {
widget.addText("nope");
return;
}
contents.forEach(item => {
const dateText = widget.addText(item.date);
dateText.font = Font.mediumSystemFont(10);
dateText.textColor = new Color("#888888");
const contentText = widget.addText(item.text);
contentText.font = Font.systemFont(12);
contentText.textColor = Color.gray();
widget.addSpacer(1);
const separator = widget.addStack();
separator.size = new Size(0, 2);
separator.backgroundColor = new Color("#A52A2A");
widget.addSpacer(1);
});
}
function addActionButtons() {
widget.addSpacer();
const buttonStack = widget.addStack();
buttonStack.layoutHorizontally();
buttonStack.spacing = 8;
if (hasPrevPage) {
const prevButton = buttonStack.addText("⬅️ up");
prevButton.font = Font.mediumSystemFont(14);
prevButton.textColor = Color.blue();
prevButton.url = generatePrevPageUrl();
} else {
buttonStack.addSpacer();
}
const refreshButton = buttonStack.addText("🔄 refresh");
refreshButton.font = Font.mediumSystemFont(14);
refreshButton.textColor = Color.blue();
refreshButton.url = generateRefreshPageUrl();
if (hasNextPage) {
const nextButton = buttonStack.addText("down ➡️");
nextButton.font = Font.mediumSystemFont(14);
nextButton.textColor = Color.blue();
nextButton.url = generateNextPageUrl();
} else {
buttonStack.addSpacer();
}
}
function generatePrevPageUrl() {
return "scriptable:///run?scriptName=" +
encodeURIComponent(Script.name()) +
"¤t_url=" + encodeURIComponent(prevPageUrl)+
"&_trigger=button"
}
function generateRefreshPageUrl() {
return "scriptable:///run?scriptName=" +
encodeURIComponent(Script.name()) +
"¤t_url=" + encodeURIComponent(currentApiUrl)+
"&_trigger=button"
}
function generateNextPageUrl() {
return "scriptable:///run?scriptName=" +
encodeURIComponent(Script.name()) +
"¤t_url=" + encodeURIComponent(nextPageUrl)+
"&_trigger=button"
}
await main();
Script.complete();
and there is my backend logs: