-
Notifications
You must be signed in to change notification settings - Fork 0
/
KPop Comebacks.scriptable
12 lines (11 loc) · 8.2 KB
/
KPop Comebacks.scriptable
1
2
3
4
5
6
7
8
9
10
11
12
{
"always_run_in_app" : false,
"icon" : {
"color" : "pink",
"glyph" : "calendar-alt"
},
"name" : "KPop Comebacks",
"script" : "\/\/ KPop Comebacks Widget by heismauri (v1.2.2)\n\nconst startWidget = async () => {\n \/\/ Variables\n const scriptVersion = '1.2.5';\n const PADDING = 15;\n const baseURL = 'https:\/\/raw.githubusercontent.com\/heismauri\/kpop-comebacks-widget\/main';\n\n \/\/ Utilities\n const addLeadingZero = (number) => {\n return `0${number}`.slice(-2);\n };\n\n const formatAMPM = (date) => {\n let hours = date.getHours();\n const minutes = addLeadingZero(date.getMinutes());\n const ampm = hours >= 12 ? 'PM' : 'AM';\n hours = (hours %= 12) || 12;\n hours = addLeadingZero(hours);\n return `${hours}:${minutes}${ampm}`;\n };\n\n const formatTime = (date) => {\n const hours = addLeadingZero(date.getHours());\n const minutes = addLeadingZero(date.getMinutes());\n return `${hours}:${minutes}`;\n };\n\n const formatDate = (date) => {\n const day = addLeadingZero(date.getDate());\n const month = addLeadingZero(date.getMonth() + 1);\n return [[day, month].join('.'), formatAMPM(date)];\n };\n\n const getKeyByValue = (object, value) => {\n return Object.keys(object).find((key) => Object.values(object[key]).indexOf(value) > -1);\n };\n\n const groupByDateAndLimit = (comebacks, limit) => {\n return comebacks.slice(0, limit).reduce((accumulator, comeback) => {\n const key = comeback.date;\n accumulator[key] = accumulator[key] || [];\n accumulator[key].push(comeback.title);\n return accumulator;\n }, {});\n };\n\n \/\/ HTML Preview\n const encodeString = (text) => {\n return text.replace(\/[\\u00A0-\\u9999<>&]\/gim, (i) => `&#${i.charCodeAt(0)};`);\n };\n\n const comebacksToHTML = (comebacks) => {\n return Object.keys(comebacks).map((day) => {\n const [date, time] = formatDate(new Date(parseInt(day, 10)));\n const title = `<ul class=\"calendar-day\"><li class=\"calendar-day-date\"><strong>${date}<\/strong><i>${time}<\/i><\/li>`;\n const listElements = comebacks[day].map((comeback) => {\n return `<li>${encodeString(comeback)}<\/li>`;\n });\n return [title, '<ul class=\"calendar-day-events\">', ...listElements, '<\/ul><\/li><\/ul>'].join('');\n }).join('');\n };\n\n const HTMLBuilder = (comebacks) => {\n return `\n <!DOCTYPE html>\n <html>\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>KPop Upcoming Comebacks<\/title>\n <style>\n *{box-sizing:border-box}body{margin:0;color:#3c4142;font-size:1rem;font-family:-apple-system,'SF Pro Text','SF Pro Icons','Helvetica Neue','Helvetica','Arial',sans-serif;line-height:1.5}.calendar-day{margin:1rem 0;padding:0;list-style-type:none}.calendar-day-events{padding-left:2rem}.container{width:100%;max-width:30rem;padding:0 2rem;margin:0 auto}h1{color:#ce5891;text-transform:uppercase;margin-top:0;margin-bottom:.5rem;font-weight:bold;font-size:1rem}.calendar-day-date i{font-style:normal;margin-left:.25rem;opacity:.5}@media(prefers-color-scheme:dark){body{background:#201c1c;color:#fff}}\n <\/style>\n <\/head>\n <body>\n <div class=\"container\">\n <h1>🫰 KPop Upcoming Comebacks<\/h1>\n ${comebacksToHTML(comebacks)}\n <\/div>\n <\/body>\n <\/html>\n `;\n };\n\n \/\/ Cache API\n const fm = FileManager.iCloud();\n const dir = fm.joinPath(fm.documentsDirectory(), 'kpop-comebacks-cache');\n if (!fm.fileExists(dir)) fm.createDirectory(dir);\n const apiCache = fm.joinPath(dir, 'api.json');\n\n \/\/ Get comebacks\n const getComebacksAPI = async () => {\n const request = new Request('https:\/\/kpop-comebacks.heismauri.com\/api');\n const json = await request.loadJSON();\n fm.writeString(apiCache, JSON.stringify(json));\n };\n\n const getComebacks = async (limit) => {\n if (!fm.fileExists(apiCache)) await getComebacksAPI();\n await fm.downloadFileFromiCloud(apiCache);\n const content = await JSON.parse(fm.readString(apiCache));\n const timestamp = content.at(0).date;\n const oneHourAgo = new Date() - (3.6 * 10 ** 6);\n if (new Date(oneHourAgo) > new Date(parseInt(timestamp, 10))) {\n await getComebacksAPI();\n await getComebacks();\n }\n return groupByDateAndLimit(content, limit);\n };\n\n \/\/ Set limit based on Widget size\n let limit;\n switch (config.widgetFamily) {\n case 'small': {\n limit = 2;\n break;\n }\n case 'medium': {\n limit = 3;\n break;\n }\n default: {\n limit = 10;\n }\n }\n \/\/ Ignore limit when script is run inside Scriptable\n if (config.runsInWidget) limit = parseInt(args.widgetParameter, 10) || limit;\n\n \/\/ Run get comebacks\n const comebacks = await getComebacks(limit);\n\n \/\/ Widget\n const widget = new ListWidget();\n widget.setPadding(PADDING, 12, PADDING, PADDING);\n widget.backgroundColor = Color.dynamic(new Color('#ffffff'), new Color('#201c1c'));\n\n const widgetTitleText = (config.widgetFamily === 'small') ? 'COMEBACKS' : 'UPCOMING COMEBACKS';\n const widgetTitle = widget.addText(`🫰 KPOP ${widgetTitleText}`);\n widgetTitle.font = Font.semiboldSystemFont(12);\n widgetTitle.textColor = new Color('#ce5891');\n\n const mainStack = widget.addStack();\n mainStack.setPadding(0, 3, 0, 0);\n mainStack.topAlignContent();\n mainStack.layoutVertically();\n\n mainStack.addSpacer();\n\n \/\/ Print each comeback by day\n Object.keys(comebacks).forEach((day) => {\n const dateStack = mainStack.addStack();\n dateStack.layoutHorizontally();\n const [date, time] = formatDate(new Date(parseInt(day, 10)));\n const dateText = dateStack.addText(date);\n dateText.font = Font.semiboldSystemFont(13);\n dateStack.addSpacer(4);\n const timeText = dateStack.addText(time);\n timeText.font = Font.regularSystemFont(13);\n timeText.textOpacity = 0.5;\n const spaceBetweenDates = Object.keys(comebacks).at(-1) === day ? 0 : 4;\n comebacks[day].forEach((comeback) => {\n const comebackStack = mainStack.addStack();\n const comebackText = comebackStack.addText(comeback);\n comebackText.font = Font.regularSystemFont(13);\n });\n mainStack.addSpacer(spaceBetweenDates);\n });\n\n mainStack.addSpacer();\n\n \/\/ Config Utilities\n \/\/ \/\/ Generate Alert\n const generateAlert = async (message, options = []) => {\n const alert = new Alert();\n alert.message = message;\n options.forEach((option) => {\n alert.addAction(option);\n });\n alert.addCancelAction('Close');\n return alert.presentAlert();\n };\n\n \/\/ \/\/ Update checker\n const updateChecker = async () => {\n try {\n const request = new Request(`${baseURL}\/package.json`);\n const json = await request.loadJSON();\n return (json.version !== scriptVersion);\n } catch (error) {\n return false;\n }\n };\n\n \/\/ Preview all comebacks, Clear cache, and update script\n if (!config.runsInWidget) {\n const options = ['View all upcoming comebacks', 'Clear cache'];\n if (await updateChecker()) options.push('Update now!');\n const response = await generateAlert('What would you like to do?', options);\n switch (response) {\n case 0: {\n const webView = new WebView();\n webView.loadHTML(HTMLBuilder(await getComebacks()));\n await webView.present();\n break;\n }\n case 1: {\n await getComebacksAPI();\n break;\n }\n case 2: {\n let message;\n try {\n const upstreamScript = new Request(`${baseURL}\/kpop-comebacks.js`);\n const kpopcomebacksScript = await upstreamScript.loadString();\n fm.writeString(module.filename, kpopcomebacksScript);\n message = 'The widget has been updated, please re-open Scriptable';\n } catch (error) {\n message = 'The update has failed, please try again later';\n }\n await generateAlert(message);\n break;\n }\n default: {\n break;\n }\n }\n }\n\n Script.setWidget(widget);\n Script.complete();\n};\n\nawait startWidget();\n",
"share_sheet_inputs" : [
]
}