From 56a54e6e22e52e038c380793492bff91cc3d4508 Mon Sep 17 00:00:00 2001 From: privexlabs Date: Sat, 17 Oct 2026 07:14:56 -0700 Subject: [PATCH] fix: claim idempotency key, claimant state, bounty OG image, manifest --- public/icon-192.png | Bin 0 -> 5950 bytes public/icon-maskable.png | Bin 0 -> 19818 bytes src/app/issues/[id]/opengraph-image.tsx | 93 ++++++++++++++++++++++++ src/app/manifest.test.ts | 4 +- src/app/manifest.ts | 14 +++- src/components/bounty/ClaimButton.tsx | 15 ++++ src/hooks/useClaimRace.ts | 9 ++- 7 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 public/icon-192.png create mode 100644 public/icon-maskable.png create mode 100644 src/app/issues/[id]/opengraph-image.tsx diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..19cf0a4b21b5ad0aad6460382ee15351ef523290 GIT binary patch literal 5950 zcmYLNWn9%wu>Kzoori8XG}0x~9J;%a5F|yqOAja|EfUh564Kov5{K?CX(Xk&y!XSs zA7*EEc0TMrJHKakHbPTf0SA);6951lB}G~7XWjXqpd&qx%k6iX&kE$Otso6lj8g4A zPh_nPm2A}10Jdiv9RP*d1IYh{JPXCM001}}1c0A4=s#aJ(*Lb?W`qCV{x48XRQNXl zkQ6J)O6mH5jxrRAnfm?>m^c4xdv%+|Yk><67LLO3fSeb4kHlEr*A*Yw=d7?rs#FJEi=4(#^QLy;2Jl_15z=?YH&x_M9$pZx7s0*Rl6n zZpPt%`A}X_QJPd)aZJ~@D(>HWOM*dD{2KJu#@5!>=DNq($tkAeoN?=`*#3IdZDnXj z$U?0VNcst>u~-(JRlLz|?jncb-e?HJ22T2KA(*V9X{;DYLtN4R&5O?Ipaw3m;#%${ zrlHi|{mM}3cY~p;`q0-p+RIK228G2;<%MI~yJeXb%BBVD+|8&^E#hD9h)i`LmCrg` zOXUc_QIyIYyP(Bj>uDcGA!FV%s^SU`UEiKDH zaYFwaR$%%vfb=ThfR(b`?Rq!NG5Pu{7hMsQ^4;PQxt8MnbYOa)Vl@$L!Ft~dStNAR zhC?7H{bNlHCy?KT{B`GjZ3`zjPG2}fy@<2$)NtO=BVlLk&spUpNb(#D3#Zd6Io=8f zKK4}Aw1^@?9SJ2!(B)0E%b1bJIb zFXIhe{ld7a`nbfkZ*2IFL5bnpqH&Tbdi-WoILTxtXyLR139; zST_3fa@5&UrXQ9-PQuS`uCD^J5+P_4d3u#!*uao4XiIP!s*2C8=kxwl5WQZM(qcB+ zatZ*H`m%3xJT4Jz=NhL=9LfejD)d_(vKoAl)wbwn#+XVf{Po#D$DRWGMB{jYjU7HGiGWG-D1a8o3QQP7)kJVSjVCVzgj<6L0sUAa#fdnP(2B1A13rAuI* z{vBJdrme|xUJ<%|0T-bw^xIUWLTidBn?c)g#2fFB*sTO7LDF;TPJ|KI;-Am54xMst znY_S4QN(ECp?xO67Tj)Ce%e&z3jN0|_6peZiKQ(}VTOS-9Vo#kLRsd|h=31U0&96_ zzZ02Z;*-p96pzSMKe+*MG=I)^2e~58l-G0yUdXu2mknb=&)P|<5sGr+%XbzQZ*Kx z(|H0eElvzTb}6D$BX=Uu=0FG@PKe{$iw5zh8}q#>5T22uC+`Y3MC^W{%#ILcq1e`! zS^Vy)_iD+drwfhtz6Vc|G2zol)9LBGNO z+g15At@tRf3&*ToUPi9`Mgx|qTxq+E)LA(`-9MMJ$ z`FOxVb`7&37;t~h4{kMno$ufp7`**=}7vrCAaC@TQaT(A|U6(MPbYKgcHn>)XZl+ z`qc_LT`-xx*NXRQ-~hEZl}{;N=tYj8&bKil28vkRry(FHNx56%?(qEn9FgU-5dkt$ zJnVRSOustYE6O_4N>o-$Of0En@>6p7hB`qPrxbH82>lYBnRhz&mnhwo>Cg(mkuX8uRSAGW-n3(%)< zL3L!v7m*koVy#-wSQuV>kN-&<=~LtdlZXh23~`K(1|v z;8VAw@a2@wN&DS#cywD*ax$&&7QTreM4u3QFJ(+t)JF`yrammBopM)S?4n^OP>9mN#z<|U4i=|+L# zj{oixvg;IL%aCB3aBy;}?e8h{MG;uSvC~8z+4DjSJ;H2aUPgw2WF-XR)WcQJiADa&17Vw0( z;ls3W7b0Q^Vtm>gnd{ko7>6gV3qvRMgVgJy!XQlwt32DqyUK#r&{`%nQ{ z)V6XLZ5!k7=K1Z`LnM2l+OLPMg}zcK0?5{Z*jx-D0n7b=%DeE1VJUR=DoJzF+Y_c~}pl zt@zQ{u^KH)1TGAD3Hd<=7?9a<6%?FCHD`HDD9tZ274XV_>?1a?;vTsts7;{RFPte~ zgg&K?c<$(AWk|>Wd(Q92%~;8^zOC!K=j3n+t@IBO-KQIa1$r6Kry7Jt{2LZX+Ok)(K0ZdMWNC9;wUVSr@grr+za6C6nAG|GzK6U0 zIv2XD$uarq`tEsyZ>$Q7CPH6=w8ETH&BlG*qk$(SUEv}$ zJ@OoDk!n6+oIAIk_7|HBwSq&h#ZP+)M|lM8(4TkD9~tT2WV@jh>9YKEdr--@&R4=^ z;#WUFXo!YXB@RjeA+SeN`~hjfVNcRoY7`!&3hR}Df&r*|#B{A33Y;RkP^BDG!XdP6 zj-pjHTz2+y(YeFar3(e*i^~Wf>6X@UY+9p%lX*Tp!;#BmyrVN7?A-An=zc;Cj}ZXP z6$4{SR5{w-`n`h#e=jCA_zTKZ!+T32-`MyZyt#|df92vih(dV5W06^-wV_yhFBYTL zYOv<|$)5t9bROmnqM5Fb^F@6EfKH(-6Q+P zvdgCH^2dkES)$+{e-jZBcTsIp6E93 zRwO$c#+BPWs>b7}=GRlcICOWhl8d54PqS~J-KWTEp9qXxOGa#twnX+d4 zwfq2NAFD43iShzCZzeSQ#q}B5t0riST9RlmhK-xpWX8q$dt%78i7?=rs1uV;=rf5ND|39IvvvYSLA4Wa_fq@pyaCYGtD? zY^he0v_g_>R`0DPFCb&7Kj$RP*kZLn`53z!Vn=Of!^^mnLGoq%Cvou+PS0eB(a%2W zi|!fzQK8MEtPgw&jzm%zTy5`TNrUQt)7sO1aEufUDG;7~@RjZR>}%qUt3+oij26iN zza~*|(QXgm@6-EMu=OUfd<|8bbIvLuF=Mgbkw3l0D4+g>uU`3c#-}&e>QQ}hs8Rcb z!5RAlS%YaA)V_(GpKp@Q{2~-12H%eq{1k{|$T0#gBD(%e@Z)N5%lrF_C(kH`URjA3Q%cdj5?lIaE5}2uml`2dg4OmbN078eJJC1yg+Oa0w#|KR0eX z)4J16?_E{%UJ$DAWovaVc|ujd7`TZnF-_1>`MUISp1bWr8+SXO{8xW*za7pLLrJCQ z1s9h5_&meTHy!n^VuAQ-di#x`brZkQm;8TCZh3TX0X}z2Ceh)OrKSWeZ!+Wj)+lz$ za+!qrQ<}HGSaFbfVN7cVv@kp6pJktHrF=JZf0g?<;|m$$zOH}odr;nxg0F{Ru>eq9{j`~m*Z3qUF!uZR z6B`kYLPFJvQp++8(jD~s%eLE_KUqdG+8;KxIEF2#qlscbkls4uCohqsvyzLK6hk1v zbxrnlg%3UQwN_OQ1#^md_tE#lJQxIH@pj-iIZgc0>SQJ~#$gJXSa$(TaxA+0b11VGAVa{**;P4n>+QAC%>E66w)ZilN1 zisQ>_CHP{K)1rYN`1z9IC=&_FXU{a11wPx^Jc_bYBY%Vu;Q6V__$AQT(o?&euy#4$r6VAQi3!>#JQ9P~9;J8gsRK}- zFmqD#CSiobKZ;_UL@OF^J|N;H^<=ACY=={W-H0AV<|Wngz7>$mAw9Xm5V=!Qpn>nu zTN|wut&9!DwDfyF4DHG{eh8ctg2SHsv(gCwoiGDTP#E(yKfAv}8w2fcE5t4U!LDj0 z0*uEORPvd$X;5~8aA{O4akY`cZSpQ!d9C2rHDBS78ZyQ`5b}C2cJ)+7-QMHFFx8kb zGrBR`qZ zQ32xgsR1g_T{(s;q|~-K9wsm#W?2*&?Qjtt<+$ zfgI;wP#}5}K<-ZY%tzv5aq;?Xr`2!qUkI41NM}PXQDpi1k0FbQb)6NowIrjGJ!~6-%lkVUz)&vZS^s-xOxo}QlW*$Et zk-Cw%_ttVG6u-d`-FG!HB~R`t(wK|=~kjt-bJFtByJVQGUR-$w8SBT(3hg~$HD8o&%pKAOaP7wnWk8k z!>*QIGP7OwhsB$EO*tRiBd?*w*}(Q<^zem!-IJ8pW>GAvo%yv!K(O*5owNl~o;n(t zo|oSNV^U&(xd;DPNv}%Qg5f5>NYwMANw{vSe$mm@%TnCJO2|W2I~0i(nSuG0U>D@U zIIw@b%F|`sn!ZPuTGJ6V55)Lme7VCeFD6vWnngJo%698?!8MmN&7_;$hKU6*Mo(oJ zCuEOjic=G5*M%+G{<+h{=tL=(IRGH~YhyYI+~uG+9{ioNpxhM@)pO77A9T=$s05-~|fvgwzf_0VWia3sXQO zsE}@*$gVrU9S62vZ*Rn=qqI83s{N5DRG`Z5cLdfT^klzG35vxaWg%@|<~IY$`?_yT z*Kah2p0`yw1YkDk+~Ss_2TL8&xFIQ8$h}~ip44W%(AL|)Yb=}u(BEFMV6zO epc<7{JkpYTSnJp%bN}xzx00N?Y=!ij;Qs-mF9zEH literal 0 HcmV?d00001 diff --git a/public/icon-maskable.png b/public/icon-maskable.png new file mode 100644 index 0000000000000000000000000000000000000000..a116e2b7ea2bf326f473f4ab9111006ad188a0e7 GIT binary patch literal 19818 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 literal 0 HcmV?d00001 diff --git a/src/app/issues/[id]/opengraph-image.tsx b/src/app/issues/[id]/opengraph-image.tsx new file mode 100644 index 0000000..27f18c8 --- /dev/null +++ b/src/app/issues/[id]/opengraph-image.tsx @@ -0,0 +1,93 @@ +import { ImageResponse } from "next/og"; +import { fetchBounty } from "@/lib/api"; +import { mockBounties } from "@/lib/mock-data"; +import { formatCurrency } from "@/lib/utils"; + +export const alt = "MergeFi bounty"; +export const size = { width: 1200, height: 630 }; +export const contentType = "image/png"; + +export default async function BountyOpenGraphImage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + const { data: bounty } = await fetchBounty( + id, + mockBounties.find((b) => b.id === id), + ); + + const title = bounty?.title ?? "Bounty not found"; + const repo = bounty ? `${bounty.org}/${bounty.repo}` : "MergeFi"; + const reward = bounty ? formatCurrency(bounty.reward, bounty.asset) : ""; + const status = bounty ? bounty.status.replace(/_/g, " ").toUpperCase() : ""; + + return new ImageResponse( +
+
+
+ M +
+ MergeFi +
+
+
+ {repo} +
+
+ {title.length > 100 ? `${title.slice(0, 97)}...` : title} +
+
+
+
+ {reward} +
+
+ {status} +
+
+
, + size, + ); +} diff --git a/src/app/manifest.test.ts b/src/app/manifest.test.ts index e68eaee..ee196cd 100644 --- a/src/app/manifest.test.ts +++ b/src/app/manifest.test.ts @@ -8,9 +8,11 @@ describe("web app manifest", () => { start_url: "/", display: "standalone", background_color: "#0a0a0f", - theme_color: "#fbfbfd", + theme_color: "#0a0a0f", icons: expect.arrayContaining([ + expect.objectContaining({ src: "/icon-192.png", sizes: "192x192" }), expect.objectContaining({ src: "/icon.png", sizes: "512x512" }), + expect.objectContaining({ src: "/icon-maskable.png", purpose: "maskable" }), expect.objectContaining({ src: "/apple-icon.png", sizes: "180x180" }), ]), }); diff --git a/src/app/manifest.ts b/src/app/manifest.ts index 616bbbc..caf2d1b 100644 --- a/src/app/manifest.ts +++ b/src/app/manifest.ts @@ -9,14 +9,26 @@ export default function manifest(): MetadataRoute.Manifest { start_url: "/", display: "standalone", background_color: "#0a0a0f", - theme_color: "#fbfbfd", + theme_color: "#0a0a0f", icons: [ + { + src: "/icon-192.png", + sizes: "192x192", + type: "image/png", + purpose: "any", + }, { src: "/icon.png", sizes: "512x512", type: "image/png", purpose: "any", }, + { + src: "/icon-maskable.png", + sizes: "512x512", + type: "image/png", + purpose: "maskable", + }, { src: "/apple-icon.png", sizes: "180x180", diff --git a/src/components/bounty/ClaimButton.tsx b/src/components/bounty/ClaimButton.tsx index 3e11d4c..2be80ee 100644 --- a/src/components/bounty/ClaimButton.tsx +++ b/src/components/bounty/ClaimButton.tsx @@ -186,6 +186,21 @@ export function ClaimButton({ const needsPayoutWallet = !!user && !user.stellarAddress; if (status === 'claimed') { + const claimedById = bounty?.claimedById ?? fallbackBounty?.claimedById; + if (user && claimedById && user.id === claimedById) { + return ( +
+

You're working on this

+

+ Open a pull request that says Fixes #{bounty?.issueNumber ?? fallbackBounty?.issueNumber} in + its description. The reward is released automatically when it merges. +

+
+ ); + } return (

diff --git a/src/hooks/useClaimRace.ts b/src/hooks/useClaimRace.ts index 599ad4d..ba20a67 100644 --- a/src/hooks/useClaimRace.ts +++ b/src/hooks/useClaimRace.ts @@ -1,6 +1,7 @@ import { useState, useCallback, useEffect, useRef } from 'react'; import { apiPost, fetchBounty } from '@/lib/api'; import { adaptBounty, type RawBounty } from '@/lib/adapters'; +import { generateIdempotencyKey } from '@/lib/utils'; import type { Bounty, ClaimResult } from '@/types/bounty'; interface ApiError { @@ -16,6 +17,10 @@ export function useClaimRace(bountyId: string, onClaimSuccess?: (bounty: Bounty) // skip state updates once unmounted. Set to true in the effect body too so // React Strict Mode's simulated unmount/remount doesn't leave it false. const isMountedRef = useRef(true); + // POST /bounties/:id/claim requires an Idempotency-Key header. One key per + // claim intent: reused when a failed request is retried, rotated once the + // claim has a definitive outcome. + const idempotencyKeyRef = useRef(generateIdempotencyKey()); useEffect(() => { isMountedRef.current = true; @@ -35,7 +40,8 @@ export function useClaimRace(bountyId: string, onClaimSuccess?: (bounty: Bounty) setIsClaiming(true); try { - const raw = await apiPost(`/bounties/${bountyId}/claim`, {}); + const raw = await apiPost(`/bounties/${bountyId}/claim`, {}, idempotencyKeyRef.current); + idempotencyKeyRef.current = generateIdempotencyKey(); const bounty = adaptBounty(raw); const result: ClaimResult = { @@ -55,6 +61,7 @@ export function useClaimRace(bountyId: string, onClaimSuccess?: (bounty: Bounty) apiError?.message?.toLowerCase().includes('claimed by another user'); if (isAlreadyClaimed) { + idempotencyKeyRef.current = generateIdempotencyKey(); try { const updatedResult = await fetchBounty(bountyId, undefined); const result: ClaimResult = {