v1.1.0: English UI + docs, bundled Bitcoin icon, canonical config page, gitea.a14r.be website URL, journal diagnostics

This commit is contained in:
ArnoldCordewiner
2026-09-05 20:07:12 +02:00
parent e3934f33b6
commit d3641469b0
8 changed files with 160 additions and 85 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import org.kde.plasma.configuration 2.0
ConfigModel {
ConfigCategory {
name: i18n("Algemeen")
name: i18n("General")
icon: "preferences-desktop"
source: "configGeneral.qml"
}
+22 -3
View File
@@ -1,16 +1,35 @@
/*
* Config page for nl.a14r.bitcoinprice.
* Follows the canonical Plasma 5 config dialog pattern:
* Kirigami.FormLayout + cfg_ prefixed property aliases.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.5 as Kirigami
Kirigami.FormLayout {
id: generalPage
// The cfg_ prefix is what Plasma uses to read/write plasmoid.configuration
property alias cfg_refreshMinutes: intervalSpin.value
SpinBox {
Item {
Kirigami.FormData.isSection: true
}
QQC2.SpinBox {
id: intervalSpin
Kirigami.FormData.label: i18n("Refresh interval (minutes):")
from: 5
to: 1440
stepSize: 5
editable: true
Kirigami.FormData.label: i18n("Vernieuwinterval (minuten):")
}
QQC2.Label {
text: i18n("CoinGecko allows one request per 5 minutes; lower values are not possible.")
font.italic: true
opacity: 0.7
}
}
+66 -35
View File
@@ -1,14 +1,14 @@
/*
* nl.a14r.bitcoinprice — KDE Plasma 5 widget
*
* Toont de bitcoinprijs in EUR (CoinGecko API).
* - Groen : prijs gestegen t.o.v. vorige meting
* - Rood : prijs gedaald t.o.v. vorige meting
* - Grijs : gelijk gebleven (of eerste meting)
* - "--" : API-fout (netwerkfout, 4xx/5xx, rate limit 429)
* Shows the bitcoin price in EUR (CoinGecko API).
* - Green : price up compared to the previous reading
* - Red : price down compared to the previous reading
* - Grey : unchanged (or first reading)
* - "--" : API failure (network error, 4xx/5xx, 429 rate limit)
*
* Vernieuwinterval: configureerbaar, minimaal 5 minuten
* (CoinGecko limiet: 1 call per 5 minuten).
* Refresh interval: configurable, minimum 5 minutes
* (CoinGecko limit: 1 call per 5 minutes).
*/
import QtQuick 2.15
@@ -22,42 +22,47 @@ Item {
readonly property int refreshMinutes: Math.max(5, plasmoid.configuration.refreshMinutes)
readonly property string apiUrl: "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur"
// -1 = gedaald, 0 = gelijk / nog geen vorige meting, 1 = gestegen
// -1 = down, 0 = unchanged / no previous reading yet, 1 = up
property int trend: 0
property double lastPrice: NaN // laatste succesvol opgehaalde prijs
property double lastPrice: NaN // last successfully fetched price
property string state: "init" // init | ok | error
property string lastUpdated: "" // HH:mm:ss van laatste succesvolle update
property string lastUpdated: "" // HH:mm:ss of the last successful update
readonly property string displayText: {
if (state !== "ok" || isNaN(lastPrice)) {
return "--";
}
return "\u20BF " + Number(lastPrice).toLocaleString(Qt.locale(), "f", 0) + " \u20AC";
return Number(lastPrice).toLocaleString(Qt.locale(), "f", 0) + " \u20AC";
}
readonly property color priceColor: {
if (state !== "ok" || isNaN(lastPrice)) {
return PlasmaCore.Theme.disabledTextColor; // "--" bij fout/init
return PlasmaCore.Theme.disabledTextColor; // "--" on error/init
}
if (trend > 0) {
return "#2ecc71"; // groen
return "#2ecc71"; // green
}
if (trend < 0) {
return "#e74c3c"; // rood
return "#e74c3c"; // red
}
return "#9e9e9e"; // grijs
return "#9e9e9e"; // grey
}
readonly property string statusText: {
if (state === "error") {
return lastUpdated === ""
? i18n("API-fout")
: i18n("API-fout — laatste geldige prijs: %1", lastUpdated);
? i18n("API error")
: i18n("API error — last known price: %1", lastUpdated);
}
if (lastUpdated === "") {
return i18n("Bezig met ophalen\u2026");
return i18n("Fetching\u2026");
}
return i18n("Laatst bijgewerkt: %1", lastUpdated);
return i18n("Last updated: %1", lastUpdated);
}
Component.onCompleted: {
console.log("[bitcoinprice] started, refresh interval:",
refreshMinutes, "minutes");
}
Plasmoid.backgroundHints: PlasmaCore.Types.StandardBackground
@@ -70,22 +75,38 @@ Item {
Item {
id: compact
Layout.minimumWidth: priceLabel.implicitWidth + PlasmaCore.Units.smallSpacing * 2
Layout.minimumWidth: row.implicitWidth + PlasmaCore.Units.smallSpacing * 2
Layout.minimumHeight: PlasmaCore.Units.iconSizes.smallMedium
Text {
id: priceLabel
Row {
id: row
anchors.centerIn: parent
text: root.displayText
color: root.priceColor
textFormat: Text.PlainText
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: PlasmaCore.Theme.defaultFont.family
font.pixelSize: compact.height > PlasmaCore.Units.iconSizes.small
? Math.round(compact.height * 0.6)
: PlasmaCore.Theme.defaultFont.pixelSize
spacing: PlasmaCore.Units.smallSpacing
Image {
id: btcIcon
source: "../images/bitcoin.svg"
sourceSize.width: PlasmaCore.Units.iconSizes.smallMedium
sourceSize.height: PlasmaCore.Units.iconSizes.smallMedium
anchors.verticalCenter: parent.verticalCenter
fillMode: Image.PreserveAspectFit
visible: root.state === "ok"
}
Text {
id: priceLabel
anchors.verticalCenter: parent.verticalCenter
text: root.displayText
color: root.priceColor
textFormat: Text.PlainText
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: PlasmaCore.Theme.defaultFont.family
font.pixelSize: compact.height > PlasmaCore.Units.iconSizes.small
? Math.round(compact.height * 0.6)
: PlasmaCore.Theme.defaultFont.pixelSize
}
}
}
}
@@ -95,12 +116,21 @@ Item {
id: fullRep
implicitWidth: PlasmaCore.Units.gridUnit * 14
implicitHeight: PlasmaCore.Units.gridUnit * 7
implicitHeight: PlasmaCore.Units.gridUnit * 9
Column {
anchors.centerIn: parent
spacing: PlasmaCore.Units.smallSpacing
Image {
anchors.horizontalCenter: parent.horizontalCenter
source: "../images/bitcoin.svg"
sourceSize.width: PlasmaCore.Units.iconSizes.large
sourceSize.height: PlasmaCore.Units.iconSizes.large
fillMode: Image.PreserveAspectFit
visible: root.state === "ok"
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: root.displayText
@@ -141,14 +171,15 @@ Item {
try {
var price = JSON.parse(xhr.responseText).bitcoin.eur;
if (typeof price === "number" && !isNaN(price)) {
console.log("[bitcoinprice] fetched EUR", price);
root.applyPrice(price);
return;
}
} catch (e) {
// ongeldige JSON → val door naar de foutafhandeling
// invalid JSON → fall through to error handling
}
}
// API-fout (netwerk, 4xx/5xx, rate limit 429, onverwachte payload)
console.log("[bitcoinprice] fetch failed, HTTP status:", xhr.status);
root.state = "error";
};
xhr.send();