Skip to content

Instantly share code, notes, and snippets.

@Critter
Forked from iiKurt/Wikipedia Top Read.js
Created January 2, 2024 03:22
Show Gist options
  • Save Critter/e60537ad7441fa2690455a7ab4121aa0 to your computer and use it in GitHub Desktop.
Save Critter/e60537ad7441fa2690455a7ab4121aa0 to your computer and use it in GitHub Desktop.
Wikipedia Top Read Widget
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: brown; icon-glyph: bookmark;
// Useful links
// https://en.wikipedia.org/api/rest_v1/#/Feed/aggregatedFeed
// https://design.wikimedia.org/blog/2021/04/26/bringing-wikipedia-to-the-homescreen-on-ios.html
// https://design.wikimedia.org/blog/assets/uploads/wikipedia-widget/wikipedia-top-read-widget.png
const PREFS = {
background: Color.dynamic(new Color("#FFF"), new Color("#000")),
foregroundPrimary: Color.dynamic(new Color("#00"), new Color("#FF")),
foregroundSecondary: Color.dynamic(new Color("#8A898D"), new Color("#8E8D93")),
statsBackground: Color.dynamic(new Color("#F8F7F9"), new Color("#1F1F1F")),
statsLines: Color.dynamic(new Color("#EBEBEB"), new Color("#373737")),
statsForeground: Color.dynamic(new Color("#4DAA8C"), new Color("#4DAA8C")),
// Not actually used at the moment
font: {
name: "San Francisco",
size: 16,
},
rankColors: [
new Color("#3F64C7"),
new Color("#3E75B5"),
new Color("#4188A7"),
new Color("#479B99")
],
debugMode: false
}
// Preview widget
if (PREFS.debugMode) {
let widget = await createWidget();
await widget.presentLarge();
}
// Running in widget
else if (config.runsInWidget) {
let widget = await createWidget()
Script.setWidget(widget);
}
// Not running in widget/being run directly
else {
let app = await createApp();
await QuickLook.present(app);
}
Script.complete();
async function createApp() {
let topRead = await getTopRead(4);
const table = new UITable()
for (let index = 0; index < topRead.length; index++) {
let article = await topRead[index];
let row = new UITableRow();
let textCell = row.addText(article.normalizedtitle + "\n" + article.description);
let imageCell = row.addImageAtURL(article.thumbnail.source);
textCell.widthWeight = 100;
imageCell.widthWeight = 10;
row.height = 60;
row.cellSpacing = 10;
row.onSelect = () => {
Safari.open(article.content_urls.desktop.page);
}
row.dismissOnSelect = false;
table.addRow(row);
}
return table
}
async function createWidget() {
let widget = new ListWidget()
// Light color first, dark color second
widget.backgroundColor = PREFS.background;
widget.setPadding(15, 15, 0, 15);
// (top, leading, bottom, trailing)
const line = widget.addText("Top read");
line.font = Font.boldSystemFont(18);
line.textColor = PREFS.foregroundPrimary;
// widget.addSpacer();
const listStack = widget.addStack();
listStack.layoutVertically();
listStack.setPadding(7.5, 0, 0, 0);
let topRead = await getTopRead(4);
for (let index = 0; index < topRead.length; index++) {
let article = topRead[index];
await listItem(
listStack,
index + 1,
article.normalizedtitle,
article.description,
article.views,
await loadThumbnail(article.thumbnail.source),
);
};
// await listItem(listStack, 1, "Octavia E. Butler", "American science fiction writer", 10000, "https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Mexico_City_New_Years_2013%21_%288333128248%29.jpg/320px-Mexico_City_New_Years_2013%21_%288333128248%29.jpg");
// await listItem(listStack, 2, "Octavia E. Butler");
// await listItem(listStack, 3, "Octavia E. Butler");
// await listItem(listStack, 4, "Octavia E. Butler");
widget.addSpacer();
return widget;
}
async function listItem(listStack, rank, title, description = "Some description", views = 0, thumbnail = SFSymbol.named("xmark.square.fill").image) {
const itemStack = listStack.addStack();
itemStack.layoutHorizontally();
itemStack.centerAlignContent();
const rankSymbol = SFSymbol.named(rank + ".circle");
rankSymbol.applyThinWeight();
const rankImage = rankSymbol.image;
const rankWidgetImage = itemStack.addImage(rankImage);
rankWidgetImage.tintColor = PREFS.rankColors[rank - 1];
rankWidgetImage.imageSize = new Size(28, 28);
const infoStack = itemStack.addStack();
infoStack.layoutVertically();
infoStack.setPadding(4, 15, 4, 0);
infoStack.spacing = 4;
const itemTitle = infoStack.addText(title);
itemTitle.font = Font.semiboldSystemFont(16);
itemTitle.textColor = PREFS.foregroundPrimary;
const itemDescription = infoStack.addText(description);
itemDescription.font = Font.regularSystemFont(14);
itemDescription.textColor = PREFS.foregroundSecondary;
//infoStack.addSpacer(0);
const statsStack = infoStack.addStack();
statsStack.backgroundColor = PREFS.statsBackground;
statsStack.cornerRadius = 4;
statsStack.size = new Size(0, 18);
statsStack.centerAlignContent();
statsStack.setPadding(4, 4, 4, 4);
// Graph would go here...
//statsStack.addSpacer(32);
const itemViews = statsStack.addText(formatNumber(views) + "");
itemViews.font = Font.regularSystemFont(12);
itemViews.textColor = PREFS.statsForeground;
itemStack.addSpacer();
const thumbnailImage = itemStack.addImage(thumbnail);
thumbnailImage.cornerRadius = 8;
thumbnailImage.applyFillingContentMode();
thumbnailImage.imageSize = new Size(60, 60);
}
async function loadThumbnail(url) {
let request = new Request(url);
return request.loadImage();
}
async function getTopRead(maximum) {
const apiUrl = 'https://en.wikipedia.org/api/rest_v1/feed/featured/';
// Get current UTC date
const currentDate = new Date().toISOString().split('T')[0].replace(/-/g, '/');
// Append the current date to the API URL
const fullApiUrl = `${apiUrl}${currentDate}`;
const request = new Request(fullApiUrl);
const response = await request.loadJSON(fullApiUrl)
const articles = response.mostread.articles;
// Sort articles by views in descending order
articles.sort((a, b) => b.views - a.views);
// Get the top articles up until maximum
const topArticles = articles.slice(0, maximum);
return topArticles;
}
function formatNumber(n) {
const ranges = [
{ divider: 1e6 , suffix: 'M' },
{ divider: 1e3 , suffix: 'k' }
];
for (let i = 0; i < ranges.length; i++) {
if (n >= ranges[i].divider) {
n = (n / ranges[i].divider);
n = Math.round(n * 10) / 10;
return n.toString() + ranges[i].suffix;
}
}
return n.toString();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment