Last active
February 29, 2024 20:28
-
-
Save andreasRedeker/51f35a841be868e7224da514381a2075 to your computer and use it in GitHub Desktop.
Parqet iOS Dividenden Widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: teal; icon-glyph: chart-bar; | |
// Script by Andreas Redeker <hello@andreasredeker.de> | |
let portfolioId; | |
const apiUrl = (portfolioId) => `https://api.parqet.com/v1/portfolios/${portfolioId}?timeframe=max&useInclude=true&include=xirr&include=dividend_growth&include=future_dividends`; | |
const IMAGE_SIZE = 32; | |
const CORNER_RADIUS = 16; | |
const LIST_SPACING = 8; | |
let MAX_LIST_LENGTH = 8; | |
const HOLDING_FONT = Font.mediumSystemFont(16); | |
const DATE_FONT = Font.mediumSystemFont(10); | |
const DATE_COLOR = Color.dynamic(Color.gray(), Color.gray()); | |
const PRICE_FONT = Font.mediumSystemFont(18); | |
const PRICE_COLOR = Color.dynamic(Color.green(), Color.green()); | |
if (config.runsInWidget && args.widgetParameter) { | |
portfolioId = args.widgetParameter; | |
} else if (config.runsInWidget && !args.widgetParameter) { | |
throw Error("Bitte Portfolio-Id in den Widget Parametern einfügen"); | |
} | |
if (config.runsInApp) { | |
portfolioId = "5f55425d139fc90007978e75"; // parqet demo portfolio | |
} | |
let widget = await createWidget(); | |
if (config.runsInApp) { | |
await widget.presentLarge(); | |
} | |
async function createWidget() { | |
let widget = new ListWidget(); | |
widget.url = 'parqetapp://'; | |
widget.backgroundColor = Color.dynamic(Color.white(), Color.black()); // light - dark color | |
if (config.widgetFamily == "medium") { | |
MAX_LIST_LENGTH = 3; | |
} | |
if (config.widgetFamily == "large") { | |
MAX_LIST_LENGTH = 8; | |
} | |
// refresh data after 12 hours | |
widget.refreshAfterDate = new Date(Date.now() + 43200); | |
let portfolio = await loadPortfolio(portfolioId); | |
if (portfolio.length < 8) { | |
let title = widget.addText("Angekündigte Dividenden"); | |
title.font = Font.boldSystemFont(16); | |
title.color = Color.gray(); | |
widget.addSpacer(12) | |
} | |
let vStack = widget.addStack(); | |
vStack.layoutVertically(); | |
vStack.spacing = LIST_SPACING; | |
for (let holding of portfolio.slice(0, MAX_LIST_LENGTH)) { | |
let hStack = vStack.addStack(); | |
hStack.layoutHorizontally(); | |
// svg's are not supported by widgets | |
if (holding.logo && !holding.logo.endsWith('.svg')) { | |
const image = await loadImage(holding.logo); | |
let img = hStack.addImage(image); | |
img.imageSize = new Size(IMAGE_SIZE, IMAGE_SIZE); | |
img.cornerRadius = CORNER_RADIUS; | |
} else { | |
console.log("Logo is svg / could not find logo for " + holding.name + " " + holding.security); | |
const label = hStack.addStack(); | |
label.size = new Size(IMAGE_SIZE, IMAGE_SIZE); | |
label.centerAlignContent(); | |
label.backgroundColor = Color.dynamic(Color.lightGray(), new Color("374151")); | |
label.cornerRadius = CORNER_RADIUS; | |
let labelChar = label.addText(holding.name[0]); | |
labelChar.font = Font.boldRoundedSystemFont(20); | |
labelChar.textColor = Color.dynamic(Color.white(), Color.white()); | |
} | |
hStack.addSpacer(8); | |
let nameDateStack = hStack.addStack(); | |
nameDateStack.layoutVertically(); | |
let holdingName = nameDateStack.addText(holding.name); | |
holdingName.font = HOLDING_FONT; | |
holdingName.lineLimit = 1; | |
let dividendDate = nameDateStack.addText(toLocaleDateStringShort(holding.date)); | |
dividendDate.font = DATE_FONT; | |
dividendDate.textColor = DATE_COLOR; | |
hStack.addSpacer(); | |
let total = hStack.addText(toLocaleCurrencyString(holding.total)); | |
total.font = PRICE_FONT; | |
total.textColor = PRICE_COLOR; | |
} | |
return widget; | |
} | |
async function loadPortfolio(portfolioId) { | |
let portfolio = await new Request(apiUrl(portfolioId)).loadJSON(); | |
if (portfolio.statusCode == 401) { | |
throw Error("Portfolio ist nicht öffentlich"); | |
} | |
let holdings = portfolio.holdings; | |
let futureDividendsRawData = portfolio.futureDividends; | |
let futureDividends = []; | |
for (let fd of futureDividendsRawData) { | |
let holdingInfo = holdings.find((holding) => holding.security == fd.security); | |
futureDividends.push({ | |
security: fd.security, | |
date: fd.date, | |
total: fd.grossAmount, | |
shares: fd.exShares, | |
price: fd.price, | |
name: fd.asset.name, | |
logo: holdingInfo.logo, | |
}); | |
} | |
futureDividends.sort((a, b) => a.date > b.date); | |
return futureDividends; | |
} | |
function toLocaleCurrencyString(value) { | |
return value.toLocaleString("de-DE", { style: "currency", currency: "EUR" }); | |
} | |
function toLocaleDateStringShort(date) { | |
const options = { day: '2-digit', month: '2-digit', year: 'numeric' }; | |
return new Date(date).toLocaleDateString("de-DE", options); | |
} | |
async function loadImage(imgUrl) { | |
const response = new Request(imgUrl); | |
return await response.loadImage(); | |
} | |
async function showAlert(message, actions) { | |
let alert = new Alert(); | |
alert.message = message; | |
for (let action of actions) { | |
alert.addAction(action); | |
} | |
let response = await alert.presentAlert(); | |
return response; | |
} | |
Script.setWidget(widget); | |
Script.complete(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
iOS Scriptable Dividenden Widget für Parqet
Dein Portfolio muss öffentlich sein und die absoluten Werte anzeigen, damit das Widget funktioniert.
Installation