I have the widget below which basically shows reminders. I wanted to have an image as a background (using the wallpaper so I can have the widget appear transparent). I am not great with javascript and don’t know how to amend the code to make it use the image as the background). Can someone help me with what to do? (Bonus: use a different image when it switches to dark mode).
// 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();
})();```
Try the example in this thread. You will need to use the appropriate portion of your background image that corresponds with the section of the home screen where the widget is displayed.
Thanks for that. I’ve followed the instructions in the linked post here. I’ve added that code ahead of the code which I posted above. I’ve also downloaded and created the separate no-background script. I am getting the following error when running my widget after doing so: 2020-11-30 19:01:55: Error on line 286:25: Failed writing to disk.
I’m getting a message now (when running my widget) that “It looks like you selected an image that isn’t an iPhone screenshot or your iPhone is not supported. Try again with a different image.”. I have an iPhone 12 Pro Max. Should it work with that?
If I run it off the full page screenshot it says 1146, if I run it off the background the the widget I want it says 2778 (the latter having used the script published in the forum to convert a screenshot of the background to the background of the widget based on size and position).
Curious why it’s different. The no-background module expects 2778 based on your device. Would you mind sending me your script and screenshot? DM if you must.
That image screenshot actually worked for me.
I did make an adjustment your code because it’s assigning the background to the “wrong” widget.
The background image is being assigned to the widget variable that’s created on top of the script instead of the actual widget variable being used by the widget.
Try this steps:
Go to Files. Delete the folder /Scriptable/cache/nobg if it’s there
make sure your no-background is the latest version
Run the no-background script. Choose Generate Slices on the presented menu. Follow the on screen guides
Copy the script below and run it.
const { transparent } = importModule('no-background')
// const widget = new ListWidget()
// widget.backgroundImage = await transparent(Script.name()) // moved to line 74
// Reminders due today and tomorrow widget
var MAX_TASKS_SHOWN = 12;
const NOW = new Date();
const RESET_WALLPAPER = false
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.backgroundImage = await transparent(Script.name(), RESET_WALLPAPER)
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.backgroundColor = BG_COLOR;
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();
})();
One question - if I run no-background and generate slices when in light mode and then do the same with a different back ground for dark mode. I’m finding in the widget it’s not automatically changing between the dark mode and light mode background. Is there an issue with my code or something else wrong?
No issue there. It’s because the widget needs to “refresh” before applying the new background. There’s no automated way to force a refresh with widgets when changing appearance modes. All you can do is wait.