fix(ui): correct supporter data and labels

This commit is contained in:
Hakan Seven committed 2026-08-03 22:08:22 +03:00
1 parent 2c5b7e76fb
commit feb110404c
4 files changed
+89 -17

No files matched your search

+19 -7
View File
@@ -7,8 +7,6 @@ run-name: ${{ github.event.release.tag_name || github.ref_name }} Web
on:
release:
types: [published]
discussion:
types: [created, edited, deleted, transferred, pinned, unpinned, labeled, unlabeled, locked, unlocked, category_changed, answered, unanswered]
workflow_dispatch:
permissions:
@@ -81,11 +79,25 @@ jobs:
echo '[]' > dist/supporters.json; exit 0
fi
curl -sf -H "Authorization: Bearer $OCS_PATREON_TOKEN" \
"https://www.patreon.com/api/oauth2/v2/campaigns/$CID/members?fields%5Bmember%5D=full_name,patron_status,currently_entitled_amount_cents&page%5Bcount%5D=200" \
| jq -c '[.data[]
| select(.attributes.patron_status=="active_patron")
| select(.attributes.currently_entitled_amount_cents>0)
| {name: .attributes.full_name, cents: .attributes.currently_entitled_amount_cents}]
"https://www.patreon.com/api/oauth2/v2/campaigns/$CID/members?include=pledge_history&fields%5Bmember%5D=full_name,patron_status,currently_entitled_amount_cents&fields%5Bpledge-event%5D=amount_cents,date,payment_status&page%5Bcount%5D=200" \
| jq -c '
(.included // []
| map(select(.type == "pledge-event"))
| map({key: .id, value: .attributes})
| from_entries) as $events
| [.data[]
| select(.attributes.patron_status == "active_patron")
| . as $member
| ([($member.relationships.pledge_history.data // [])[]
| $events[.id]
| select(.payment_status == "Paid")
| select(.amount_cents > 0)]
| sort_by(.date)
| last) as $last_paid
| {name: ((.attributes.full_name // "") | gsub("^\\s+|\\s+$"; "")),
cents: ($last_paid.amount_cents // .attributes.currently_entitled_amount_cents)}
| select(.name != "")
| select(.cents > 0)]
| sort_by(-.cents)' \
> dist/supporters.json || echo '[]' > dist/supporters.json
echo "supporters: $(jq 'length' dist/supporters.json)"
+8 -8
View File
@@ -582,10 +582,10 @@ catalog-browser-storage-73cbc39086 = Tarayıcı depolaması
catalog-building-scene-caches-3518c9d139 = Sahne önbellekleri oluşturuluyor…
catalog-bump-map-301f0ab5fb = Kabartma Haritası
catalog-by-annotation-scale-0aeb536ce3 = Açıklama ölçeğine göre
catalog-by-layer-3243a14fc9 = Katmana göre
catalog-by-layer-3243a14fc9 = Katmandan
catalog-by-style-4697cfbbbf = Stile göre
catalog-byblock-a5106d0575 = Bloğa Göre
catalog-bylayer-c668f6e29f = Katmana Göre
catalog-byblock-a5106d0575 = Bloktan
catalog-bylayer-c668f6e29f = Katmandan
catalog-c-class-dffdc6893e = C++ Sınıfı
catalog-cad-application-for-architecture-engineering-79b5234416 = Mimarlık ve mühendislik için CAD uygulaması
catalog-cal-expression-e-g-2-3-4-9d91f1992e = CAL İfade (ör. (2+3)*4):
@@ -604,7 +604,7 @@ catalog-chamfer-enter-second-chamfer-distance-d2-2de574d864 = CHAMFER İkinci p
catalog-chamfer-select-first-line-d1-d1-d2-d2-3c27011b33 = CHAMFER Birinci çizgiyi seçin [D1=__ocs_arg_d1__ D2=__ocs_arg_d2__]:
catalog-chamfer-select-second-line-d1-d1-d2-d2-f2857f4ba8 = CHAMFER İkinci çizgiyi seçin [D1=__ocs_arg_d1__ D2=__ocs_arg_d2__]:
catalog-chamfer-select-the-adjacent-polyline-segment-d-0380882c9b = CHAMFER Bitişik çoklu çizgi parçasını seçin [D1=__ocs_arg_d1__ D2=__ocs_arg_d2__]:
catalog-chprop-new-colour-name-1-255-bylayer-93afe25036 = CHPROP Yeni renk (ad / 1-255 / Katmana Göre):
catalog-chprop-new-colour-name-1-255-bylayer-93afe25036 = CHPROP Yeni renk (ad / 1-255 / Katmandan):
catalog-chprop-new-layer-name-f0d35963a9 = CHPROP Yeni katman adı:
catalog-chprop-new-linetype-name-45684c2bb8 = CHPROP Yeni çizgi tipi adı:
catalog-chprop-new-linetype-scale-6c8dca5d8e = CHPROP Yeni çizgi tipi ölçeği:
@@ -633,7 +633,7 @@ catalog-clayer-new-current-layer-name-f0f6991d49 = CLAYER Yeni geçerli katman
catalog-clayer-cur-968bad6379 = CLAYER = "__ocs_fmt_0__"
catalog-clayer-set-to-name-arg-c1045e6576 = CLAYER "__ocs_fmt_0__" olarak ayarlandı.
catalog-clayer-layer-not-found-fb5d0a4e1f = CLAYER: '__ocs_fmt_0__' katmanı bulunamadı.
catalog-color-new-object-colour-bylayer-byblock-1-255-fb9a12e441 = COLOR Yeni nesne rengi [Katmana Göre / Bloğa Göre / 1-255 / kırmızı / sarı / yeşil / camgöbeği / mavi / macenta / beyaz]:
catalog-color-new-object-colour-bylayer-byblock-1-255-fb9a12e441 = COLOR Yeni nesne rengi [Katmandan / Bloktan / 1-255 / kırmızı / sarı / yeşil / camgöbeği / mavi / macenta / beyaz]:
catalog-colorscheme-unknown-theme-type-colorscheme-lis-8bb80164f7 = COLORSCHEME: bilinmeyen tema '__ocs_fmt_0__'. Seçenekler için COLORSCHEME LIST yazın.
catalog-convtosurface-converted-n-solid-s-to-surface-s-1f4c640ada = CONVTOSURFACE: __ocs_fmt_0__ katı yüzeye dönüştürüldü.
catalog-convtosurface-select-a-solid-created-this-sess-8a3ac96200 = CONVTOSURFACE: bu oturumda oluşturulmuş bir katı seçin.
@@ -1447,7 +1447,7 @@ catalog-line-specify-first-point-c01dd86573 = LINE İlk noktayı belirtin:
catalog-line-specify-next-point-close-undo-2db6e2e095 = LINE Sonraki noktayı belirtin [Kapat/Geri Al]:
catalog-line-specify-next-point-undo-ade411d556 = LINE Sonraki noktayı belirtin [Geri Al]:
catalog-linetype-list-set-4a097128f4 = LINETYPE [Listele / Ayarla]:
catalog-linetype-set-linetype-name-bylayer-byblock-08f8a9af56 = LINETYPE SET Çizgi tipi adı (Katmana Göre / Bloğa Göre / …):
catalog-linetype-set-linetype-name-bylayer-byblock-08f8a9af56 = LINETYPE SET Çizgi tipi adı (Katmandan / Bloktan / …):
catalog-linetype-name-is-not-loaded-use-linetype-list-6299789ccf = LINETYPE: "__ocs_fmt_0__" yüklü değil. Kullanılabilir çizgi tipleri için LINETYPE LIST komutunu kullanın.
catalog-list-no-entities-selected-select-entities-firs-84fa189d6b = LIST: Nesne seçilmedi. Önce nesneleri seçin.
catalog-loft-select-first-cross-section-53660582e4 = LOFT İlk kesiti seçin:
@@ -2314,7 +2314,7 @@ catalog-section-solid-modelling-is-unavailable-in-this-da202cedf5 = SECTION: Bu
catalog-section-the-plane-does-not-cross-the-solid-dbf07e9f4b = SECTION: Düzlem katıyı kesmiyor.
catalog-selectall-object-s-selected-1f84d4cdcd = SELECTALL: __ocs_fmt_0__ nesne seçildi.
catalog-selhandles-no-entities-selected-select-entitie-4a7c8d016c = SELHANDLES: Nesne seçilmedi. Önce nesneleri seçin.
catalog-setbylayer-reset-changed-entity-entities-to-by-a875faf5fc = SETBYLAYER: __ocs_fmt_0__ nesne Katmana Göre olarak sıfırlandı.
catalog-setbylayer-reset-changed-entity-entities-to-by-a875faf5fc = SETBYLAYER: __ocs_fmt_0__ nesne Katmandan özelliklerine sıfırlandı.
catalog-setbylayer-select-entities-first-07cb922ea6 = SETBYLAYER: Önce nesneleri seçin.
catalog-setvar-new-value-blank-to-read-99412ea9e4 = SETVAR Yeni değer (okumak için boş bırakın):
catalog-setvar-variable-name-c09f71bfdc = SETVAR Değişken adı:
@@ -3225,7 +3225,7 @@ dimstyle-when-text-is-moved = Metin taşındığında
dimstyle-zero-suppression = Sıfırları gizleme
dimstyle-add-a-leader = Kılavuz ekle
dimstyle-automatic = Otomatik
dimstyle-by-block = Bloğa göre
dimstyle-by-block = Bloktan
dimstyle-comma = Virgül
dimstyle-degrees-minutes-seconds = Derece, dakika, saniye
dimstyle-keep-dimension-line-with-text = Ölçü çizgisini metinle birlikte taşı
+4
View File
@@ -2738,6 +2738,10 @@ fn start_page_content<'a>(
Space::new().height(iced::Length::Fixed(12.0)),
]
.spacing(6)
.padding(iced::Padding {
right: 12.0,
..iced::Padding::ZERO
})
.width(Fill);
for (name, cents) in patrons {
// Cents → the campaign currency's main unit. Symbol assumed "$";
+58 -2
View File
@@ -60,12 +60,18 @@ pub fn fetch_patrons() -> Result<Vec<(String, i64)>, String> {
let mut patrons: Vec<(String, i64)> = Vec::new();
let mut url = format!(
"https://www.patreon.com/api/oauth2/v2/campaigns/{campaign_id}/members\
?fields%5Bmember%5D=full_name,patron_status,currently_entitled_amount_cents\
?include=pledge_history\
&fields%5Bmember%5D=full_name,patron_status,currently_entitled_amount_cents\
&fields%5Bpledge-event%5D=amount_cents,date,payment_status\
&page%5Bcount%5D=200"
);
// Bound the loop so a malformed `next` link can never spin forever.
for _ in 0..50 {
let page = get_json(&agent, token, &url)?;
let included = page["included"]
.as_array()
.map(Vec::as_slice)
.unwrap_or(&[]);
if let Some(arr) = page["data"].as_array() {
for m in arr {
let attrs = &m["attributes"];
@@ -75,7 +81,15 @@ pub fn fetch_patrons() -> Result<Vec<(String, i64)>, String> {
if attrs["patron_status"].as_str() != Some("active_patron") {
continue;
}
let cents = attrs["currently_entitled_amount_cents"].as_i64().unwrap_or(0);
// The entitlement can retain the previous tier amount after a
// pledge change. The latest successful pledge event records
// what was actually paid; retain the entitlement as a fallback
// for memberships whose history is unavailable.
let cents = latest_paid_amount(m, included).unwrap_or_else(|| {
attrs["currently_entitled_amount_cents"]
.as_i64()
.unwrap_or(0)
});
if cents <= 0 {
continue;
}
@@ -96,6 +110,48 @@ pub fn fetch_patrons() -> Result<Vec<(String, i64)>, String> {
Ok(patrons)
}
#[cfg(not(target_arch = "wasm32"))]
fn latest_paid_amount(
member: &serde_json::Value,
included: &[serde_json::Value],
) -> Option<i64> {
let history = member["relationships"]["pledge_history"]["data"].as_array()?;
let mut latest: Option<(String, i64)> = None;
for relationship in history {
let Some(id) = relationship["id"].as_str() else {
continue;
};
let Some(resource_type) = relationship["type"].as_str() else {
continue;
};
let Some(event) = included.iter().find(|entry| {
entry["id"].as_str() == Some(id)
&& entry["type"].as_str() == Some(resource_type)
}) else {
continue;
};
let attrs = &event["attributes"];
if attrs["payment_status"].as_str() != Some("Paid") {
continue;
}
let cents = attrs["amount_cents"].as_i64().unwrap_or(0);
if cents <= 0 {
continue;
}
let date = attrs["date"].as_str().unwrap_or("");
if latest
.as_ref()
.map(|(current, _)| date > current.as_str())
.unwrap_or(true)
{
latest = Some((date.to_string(), cents));
}
}
latest.map(|(_, cents)| cents)
}
/// Web build: the browser can't call the Patreon API directly (CORS + the
/// token would be exposed in the bundle), so it fetches a pre-generated
/// `supporters.json` published next to the app on the same origin (produced by