Skip to content

Instantly share code, notes, and snippets.

@andreasRedeker
Last active July 4, 2024 06:20
Show Gist options
  • Save andreasRedeker/51f35a841be868e7224da514381a2075 to your computer and use it in GitHub Desktop.
Save andreasRedeker/51f35a841be868e7224da514381a2075 to your computer and use it in GitHub Desktop.
Parqet iOS Dividenden Widget
// 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 <[email protected]>
let portfolioId;
const apiUrl = (portfolioId) => `https://api.parqet.com/v1/portfolios/${portfolioId}?timeframe=max&useInclude=true&include=xirr&include=dividend_growth&include=future_dividends`;
// Logos provided by Parqet: https://parqet.com/api
const imageUrl = (symbol) => `https://assets.parqet.com/logos/isin/${symbol}?format=png`;
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://';
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();
const logo = hStack.addStack();
logo.size = new Size(IMAGE_SIZE, IMAGE_SIZE);
logo.centerAlignContent();
logo.cornerRadius = CORNER_RADIUS;
logo.borderWidth = 1;
logo.borderColor = Color.dynamic(Color.lightGray(), new Color("374151"));
logo.backgroundColor = Color.dynamic(Color.white(), new Color("374151"));
try {
const image = await loadImage(imageUrl(holding.security));
let img = logo.addImage(image);
img.imageSize = new Size(IMAGE_SIZE, IMAGE_SIZE);
img.cornerRadius = CORNER_RADIUS;
} catch (err) {
let labelChar = logo.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();
@andreasRedeker
Copy link
Author

andreasRedeker commented Feb 29, 2024

iOS Scriptable Dividenden Widget für Parqet

Mockup

Dein Portfolio muss öffentlich sein und die absoluten Werte anzeigen, damit das Widget funktioniert.

Installation

  1. Lade die kostenlose Scriptable App aus dem iOS App Store herunter
  2. Kopiere den gesamten Code aus GitHub (siehe oben)
  3. Öffne die Scriptable App und drücke auf das Plus rechts oben
  4. Füge jetzt den Code ein und drücke links oben auf "Done"
  5. Gehe auf deinen Home Screen und füge das mittlere oder große Scriptable Widget hinzu
  6. Drücke auf das Widget und wähle bei Script den eben eingefügten Code aus
  7. Füge bei Parameter deine Portfolio-Id ein, z. B. aus dem Browser Link: https://app.parqet.com/p/5f55425d139fc90007978e75 Id = 5f55425d139fc90007978e75
  8. Nun das Bearbeiten beenden - Fertig!

Logos provided by Parqet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment