Issue with error message on widget

I’ve edited some code from some of the forums to create a reminders widget. It all works fine but every now and again I get the following text in the widget which lasts for a few minutes (presumably it fails to refresh sometimes) - Call Script.setWidget() to set the content of the widget. I am a javascript newbie and am not sure what is wrong in the code. I was wondering if someone could assist with what is causing it and how I can fix? Code below.

// Reminders due today and tomorrow widget
var MAX_TASKS_SHOWN = 12;
const NOW = new Date();


const TITLE_FONT = Font.boldSystemFont(8);
const BODY_FONT = Font.semiboldRoundedSystemFont(9);

const today = new Date()
const tomorrow = new Date(today)
  tomorrow.setDate(tomorrow.getDate() + 1)
  tomorrow.setHours(0,0,0,0)
const tomorrow2 = new Date(today)
  tomorrow2.setDate(tomorrow2.getDate() + 2)
  tomorrow2.setHours(0,0,0,0)
  
const BACKGROUND_DARK_MODE = "system" 
// options: "yes", "no", "system"

//
// Utils
//

const compareReminderDuedates = (reminderA, reminderB) =>
  reminderA.dueDate - reminderB.dueDate;
const sortRemindersByDuedateAsc = reminders =>
  reminders.sort(compareReminderDuedates);

/** Round date down to 00:00 */
const stripTime = date => new Date(new Date(date).setHours(0, 0, 0, 0));

/** d1 - d2: will be negative if d2 > d1 */
const daysBetween = (d1, d2) => {
  const differenceMs = stripTime(d1).getTime() - stripTime(d2).getTime();
  return Math.floor(differenceMs / 86400000);
};

const getOverdueTasks = async () => {
  const all = await Reminder.allIncomplete();
  return sortRemindersByDuedateAsc(
    all.filter(task => task.dueDate && task.dueDate < today)
  );
};

const getTodayTasks = async () => {
  const all = await Reminder.allIncomplete();
  return sortRemindersByDuedateAsc(
    all.filter(task => today <= task.dueDate && task.dueDate < tomorrow)
  );
};

const getTomorrowTasks = async () => {
  const all = await Reminder.allIncomplete();
  return sortRemindersByDuedateAsc(
    all.filter(task => tomorrow <= task.dueDate && task.dueDate < tomorrow2)
  );
};

// overdue tasks

const getWidget = async () => {
  const overdueTasks = await getOverdueTasks();
  const todayTasks = await getTodayTasks();
  const tomorrowTasks = await getTomorrowTasks();
  
  const widget = new ListWidget();
  widget.backgroundColor = BG_COLOR;
  
  let isDarkMode = 
    BACKGROUND_DARK_MODE=="system" ? 
    await isUsingDarkAppearance() : 
    BACKGROUND_DARK_MODE=="yes"
    
   if (isDarkMode) {
      var BG_COLOR = new Color("#000000");
      var TITLE_COLOR = new Color("#9E9E9E");
      var OVERDUE_COLOR = new Color("#FE4639");
      var TASK_COLOR = new Color("#FFFFFF");
      var NO_OVERDUE_COLOR = new Color("#2FD15D");
      } else {
      var BG_COLOR = Color.white();
      var TITLE_COLOR = new Color("#8C8C8C");
      var OVERDUE_COLOR = new Color("#FD3F32");
      var TASK_COLOR = new Color("#000000");
      var NO_OVERDUE_COLOR = new Color("#13C759");
  }
  
  widget.addSpacer(0);
  
  const dueNum = overdueTasks.length + todayTasks.length
  if (dueNum) {
    var title = widget.addText(`Today   ${dueNum}`);
  }else{
    var title = widget.addText(`Today`);
  }
  title.textColor = TITLE_COLOR;
  title.font = TITLE_FONT;
  



  if (overdueTasks.length) {
    overdueTasks.slice(0, MAX_TASKS_SHOWN).forEach(({ title, dueDate }) => {
      const task = widget.addText(`| ${title}`);
      task.textColor = OVERDUE_COLOR;
      task.font = BODY_FONT;
      task.lineLimit = 1;
    });
  } else {
    
  }
  
  MAX_TASKS_SHOWN = MAX_TASKS_SHOWN - overdueTasks.length 
  
  if (MAX_TASKS_SHOWN > 0) {
    if (todayTasks.length) {
      todayTasks.slice(0, MAX_TASKS_SHOWN).forEach(({ title, dueDate }) => {
        const task = widget.addText(`| ${title}`);
        task.textColor = TASK_COLOR;
        task.font = BODY_FONT;
        task.lineLimit = 1;
      });
    } else {}
  } else {}
  
  if (dueNum) {
    
  } else {
    const noTasks = widget.addText("All done.");
    noTasks.textColor = NO_OVERDUE_COLOR;
    noTasks.font = BODY_FONT;
  }

// tomorrow

//  const tomorrowTasks = Reminder.allDueTomorrow();
  
  widget.addSpacer(5);

  MAX_TASKS_SHOWN = MAX_TASKS_SHOWN - todayTasks.length - 2
  
  if (MAX_TASKS_SHOWN > 0){
    if (tomorrowTasks.length) {
      const title = widget.addText(`Tomorrow   ${tomorrowTasks.length}`);
      title.textColor = TITLE_COLOR;
      title.font = TITLE_FONT;
      tomorrowTasks.slice(0, MAX_TASKS_SHOWN).forEach(({ title, dueDate }) => {
        const task = widget.addText(`| ${title}`);
        task.textColor = TASK_COLOR;
        task.font = BODY_FONT;
        task.lineLimit = 1;
      });
    } else {}
  } else {}
  
  widget.addSpacer();

  return widget;
};

async function isUsingDarkAppearance() {
  // yes there's a Device.isUsingDarkAppearance() method
  // but I find it unreliable
  const wv = new WebView()
  let js ="(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)"
  let r = await wv.evaluateJavaScript(js)
  return r
}

(async () => {
  const widget = await getWidget();
  if (config.runsInWidget) {
    Script.setWidget(widget);
    Script.complete();
  } else await widget.presentSmall();
})();

You can remove the async function wrapper around the last bit of code. Scriptable already wraps everything you write in an asynchronous function called _scriptable_run, so await works at the apparent top level. Wrapping your code in an asynchronous function just means that it will finish after the execution environment has already moved past the end of _scriptable_run, which I don’t think you want. That might interfere with the execution environment’s handling of things like the widget.

I don’t know for sure if that’s why your code sometimes fails to set the widget, but it’s my best guess.

Spot on. I actually just helped someone else in Discord that had a similar code setup and the set widget API was never being called because of async

I presume that since the main async call wasn’t called with await, it immediately reaches the end of the script and sort of terminates.

1 Like

OK so I delete everything from async function onwards but then what do I need to add? If I add Script.setWidget(widget); instead I get an error. Not sure how to fix it so any help would be appreciated.

You only need to delete the wrapper; the body of the async function is what you need to keep.

So if you’ve deleted the function, then just replace it with:

const widget = await getWidget();
if (config.runsInWidget) {
  Script.setWidget(widget);
  Script.complete();
} else await widget.presentSmall();
1 Like

You can also utilize it like This

Reddit thread

Thanks so much. That works!

2 Likes

Also this does. Thanks.

1 Like