aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/app.js2
-rw-r--r--src/ark/manager.js5
-rw-r--r--src/features/ark.js45
-rw-r--r--src/i18n.js8
-rw-r--r--src/locales/ar.json8
-rw-r--r--src/locales/bn.json8
-rw-r--r--src/locales/de.json8
-rw-r--r--src/locales/es.json8
-rw-r--r--src/locales/fr.json8
-rw-r--r--src/locales/hi.json8
-rw-r--r--src/locales/id.json8
-rw-r--r--src/locales/ja.json8
-rw-r--r--src/locales/ko.json8
-rw-r--r--src/locales/mr.json8
-rw-r--r--src/locales/pt.json8
-rw-r--r--src/locales/ru.json8
-rw-r--r--src/locales/sw.json8
-rw-r--r--src/locales/ta.json8
-rw-r--r--src/locales/te.json8
-rw-r--r--src/locales/tr.json8
-rw-r--r--src/locales/ur.json8
-rw-r--r--src/locales/vi.json8
-rw-r--r--src/locales/zh.json8
-rw-r--r--tools/ark/renew-confirm-ui-test.js132
-rw-r--r--tools/ark/renew-txid-test.js45
25 files changed, 359 insertions, 30 deletions
diff --git a/src/app.js b/src/app.js
index 7b37ead..28cf9aa 100644
--- a/src/app.js
+++ b/src/app.js
@@ -3015,7 +3015,7 @@ function goHome() {
if (['screen', 'tab', 'msgView', 'msgCommunity', 'msgPeer', 'feedId'].includes(f)) continue;
ui[f] = ['chatOpen', 'giftMode', 'profEditFilled', 'profOverThread'].includes(f) ? false : null;
}
- ui.arkCoinsSel = null; ui.pos = null; ui.feedEdit = null; ui.feedMenu = null;
+ ui.arkCoinsSel = null; ui.arkRenewConfirm = null; ui.pos = null; ui.feedEdit = null; ui.feedMenu = null;
ui.tab = wallet.offline ? 'settings' : 'history';
ui.draft = null;
ui.sendResult = null;
diff --git a/src/ark/manager.js b/src/ark/manager.js
index a114702..1cf1d7a 100644
--- a/src/ark/manager.js
+++ b/src/ark/manager.js
@@ -2143,6 +2143,9 @@ export class ArkManager {
}
if (status.status === 0 || !status.fundingTx) return; // round pending; retry on next sync
const fundingTx = parseTx(status.fundingTx);
+ // The round has run: its transaction is on the network from here on,
+ // and is what the wait is for — kept so the wallet can link to it.
+ if (action.fundingTxid !== fundingTx.txid) { action.fundingTxid = fundingTx.txid; this._save(); }
const confirmed = await this.chain.getTxStatus(fundingTx.txid);
if (!confirmed?.confirmed) return; // wait for funding confirmations
action.fundingTxHex = hex.encode(status.fundingTx);
@@ -2185,7 +2188,7 @@ export class ArkManager {
// inputIds let the history skip "spent elsewhere" rows for coins this
// renewal consumed — reconciled before the claim, they'd otherwise
// stand as spends that the renewed coin silently contradicts.
- this._movement({ type: 'refresh', amountSat: action.outAmountSat, feeSat: action.feeSat || 0, manual: !!action.manual, status: 'complete', unlockHash: action.unlockHash, inputIds: [...(action.inputIds || [])], detail: `${inputRecs.length} in -> ${newVtxos.length} out${action.feeSat ? ` · fee ${action.feeSat} sat` : ''}` });
+ this._movement({ type: 'refresh', amountSat: action.outAmountSat, feeSat: action.feeSat || 0, manual: !!action.manual, status: 'complete', txid: fundingTx.txid, unlockHash: action.unlockHash, inputIds: [...(action.inputIds || [])], detail: `${inputRecs.length} in -> ${newVtxos.length} out${action.feeSat ? ` · fee ${action.feeSat} sat` : ''}` });
this._save();
}
}
diff --git a/src/features/ark.js b/src/features/ark.js
index 523a3a2..c25bae2 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -2215,8 +2215,8 @@ export function arkFeature(ctx) {
const pendingCoins = ((st && st.vtxos) || []).filter((v) => v.state === 'pending' && inRound.has(v.id));
const tip = (mgr && mgr._tipH) || 0;
const back = embedded
- ? () => { ui.arkCoinsSel = null; ctx.goHome(); }
- : () => ctx.goBack(() => { ui.arkCoinsPage = null; ui.arkCoinsSel = null; }); // the page is a history entry now
+ ? () => { ui.arkCoinsSel = null; ui.arkRenewConfirm = null; ctx.goHome(); }
+ : () => ctx.goBack(() => { ui.arkCoinsPage = null; ui.arkCoinsSel = null; ui.arkRenewConfirm = null; }); // the page is a history entry now
if (!spend.length && !pendingCoins.length) {
if (embedded) return h('div', { class: 'card col', style: 'gap:6px' }, h('h3', { style: 'margin:0' }, t('arkCoinsTitle')), h('p', { class: 'small muted', style: 'margin:0' }, t('arkCoinsNone')));
ui.arkCoinsPage = null; return null; // nothing left to manage
@@ -2296,8 +2296,33 @@ export function arkFeature(ctx) {
mgr.refresh(selCoins.map((v) => v.id), { manual: true }).catch((e) => toast(e.message));
toast(t('arkDepthRenewed'));
ui.arkCoinsSel = null;
+ ui.arkRenewConfirm = null;
render(); // the page stays: the coins show as renewing
};
+ // "Renew" asks once, with the numbers on the table: the coins it takes
+ // can't be spent until the round's transaction confirms, it can't be
+ // called back, and by default it takes every coin. (A user renewed their
+ // whole balance right after being paid and couldn't pay for an hour.)
+ if (ui.arkRenewConfirm && selCoins.length) {
+ const selSat = selCoins.reduce((n, v) => n + v.amountSat, 0);
+ const leftSat = spend.reduce((n, v) => n + v.amountSat, 0) - selSat;
+ const row = (k, v, cls = '') => h('div', { class: 'row between' }, h('span', { class: 'small muted' }, k), h('span', { class: 'small' + (cls ? ' ' + cls : '') }, v));
+ return h('div', { class: 'col ark-renew-confirm', style: 'gap:16px' },
+ h('div', { class: 'card col', style: 'gap:10px' },
+ h('h3', { style: 'margin:0' }, t('arkRenewConfirmTitle')),
+ h('p', { class: 'small muted', style: 'margin:0' }, t('arkRenewConfirmBody')),
+ h('div', { class: 'col', style: 'gap:4px' },
+ row(t('arkExitConfirmCoins'), String(selCoins.length)),
+ row(t('arkExitConfirmAmount'), fmtSats(selSat) + ' sats'),
+ row(t('arkRenewConfirmFee'), selFee > 0 ? fmtSats(selFee) + ' sats' : t('arkDepthFree')),
+ row(t('arkRenewConfirmLeft'), fmtSats(leftSat) + ' sats', leftSat ? '' : 'err')),
+ leftSat ? null : h('div', { class: 'notice err small' }, t('arkRenewConfirmAll')),
+ h('p', { class: 'small muted', style: 'margin:0' }, t('arkRenewConfirmWait')),
+ h('button', { class: 'btn-primary btn-block ark-renew-go', disabled: !!ui.arkBusy || !mgr, onClick: renew },
+ selFee > 0 ? t('arkCoinsRenewNowFee', { fee: fmtSats(selFee) + ' sats' }) : t('arkDepthRenewBtn'))),
+ h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.arkRenewConfirm = null; render(); } }, t('cancel')));
+ }
+ if (ui.arkRenewConfirm) ui.arkRenewConfirm = null; // nothing selected any more
const stageOf = (a) => a.step === 'issued' ? t('arkRenewStageFinishing') : a.step === 'submitted' ? t('arkRenewStageRound') : t('arkRenewStageQueued');
const renewingCard = renewing.length ? h('div', { class: 'card col', style: 'gap:8px' },
h('div', { class: 'row gap6', style: 'align-items:center' },
@@ -2305,6 +2330,13 @@ export function arkFeature(ctx) {
...renewing.map((a) => h('div', { class: 'col', style: 'gap:2px' },
h('div', { class: 'small' }, t('arkRenewingLine', { amount: fmtSats(a.inAmountSat || (a.outAmountSat + (a.feeSat || 0))), fee: a.feeSat ? t('arkRenewingFee', { fee: fmtSats(a.feeSat) }) : '' })),
h('div', { class: 'small muted' }, stageOf(a)),
+ // the round's transaction, once the round has run: the thing the
+ // wait is for, there to be looked at
+ a.fundingTxid ? (() => {
+ const url = wallet.api.explorerTx(a.fundingTxid);
+ return h('a', { class: 'linklike small ark-renew-tx', href: url, target: '_blank', rel: 'noopener',
+ onClick: (e) => { e.preventDefault(); openExternal(url); } }, t('arkRenewViewTx'));
+ })() : null,
// a check that failed is retried on the next sync; the reason is a
// server string nobody should have to read
a.lastError ? h('div', { class: 'small faint' }, t('arkRenewStageRetry')) : null))) : null;
@@ -2415,20 +2447,17 @@ export function arkFeature(ctx) {
...schedule.map((r) => h('div', { class: 'row between' },
h('span', { class: 'small muted' }, r.label),
h('span', { class: 'small' + (r.free ? ' faint' : '') }, r.cost)))) : null,
- !spend.length ? null : h('button', { class: 'btn-primary btn-block', disabled: !!ui.arkBusy || !selCoins.length || !mgr, onClick: renew },
+ !spend.length ? null : h('button', { class: 'btn-primary btn-block ark-renew-ask', disabled: !!ui.arkBusy || !selCoins.length || !mgr, onClick: () => { ui.arkRenewConfirm = true; render(); try { window.scrollTo({ top: 0 }); } catch {} } },
!selCoins.length ? t('arkCoinsRenewNone')
: selCoins.length < spend.length
? t('arkCoinsRenewSome', { n: selCoins.length, fee: selFee > 0 ? fmtSats(selFee) + ' sats' : t('arkDepthFree') })
- : selFee > 0 ? t('arkCoinsRenewNowFee', { fee: fmtSats(selFee) + ' sats' }) : t('arkDepthRenewBtn')),
- // said before the tap: a renewal takes its coins out of reach until
- // the round's transaction confirms, and by default it takes them all
- !spend.length ? null : h('div', { class: 'notice info small ark-renew-lock' }, t('arkCoinsRenewLock'))),
+ : selFee > 0 ? t('arkCoinsRenewNowFee', { fee: fmtSats(selFee) + ' sats' }) : t('arkDepthRenewBtn'))),
h('div', { class: 'card col', style: 'gap:8px' },
h('h4', { style: 'margin:0' }, t('arkCoinsCoopTitle')),
h('p', { class: 'small muted', style: 'margin:0' }, t('arkCoopDesc')),
h('button', { class: 'btn-primary btn-block', disabled: !!ui.arkBusy || !spendableSat || wallet.watchOnly, onClick: () => {
const address = wallet.freshReceive().address;
- ui.arkCoinsPage = null; ui.arkCoinsSel = null; ui.arkExitPage = null; ui.arkExitConfirm = null;
+ ui.arkCoinsPage = null; ui.arkCoinsSel = null; ui.arkExitPage = null; ui.arkExitConfirm = null; ui.arkRenewConfirm = null;
ui.arkOffboarded = null; ui.arkError = '';
ctx.showSend({ fresh: true });
ui.arkOffboardSend = { address, amount: String(spendableSat), fromCoins: true };
diff --git a/src/i18n.js b/src/i18n.js
index 4bdf2e6..1a27cfe 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1066,9 +1066,15 @@ const STR = {
arkLnPaying: 'Paying invoice…',
arkLnRetrying: 'Route failed. Retrying with a bigger fee budget ({n}/{of})…',
arkLnPayFailed: 'Payment failed',
+ arkRenewConfirmTitle: "Renew these coins?",
+ arkRenewConfirmBody: "Your wallet renews coins by itself, for free, when they are close to expiring. Renewing now is optional.",
+ arkRenewConfirmFee: "Renewal fee",
+ arkRenewConfirmLeft: "Left to spend meanwhile",
+ arkRenewConfirmAll: "This is your whole Spending balance. You won’t be able to pay or send from Spending until the renewal confirms.",
+ arkRenewConfirmWait: "Coins being renewed can’t be spent until the round’s transaction confirms on the Bitcoin network: usually about ten minutes, sometimes an hour or more. A renewal can’t be cancelled once it has started.",
+ arkRenewViewTx: "View the round’s transaction",
arkLnRenewing: "Nothing was sent. {sats} sats are being renewed and can’t be spent until the renewal confirms on the Bitcoin network: usually about ten minutes, sometimes an hour or more. Try again then.",
arkLnShort: "Not enough in Spending: this payment needs {need} sats with its fee, and {have} are available.",
- arkCoinsRenewLock: "Coins being renewed can’t be spent until the round’s transaction confirms on the Bitcoin network: usually about ten minutes, sometimes an hour or more. Renewing every coin leaves nothing to pay with in the meantime.",
arkLnRefunded: 'Payment failed. Your money is back in Spending.',
arkLnPaidTitle: 'Paid!',
or: 'or',
diff --git a/src/locales/ar.json b/src/locales/ar.json
index 2bce98d..c6c4451 100644
--- a/src/locales/ar.json
+++ b/src/locales/ar.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "الفيديوهات التي تزيد على {mb} ميغابايت أو تتجاوز 1080p تنتظر نقرة، حتى لا تستهلك الخلاصة بياناتك. الحد أقل على الاتصال البطيء أو عند توفير البيانات.",
"arkLnRenewing": "لم يُرسل شيء. {sats} ساتس قيد التجديد ولا يمكن إنفاقها حتى يتأكد التجديد على شبكة بيتكوين: عادة نحو عشر دقائق، وأحيانًا ساعة أو أكثر. حاول مجددًا بعد ذلك.",
"arkLnShort": "الرصيد غير كافٍ: هذه الدفعة تحتاج {need} ساتس مع رسومها، والمتاح {have}.",
- "arkCoinsRenewLock": "العملات قيد التجديد لا يمكن إنفاقها حتى تتأكد معاملة الجولة على شبكة بيتكوين: عادة نحو عشر دقائق، وأحيانًا ساعة أو أكثر. تجديد كل العملات لا يترك شيئًا للدفع في تلك الأثناء."
+ "arkRenewConfirmTitle": "تجديد هذه العملات؟",
+ "arkRenewConfirmBody": "محفظتك تجدّد العملات تلقائيًا ومجانًا عندما تقترب من انتهاء صلاحيتها. التجديد الآن اختياري.",
+ "arkRenewConfirmFee": "رسوم التجديد",
+ "arkRenewConfirmLeft": "المتبقي للإنفاق في الأثناء",
+ "arkRenewConfirmAll": "هذا كامل رصيد الإنفاق لديك. لن تتمكن من الدفع أو الإرسال منه حتى يتأكد التجديد.",
+ "arkRenewConfirmWait": "العملات قيد التجديد لا يمكن إنفاقها حتى تتأكد معاملة الجولة على شبكة بيتكوين: عادة نحو عشر دقائق، وأحيانًا ساعة أو أكثر. لا يمكن إلغاء التجديد بعد بدئه.",
+ "arkRenewViewTx": "عرض معاملة الجولة"
}
diff --git a/src/locales/bn.json b/src/locales/bn.json
index 27ab990..0856b8b 100644
--- a/src/locales/bn.json
+++ b/src/locales/bn.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "{mb} MB-এর বেশি বা 1080p-এর ওপরের ভিডিও ট্যাপের অপেক্ষায় থাকে, যাতে ফিড আপনার ডেটা না খায়। ধীর বা ডেটা-সেভার সংযোগে সীমা কম।",
"arkLnRenewing": "কিছুই পাঠানো হয়নি। {sats} sats নবায়ন হচ্ছে এবং নবায়ন Bitcoin নেটওয়ার্কে নিশ্চিত না হওয়া পর্যন্ত খরচ করা যাবে না: সাধারণত প্রায় দশ মিনিট, কখনও এক ঘণ্টা বা তার বেশি। তখন আবার চেষ্টা করুন।",
"arkLnShort": "যথেষ্ট নেই: এই পেমেন্টে ফি-সহ {need} sats লাগে, আর {have} পাওয়া যাচ্ছে।",
- "arkCoinsRenewLock": "নবায়নরত কয়েন রাউন্ডের লেনদেন Bitcoin নেটওয়ার্কে নিশ্চিত না হওয়া পর্যন্ত খরচ করা যায় না: সাধারণত প্রায় দশ মিনিট, কখনও এক ঘণ্টা বা তার বেশি। সব কয়েন নবায়ন করলে এর মধ্যে পেমেন্ট করার কিছু থাকে না।"
+ "arkRenewConfirmTitle": "এই কয়েনগুলো নবায়ন করবেন?",
+ "arkRenewConfirmBody": "মেয়াদ শেষের কাছাকাছি এলে আপনার ওয়ালেট নিজে থেকেই বিনামূল্যে কয়েন নবায়ন করে। এখন নবায়ন করা ঐচ্ছিক।",
+ "arkRenewConfirmFee": "নবায়ন ফি",
+ "arkRenewConfirmLeft": "এর মধ্যে খরচের জন্য থাকবে",
+ "arkRenewConfirmAll": "এটি আপনার পুরো খরচের ব্যালেন্স। নবায়ন নিশ্চিত না হওয়া পর্যন্ত এখান থেকে পেমেন্ট বা পাঠানো যাবে না।",
+ "arkRenewConfirmWait": "নবায়নরত কয়েন রাউন্ডের লেনদেন Bitcoin নেটওয়ার্কে নিশ্চিত না হওয়া পর্যন্ত খরচ করা যায় না: সাধারণত প্রায় দশ মিনিট, কখনও এক ঘণ্টা বা তার বেশি। শুরু হওয়া নবায়ন বাতিল করা যায় না।",
+ "arkRenewViewTx": "রাউন্ডের লেনদেন দেখুন"
}
diff --git a/src/locales/de.json b/src/locales/de.json
index da21d08..032bab5 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "Videos über {mb} MB oder über 1080p warten auf einen Tipp, damit der Feed nicht dein Datenvolumen frisst. Bei langsamer Verbindung oder Datensparmodus ist die Grenze niedriger.",
"arkLnRenewing": "Es wurde nichts gesendet. {sats} Sats werden gerade erneuert und können erst ausgegeben werden, wenn die Erneuerung im Bitcoin-Netzwerk bestätigt ist: meist etwa zehn Minuten, manchmal eine Stunde oder länger. Versuche es dann erneut.",
"arkLnShort": "Nicht genug im Ausgabenkonto: Diese Zahlung braucht mit Gebühr {need} Sats, verfügbar sind {have}.",
- "arkCoinsRenewLock": "Coins in Erneuerung können erst ausgegeben werden, wenn die Transaktion der Runde im Bitcoin-Netzwerk bestätigt ist: meist etwa zehn Minuten, manchmal eine Stunde oder länger. Wer alle Coins erneuert, kann in der Zwischenzeit nichts bezahlen."
+ "arkRenewConfirmTitle": "Diese Coins erneuern?",
+ "arkRenewConfirmBody": "Deine Wallet erneuert Coins von selbst und kostenlos, wenn sie bald ablaufen. Jetzt zu erneuern ist freiwillig.",
+ "arkRenewConfirmFee": "Erneuerungsgebühr",
+ "arkRenewConfirmLeft": "In der Zwischenzeit verfügbar",
+ "arkRenewConfirmAll": "Das ist dein gesamtes Ausgabenguthaben. Du kannst daraus nichts bezahlen oder senden, bis die Erneuerung bestätigt ist.",
+ "arkRenewConfirmWait": "Coins in Erneuerung können erst ausgegeben werden, wenn die Transaktion der Runde im Bitcoin-Netzwerk bestätigt ist: meist etwa zehn Minuten, manchmal eine Stunde oder länger. Eine begonnene Erneuerung lässt sich nicht abbrechen.",
+ "arkRenewViewTx": "Transaktion der Runde ansehen"
}
diff --git a/src/locales/es.json b/src/locales/es.json
index 1b6ed7b..a93e1af 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "Los vídeos de más de {mb} MB o por encima de 1080p esperan a que los toques, para que el feed no se coma tus datos. El límite es menor con una conexión lenta o en ahorro de datos.",
"arkLnRenewing": "No se envió nada. {sats} sats se están renovando y no pueden gastarse hasta que la renovación se confirme en la red Bitcoin: normalmente unos diez minutos, a veces una hora o más. Inténtalo entonces.",
"arkLnShort": "No hay suficiente en Gastos: este pago necesita {need} sats con su comisión, y hay {have} disponibles.",
- "arkCoinsRenewLock": "Las monedas en renovación no pueden gastarse hasta que la transacción de la ronda se confirme en la red Bitcoin: normalmente unos diez minutos, a veces una hora o más. Renovar todas las monedas no deja nada con que pagar mientras tanto."
+ "arkRenewConfirmTitle": "¿Renovar estas monedas?",
+ "arkRenewConfirmBody": "Tu billetera renueva las monedas sola y gratis cuando están cerca de expirar. Renovar ahora es opcional.",
+ "arkRenewConfirmFee": "Comisión de renovación",
+ "arkRenewConfirmLeft": "Queda para gastar mientras tanto",
+ "arkRenewConfirmAll": "Es todo tu saldo de Gastos. No podrás pagar ni enviar desde Gastos hasta que la renovación se confirme.",
+ "arkRenewConfirmWait": "Las monedas en renovación no pueden gastarse hasta que la transacción de la ronda se confirme en la red Bitcoin: normalmente unos diez minutos, a veces una hora o más. Una renovación iniciada no se puede cancelar.",
+ "arkRenewViewTx": "Ver la transacción de la ronda"
}
diff --git a/src/locales/fr.json b/src/locales/fr.json
index ea83805..e03bed9 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "Les vidéos de plus de {mb} Mo ou au-delà de 1080p attendent un toucher, pour que le fil ne dévore pas vos données. La limite est plus basse sur une connexion lente ou en économie de données.",
"arkLnRenewing": "Rien n’a été envoyé. {sats} sats sont en cours de renouvellement et ne peuvent pas être dépensés avant que le renouvellement soit confirmé sur le réseau Bitcoin : en général une dizaine de minutes, parfois une heure ou plus. Réessayez ensuite.",
"arkLnShort": "Solde insuffisant : ce paiement demande {need} sats avec ses frais, et {have} sont disponibles.",
- "arkCoinsRenewLock": "Les pièces en cours de renouvellement ne peuvent pas être dépensées avant que la transaction du tour soit confirmée sur le réseau Bitcoin : en général une dizaine de minutes, parfois une heure ou plus. Tout renouveler ne laisse rien pour payer entre-temps."
+ "arkRenewConfirmTitle": "Renouveler ces pièces ?",
+ "arkRenewConfirmBody": "Votre portefeuille renouvelle les pièces tout seul, gratuitement, quand elles approchent de l’expiration. Renouveler maintenant est facultatif.",
+ "arkRenewConfirmFee": "Frais de renouvellement",
+ "arkRenewConfirmLeft": "Reste à dépenser entre-temps",
+ "arkRenewConfirmAll": "C’est tout votre solde Dépenses. Vous ne pourrez ni payer ni envoyer depuis Dépenses avant la confirmation du renouvellement.",
+ "arkRenewConfirmWait": "Les pièces en cours de renouvellement ne peuvent pas être dépensées avant que la transaction du tour soit confirmée sur le réseau Bitcoin : en général une dizaine de minutes, parfois une heure ou plus. Un renouvellement lancé ne peut pas être annulé.",
+ "arkRenewViewTx": "Voir la transaction du tour"
}
diff --git a/src/locales/hi.json b/src/locales/hi.json
index 7be94f6..fa3fb5e 100644
--- a/src/locales/hi.json
+++ b/src/locales/hi.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "{mb} MB से बड़े या 1080p से ऊपर के वीडियो टैप का इंतज़ार करते हैं, ताकि फ़ीड आपका डेटा न खाए। धीमे या डेटा-सेवर कनेक्शन पर सीमा कम होती है।",
"arkLnRenewing": "कुछ भी नहीं भेजा गया। {sats} sats का नवीनीकरण हो रहा है और जब तक वह Bitcoin नेटवर्क पर पुष्ट नहीं होता, उन्हें खर्च नहीं किया जा सकता: आमतौर पर लगभग दस मिनट, कभी-कभी एक घंटा या ज़्यादा। तब फिर कोशिश करें।",
"arkLnShort": "पर्याप्त राशि नहीं: इस भुगतान को शुल्क सहित {need} sats चाहिए, और {have} उपलब्ध हैं।",
- "arkCoinsRenewLock": "जिन सिक्कों का नवीनीकरण हो रहा है, उन्हें तब तक खर्च नहीं किया जा सकता जब तक राउंड का लेनदेन Bitcoin नेटवर्क पर पुष्ट न हो: आमतौर पर लगभग दस मिनट, कभी-कभी एक घंटा या ज़्यादा। सभी सिक्कों का नवीनीकरण करने पर इस बीच भुगतान के लिए कुछ नहीं बचता।"
+ "arkRenewConfirmTitle": "इन सिक्कों का नवीनीकरण करें?",
+ "arkRenewConfirmBody": "आपका वॉलेट समाप्ति के करीब पहुँचे सिक्कों का नवीनीकरण अपने आप, मुफ़्त में करता है। अभी नवीनीकरण करना वैकल्पिक है।",
+ "arkRenewConfirmFee": "नवीनीकरण शुल्क",
+ "arkRenewConfirmLeft": "इस बीच खर्च के लिए बचेगा",
+ "arkRenewConfirmAll": "यह आपका पूरा खर्च बैलेंस है। नवीनीकरण की पुष्टि होने तक आप इससे भुगतान या भेज नहीं पाएँगे।",
+ "arkRenewConfirmWait": "जिन सिक्कों का नवीनीकरण हो रहा है, उन्हें तब तक खर्च नहीं किया जा सकता जब तक राउंड का लेनदेन Bitcoin नेटवर्क पर पुष्ट न हो: आमतौर पर लगभग दस मिनट, कभी-कभी एक घंटा या ज़्यादा। शुरू हुआ नवीनीकरण रद्द नहीं किया जा सकता।",
+ "arkRenewViewTx": "राउंड का लेनदेन देखें"
}
diff --git a/src/locales/id.json b/src/locales/id.json
index 01bc000..7cd5f5f 100644
--- a/src/locales/id.json
+++ b/src/locales/id.json
@@ -251,5 +251,11 @@
"videoLargeHelp": "Video di atas {mb} MB atau lebih dari 1080p menunggu ketukan, agar feed tidak menghabiskan kuota. Batasnya lebih rendah di koneksi lambat atau mode hemat data.",
"arkLnRenewing": "Tidak ada yang terkirim. {sats} sats sedang diperbarui dan tidak bisa dibelanjakan sampai pembaruan terkonfirmasi di jaringan Bitcoin: biasanya sekitar sepuluh menit, kadang satu jam atau lebih. Coba lagi nanti.",
"arkLnShort": "Saldo tidak cukup: pembayaran ini butuh {need} sats termasuk biayanya, dan yang tersedia {have}.",
- "arkCoinsRenewLock": "Koin yang sedang diperbarui tidak bisa dibelanjakan sampai transaksi ronde terkonfirmasi di jaringan Bitcoin: biasanya sekitar sepuluh menit, kadang satu jam atau lebih. Memperbarui semua koin berarti tidak ada yang bisa dipakai membayar untuk sementara."
+ "arkRenewConfirmTitle": "Perbarui koin ini?",
+ "arkRenewConfirmBody": "Dompetmu memperbarui koin sendiri, gratis, saat koin hampir kedaluwarsa. Memperbarui sekarang bersifat opsional.",
+ "arkRenewConfirmFee": "Biaya pembaruan",
+ "arkRenewConfirmLeft": "Sisa untuk dibelanjakan sementara itu",
+ "arkRenewConfirmAll": "Ini seluruh saldo Belanja-mu. Kamu tidak bisa membayar atau mengirim dari Belanja sampai pembaruan terkonfirmasi.",
+ "arkRenewConfirmWait": "Koin yang sedang diperbarui tidak bisa dibelanjakan sampai transaksi ronde terkonfirmasi di jaringan Bitcoin: biasanya sekitar sepuluh menit, kadang satu jam atau lebih. Pembaruan yang sudah dimulai tidak bisa dibatalkan.",
+ "arkRenewViewTx": "Lihat transaksi ronde"
}
diff --git a/src/locales/ja.json b/src/locales/ja.json
index 3805d70..bcfa7bb 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "{mb} MBを超える動画や1080pを超える動画は、データを使いすぎないようタップするまで再生しません。低速回線やデータセーバー時は上限が下がります。",
"arkLnRenewing": "何も送金されていません。{sats} satsは更新中で、更新がビットコインネットワークで承認されるまで使えません(通常は約10分、1時間以上かかることもあります)。その後にもう一度お試しください。",
"arkLnShort": "残高が足りません。この支払いには手数料込みで{need} sats必要ですが、使えるのは{have} satsです。",
- "arkCoinsRenewLock": "更新中のコインは、ラウンドのトランザクションがビットコインネットワークで承認されるまで使えません(通常は約10分、1時間以上かかることもあります)。すべてのコインを更新すると、その間は支払いに使えるものがなくなります。"
+ "arkRenewConfirmTitle": "これらのコインを更新しますか?",
+ "arkRenewConfirmBody": "ウォレットは、期限が近づいたコインを自動的に無料で更新します。今すぐの更新は任意です。",
+ "arkRenewConfirmFee": "更新手数料",
+ "arkRenewConfirmLeft": "その間に使える残高",
+ "arkRenewConfirmAll": "これは支払い用残高のすべてです。更新が承認されるまで、支払いも送金もできません。",
+ "arkRenewConfirmWait": "更新中のコインは、ラウンドのトランザクションがビットコインネットワークで承認されるまで使えません(通常は約10分、1時間以上かかることもあります)。始めた更新は取り消せません。",
+ "arkRenewViewTx": "ラウンドのトランザクションを見る"
}
diff --git a/src/locales/ko.json b/src/locales/ko.json
index a6b23aa..47a9386 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -914,5 +914,11 @@
"videoLargeHelp": "{mb}MB를 넘거나 1080p보다 높은 동영상은 데이터를 아끼기 위해 탭할 때까지 기다립니다. 느린 연결이나 데이터 절약 모드에서는 한도가 더 낮습니다.",
"arkLnRenewing": "아무것도 보내지지 않았습니다. {sats} sats가 갱신 중이며, 갱신이 비트코인 네트워크에서 확인될 때까지 쓸 수 없습니다. 보통 10분 정도, 때로는 1시간 이상 걸립니다. 그 후에 다시 시도하세요.",
"arkLnShort": "잔액이 부족합니다. 이 결제에는 수수료 포함 {need} sats가 필요하고, 사용할 수 있는 것은 {have} sats입니다.",
- "arkCoinsRenewLock": "갱신 중인 코인은 라운드 거래가 비트코인 네트워크에서 확인될 때까지 쓸 수 없습니다. 보통 10분 정도, 때로는 1시간 이상 걸립니다. 모든 코인을 갱신하면 그동안 결제할 수 있는 돈이 없습니다."
+ "arkRenewConfirmTitle": "이 코인들을 갱신할까요?",
+ "arkRenewConfirmBody": "지갑은 만료가 가까워진 코인을 스스로 무료로 갱신합니다. 지금 갱신하는 것은 선택 사항입니다.",
+ "arkRenewConfirmFee": "갱신 수수료",
+ "arkRenewConfirmLeft": "그동안 쓸 수 있는 잔액",
+ "arkRenewConfirmAll": "이것은 지출 잔액 전부입니다. 갱신이 확인될 때까지 지출 잔액으로 결제하거나 보낼 수 없습니다.",
+ "arkRenewConfirmWait": "갱신 중인 코인은 라운드 거래가 비트코인 네트워크에서 확인될 때까지 쓸 수 없습니다. 보통 10분 정도, 때로는 1시간 이상 걸립니다. 시작한 갱신은 취소할 수 없습니다.",
+ "arkRenewViewTx": "라운드 거래 보기"
}
diff --git a/src/locales/mr.json b/src/locales/mr.json
index 88479c1..e982d98 100644
--- a/src/locales/mr.json
+++ b/src/locales/mr.json
@@ -251,5 +251,11 @@
"videoLargeHelp": "{mb} MB पेक्षा मोठे किंवा 1080p पेक्षा वरचे व्हिडिओ तुमचा डेटा वाचवण्यासाठी टॅपची वाट पाहतात. धीम्या किंवा डेटा-सेव्हर कनेक्शनवर मर्यादा कमी असते.",
"arkLnRenewing": "काहीही पाठवले गेले नाही. {sats} sats चे नूतनीकरण सुरू आहे आणि ते Bitcoin नेटवर्कवर निश्चित होईपर्यंत खर्च करता येणार नाहीत: साधारणपणे सुमारे दहा मिनिटे, कधी कधी तासभर किंवा जास्त. नंतर पुन्हा प्रयत्न करा.",
"arkLnShort": "पुरेशी शिल्लक नाही: या पेमेंटला शुल्कासह {need} sats लागतात, आणि {have} उपलब्ध आहेत.",
- "arkCoinsRenewLock": "नूतनीकरण होत असलेली नाणी, राउंडचा व्यवहार Bitcoin नेटवर्कवर निश्चित होईपर्यंत खर्च करता येत नाहीत: साधारणपणे सुमारे दहा मिनिटे, कधी कधी तासभर किंवा जास्त. सर्व नाण्यांचे नूतनीकरण केल्यास तोपर्यंत पैसे देण्यासाठी काहीच उरत नाही."
+ "arkRenewConfirmTitle": "या नाण्यांचे नूतनीकरण करायचे?",
+ "arkRenewConfirmBody": "मुदत संपत आली की तुमचे वॉलेट नाण्यांचे नूतनीकरण आपोआप, मोफत करते. आत्ता नूतनीकरण करणे ऐच्छिक आहे.",
+ "arkRenewConfirmFee": "नूतनीकरण शुल्क",
+ "arkRenewConfirmLeft": "तोपर्यंत खर्चासाठी उरलेले",
+ "arkRenewConfirmAll": "ही तुमची संपूर्ण खर्च शिल्लक आहे. नूतनीकरण निश्चित होईपर्यंत तुम्ही यातून पैसे देऊ किंवा पाठवू शकणार नाही.",
+ "arkRenewConfirmWait": "नूतनीकरण होत असलेली नाणी, राउंडचा व्यवहार Bitcoin नेटवर्कवर निश्चित होईपर्यंत खर्च करता येत नाहीत: साधारणपणे सुमारे दहा मिनिटे, कधी कधी तासभर किंवा जास्त. सुरू झालेले नूतनीकरण रद्द करता येत नाही.",
+ "arkRenewViewTx": "राउंडचा व्यवहार पहा"
}
diff --git a/src/locales/pt.json b/src/locales/pt.json
index fdf925e..424d04e 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "Vídeos acima de {mb} MB ou de 1080p esperam um toque, para o feed não consumir seus dados. O limite é menor em conexões lentas ou com economia de dados.",
"arkLnRenewing": "Nada foi enviado. {sats} sats estão sendo renovados e não podem ser gastos até a renovação ser confirmada na rede Bitcoin: normalmente uns dez minutos, às vezes uma hora ou mais. Tente de novo depois.",
"arkLnShort": "Saldo insuficiente em Gastos: este pagamento precisa de {need} sats com a taxa, e há {have} disponíveis.",
- "arkCoinsRenewLock": "Moedas em renovação não podem ser gastas até a transação da rodada ser confirmada na rede Bitcoin: normalmente uns dez minutos, às vezes uma hora ou mais. Renovar todas as moedas não deixa nada para pagar nesse meio-tempo."
+ "arkRenewConfirmTitle": "Renovar estas moedas?",
+ "arkRenewConfirmBody": "Sua carteira renova as moedas sozinha, de graça, quando estão perto de expirar. Renovar agora é opcional.",
+ "arkRenewConfirmFee": "Taxa de renovação",
+ "arkRenewConfirmLeft": "Sobra para gastar enquanto isso",
+ "arkRenewConfirmAll": "Este é todo o seu saldo de Gastos. Você não poderá pagar nem enviar de Gastos até a renovação ser confirmada.",
+ "arkRenewConfirmWait": "Moedas em renovação não podem ser gastas até a transação da rodada ser confirmada na rede Bitcoin: normalmente uns dez minutos, às vezes uma hora ou mais. Uma renovação iniciada não pode ser cancelada.",
+ "arkRenewViewTx": "Ver a transação da rodada"
}
diff --git a/src/locales/ru.json b/src/locales/ru.json
index 918eb63..eef1a80 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "Видео больше {mb} МБ или выше 1080p ждут нажатия, чтобы лента не съедала трафик. На медленном соединении или в режиме экономии лимит ниже.",
"arkLnRenewing": "Ничего не отправлено. {sats} сат обновляются, и их нельзя потратить, пока обновление не подтвердится в сети Bitcoin: обычно около десяти минут, иногда час и больше. Попробуйте позже.",
"arkLnShort": "Недостаточно средств: для этого платежа с комиссией нужно {need} сат, доступно {have}.",
- "arkCoinsRenewLock": "Монеты в процессе обновления нельзя потратить, пока транзакция раунда не подтвердится в сети Bitcoin: обычно около десяти минут, иногда час и больше. Если обновить все монеты, платить в это время будет нечем."
+ "arkRenewConfirmTitle": "Обновить эти монеты?",
+ "arkRenewConfirmBody": "Кошелёк сам бесплатно обновляет монеты, когда их срок подходит к концу. Обновлять сейчас необязательно.",
+ "arkRenewConfirmFee": "Комиссия за обновление",
+ "arkRenewConfirmLeft": "Останется для трат на это время",
+ "arkRenewConfirmAll": "Это весь ваш баланс для трат. Платить и отправлять с него будет нельзя, пока обновление не подтвердится.",
+ "arkRenewConfirmWait": "Монеты в процессе обновления нельзя потратить, пока транзакция раунда не подтвердится в сети Bitcoin: обычно около десяти минут, иногда час и больше. Начатое обновление нельзя отменить.",
+ "arkRenewViewTx": "Посмотреть транзакцию раунда"
}
diff --git a/src/locales/sw.json b/src/locales/sw.json
index 93076bc..8901cef 100644
--- a/src/locales/sw.json
+++ b/src/locales/sw.json
@@ -251,5 +251,11 @@
"videoLargeHelp": "Video zaidi ya MB {mb} au juu ya 1080p husubiri uguse, ili mlisho usimalize data yako. Kikomo ni cha chini kwenye muunganisho wa polepole au wa kuokoa data.",
"arkLnRenewing": "Hakuna kilichotumwa. Sats {sats} zinahuishwa na haziwezi kutumika hadi uhuishaji uthibitishwe kwenye mtandao wa Bitcoin: kwa kawaida kama dakika kumi, wakati mwingine saa moja au zaidi. Jaribu tena baadaye.",
"arkLnShort": "Salio halitoshi: malipo haya yanahitaji sats {need} pamoja na ada, na zilizopo ni {have}.",
- "arkCoinsRenewLock": "Sarafu zinazohuishwa haziwezi kutumika hadi muamala wa raundi uthibitishwe kwenye mtandao wa Bitcoin: kwa kawaida kama dakika kumi, wakati mwingine saa moja au zaidi. Kuhuisha sarafu zote hakuachi chochote cha kulipia kwa muda huo."
+ "arkRenewConfirmTitle": "Huisha sarafu hizi?",
+ "arkRenewConfirmBody": "Pochi yako huhuisha sarafu yenyewe, bila malipo, zinapokaribia kuisha muda. Kuhuisha sasa ni hiari.",
+ "arkRenewConfirmFee": "Ada ya kuhuisha",
+ "arkRenewConfirmLeft": "Kinachobaki kutumia kwa sasa",
+ "arkRenewConfirmAll": "Hili ni salio lako lote la Matumizi. Hutaweza kulipa wala kutuma kutoka Matumizi hadi uhuishaji uthibitishwe.",
+ "arkRenewConfirmWait": "Sarafu zinazohuishwa haziwezi kutumika hadi muamala wa raundi uthibitishwe kwenye mtandao wa Bitcoin: kwa kawaida kama dakika kumi, wakati mwingine saa moja au zaidi. Uhuishaji ukishaanza hauwezi kughairiwa.",
+ "arkRenewViewTx": "Tazama muamala wa raundi"
}
diff --git a/src/locales/ta.json b/src/locales/ta.json
index c361637..e66fee5 100644
--- a/src/locales/ta.json
+++ b/src/locales/ta.json
@@ -251,5 +251,11 @@
"videoLargeHelp": "{mb} MB-க்கு மேல் அல்லது 1080p-க்கு மேலான வீடியோக்கள் உங்கள் டேட்டாவைச் சேமிக்க, தட்டும் வரை காத்திருக்கும். மெதுவான அல்லது டேட்டா சேமிப்பு இணைப்பில் வரம்பு குறைவு.",
"arkLnRenewing": "எதுவும் அனுப்பப்படவில்லை. {sats} sats புதுப்பிக்கப்படுகின்றன; புதுப்பிப்பு Bitcoin வலையமைப்பில் உறுதியாகும் வரை அவற்றைச் செலவிட முடியாது: பொதுவாக சுமார் பத்து நிமிடங்கள், சில நேரங்களில் ஒரு மணி நேரம் அல்லது அதற்கு மேல். பிறகு மீண்டும் முயலவும்.",
"arkLnShort": "போதிய இருப்பு இல்லை: இந்தக் கட்டணத்திற்கு கட்டணத்துடன் {need} sats தேவை, கிடைப்பது {have}.",
- "arkCoinsRenewLock": "புதுப்பிக்கப்படும் நாணயங்களை, சுற்றின் பரிவர்த்தனை Bitcoin வலையமைப்பில் உறுதியாகும் வரை செலவிட முடியாது: பொதுவாக சுமார் பத்து நிமிடங்கள், சில நேரங்களில் ஒரு மணி நேரம் அல்லது அதற்கு மேல். எல்லா நாணயங்களையும் புதுப்பித்தால் அதுவரை செலுத்த எதுவும் இருக்காது."
+ "arkRenewConfirmTitle": "இந்த நாணயங்களைப் புதுப்பிக்கவா?",
+ "arkRenewConfirmBody": "காலாவதி நெருங்கும்போது உங்கள் பணப்பை நாணயங்களைத் தானாகவே, இலவசமாகப் புதுப்பிக்கும். இப்போது புதுப்பிப்பது விருப்பத்திற்குரியது.",
+ "arkRenewConfirmFee": "புதுப்பிப்புக் கட்டணம்",
+ "arkRenewConfirmLeft": "அதுவரை செலவிட மீதம்",
+ "arkRenewConfirmAll": "இது உங்கள் செலவு இருப்பு முழுவதும். புதுப்பிப்பு உறுதியாகும் வரை இதிலிருந்து செலுத்தவோ அனுப்பவோ முடியாது.",
+ "arkRenewConfirmWait": "புதுப்பிக்கப்படும் நாணயங்களை, சுற்றின் பரிவர்த்தனை Bitcoin வலையமைப்பில் உறுதியாகும் வரை செலவிட முடியாது: பொதுவாக சுமார் பத்து நிமிடங்கள், சில நேரங்களில் ஒரு மணி நேரம் அல்லது அதற்கு மேல். தொடங்கிய புதுப்பிப்பை ரத்து செய்ய முடியாது.",
+ "arkRenewViewTx": "சுற்றின் பரிவர்த்தனையைக் காண்க"
}
diff --git a/src/locales/te.json b/src/locales/te.json
index 4199e2e..0c48c6b 100644
--- a/src/locales/te.json
+++ b/src/locales/te.json
@@ -249,5 +249,11 @@
"videoLargeHelp": "{mb} MB కంటే పెద్దవి లేదా 1080p పైబడిన వీడియోలు మీ డేటా ఆదా కోసం నొక్కే వరకు ఆగుతాయి. నెమ్మదైన లేదా డేటా సేవర్ కనెక్షన్‌లో పరిమితి తక్కువ.",
"arkLnRenewing": "ఏమీ పంపబడలేదు. {sats} sats పునరుద్ధరణలో ఉన్నాయి; అది Bitcoin నెట్‌వర్క్‌లో నిర్ధారణ అయ్యే వరకు వాటిని ఖర్చు చేయలేరు: సాధారణంగా సుమారు పది నిమిషాలు, కొన్నిసార్లు గంట లేదా అంతకంటే ఎక్కువ. తర్వాత మళ్లీ ప్రయత్నించండి.",
"arkLnShort": "సరిపడా నిల్వ లేదు: ఈ చెల్లింపుకు రుసుముతో కలిపి {need} sats కావాలి, అందుబాటులో ఉన్నవి {have}.",
- "arkCoinsRenewLock": "పునరుద్ధరణలో ఉన్న నాణేలను, రౌండ్ లావాదేవీ Bitcoin నెట్‌వర్క్‌లో నిర్ధారణ అయ్యే వరకు ఖర్చు చేయలేరు: సాధారణంగా సుమారు పది నిమిషాలు, కొన్నిసార్లు గంట లేదా అంతకంటే ఎక్కువ. అన్ని నాణేలను పునరుద్ధరిస్తే ఆ సమయంలో చెల్లించడానికి ఏమీ ఉండదు."
+ "arkRenewConfirmTitle": "ఈ నాణేలను పునరుద్ధరించాలా?",
+ "arkRenewConfirmBody": "గడువు దగ్గరపడినప్పుడు మీ వాలెట్ నాణేలను తనంతట తానే, ఉచితంగా పునరుద్ధరిస్తుంది. ఇప్పుడు పునరుద్ధరించడం ఐచ్ఛికం.",
+ "arkRenewConfirmFee": "పునరుద్ధరణ రుసుము",
+ "arkRenewConfirmLeft": "ఈలోగా ఖర్చుకు మిగిలేది",
+ "arkRenewConfirmAll": "ఇది మీ మొత్తం ఖర్చు నిల్వ. పునరుద్ధరణ నిర్ధారణ అయ్యే వరకు దీని నుంచి చెల్లించలేరు, పంపలేరు.",
+ "arkRenewConfirmWait": "పునరుద్ధరణలో ఉన్న నాణేలను, రౌండ్ లావాదేవీ Bitcoin నెట్‌వర్క్‌లో నిర్ధారణ అయ్యే వరకు ఖర్చు చేయలేరు: సాధారణంగా సుమారు పది నిమిషాలు, కొన్నిసార్లు గంట లేదా అంతకంటే ఎక్కువ. మొదలైన పునరుద్ధరణను రద్దు చేయలేరు.",
+ "arkRenewViewTx": "రౌండ్ లావాదేవీని చూడండి"
}
diff --git a/src/locales/tr.json b/src/locales/tr.json
index c96eb99..09e5196 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -914,5 +914,11 @@
"videoLargeHelp": "{mb} MB’den büyük veya 1080p’nin üzerindeki videolar, akış verini tüketmesin diye dokunmanı bekler. Yavaş ya da veri tasarruflu bağlantıda sınır daha düşüktür.",
"arkLnRenewing": "Hiçbir şey gönderilmedi. {sats} sats yenileniyor ve yenileme Bitcoin ağında onaylanana kadar harcanamaz: genellikle on dakika kadar, bazen bir saat veya daha fazla. Sonra tekrar dene.",
"arkLnShort": "Yeterli bakiye yok: bu ödeme ücretiyle birlikte {need} sats gerektiriyor, kullanılabilir olan {have}.",
- "arkCoinsRenewLock": "Yenilenen coinler, turun işlemi Bitcoin ağında onaylanana kadar harcanamaz: genellikle on dakika kadar, bazen bir saat veya daha fazla. Tüm coinleri yenilemek, bu sürede ödeme yapacak bir şey bırakmaz."
+ "arkRenewConfirmTitle": "Bu coinler yenilensin mi?",
+ "arkRenewConfirmBody": "Cüzdanın, süresi dolmak üzere olan coinleri kendiliğinden ve ücretsiz yeniler. Şimdi yenilemek isteğe bağlıdır.",
+ "arkRenewConfirmFee": "Yenileme ücreti",
+ "arkRenewConfirmLeft": "Bu sırada harcanabilir kalan",
+ "arkRenewConfirmAll": "Bu, Harcama bakiyenin tamamı. Yenileme onaylanana kadar Harcama’dan ödeme yapamaz veya gönderemezsin.",
+ "arkRenewConfirmWait": "Yenilenen coinler, turun işlemi Bitcoin ağında onaylanana kadar harcanamaz: genellikle on dakika kadar, bazen bir saat veya daha fazla. Başlayan bir yenileme iptal edilemez.",
+ "arkRenewViewTx": "Turun işlemini görüntüle"
}
diff --git a/src/locales/ur.json b/src/locales/ur.json
index 56b5b90..25662c7 100644
--- a/src/locales/ur.json
+++ b/src/locales/ur.json
@@ -251,5 +251,11 @@
"videoLargeHelp": "{mb} MB سے بڑی یا 1080p سے اوپر کی ویڈیوز ٹیپ کا انتظار کرتی ہیں، تاکہ فیڈ آپ کا ڈیٹا نہ کھائے۔ سست یا ڈیٹا بچت والے کنکشن پر حد کم ہوتی ہے۔",
"arkLnRenewing": "کچھ نہیں بھیجا گیا۔ {sats} sats کی تجدید ہو رہی ہے اور جب تک تجدید Bitcoin نیٹ ورک پر تصدیق نہ ہو جائے انہیں خرچ نہیں کیا جا سکتا: عام طور پر تقریباً دس منٹ، کبھی کبھی ایک گھنٹہ یا زیادہ۔ اس کے بعد دوبارہ کوشش کریں۔",
"arkLnShort": "رقم ناکافی ہے: اس ادائیگی کو فیس سمیت {need} sats درکار ہیں، اور {have} دستیاب ہیں۔",
- "arkCoinsRenewLock": "جن سکوں کی تجدید ہو رہی ہے وہ اس وقت تک خرچ نہیں ہو سکتے جب تک راؤنڈ کی ٹرانزیکشن Bitcoin نیٹ ورک پر تصدیق نہ ہو جائے: عام طور پر تقریباً دس منٹ، کبھی کبھی ایک گھنٹہ یا زیادہ۔ تمام سکوں کی تجدید کرنے سے اس دوران ادائیگی کے لیے کچھ نہیں بچتا۔"
+ "arkRenewConfirmTitle": "ان سکوں کی تجدید کریں؟",
+ "arkRenewConfirmBody": "آپ کا والیٹ میعاد ختم ہونے کے قریب سکوں کی تجدید خود بخود، مفت کرتا ہے۔ ابھی تجدید کرنا اختیاری ہے۔",
+ "arkRenewConfirmFee": "تجدید فیس",
+ "arkRenewConfirmLeft": "اس دوران خرچ کے لیے باقی",
+ "arkRenewConfirmAll": "یہ آپ کا پورا خرچ بیلنس ہے۔ تجدید کی تصدیق تک آپ اس سے ادائیگی یا بھیج نہیں سکیں گے۔",
+ "arkRenewConfirmWait": "جن سکوں کی تجدید ہو رہی ہے وہ اس وقت تک خرچ نہیں ہو سکتے جب تک راؤنڈ کی ٹرانزیکشن Bitcoin نیٹ ورک پر تصدیق نہ ہو جائے: عام طور پر تقریباً دس منٹ، کبھی کبھی ایک گھنٹہ یا زیادہ۔ شروع ہونے والی تجدید منسوخ نہیں ہو سکتی۔",
+ "arkRenewViewTx": "راؤنڈ کی ٹرانزیکشن دیکھیں"
}
diff --git a/src/locales/vi.json b/src/locales/vi.json
index f01f0a7..3a82425 100644
--- a/src/locales/vi.json
+++ b/src/locales/vi.json
@@ -251,5 +251,11 @@
"videoLargeHelp": "Video trên {mb} MB hoặc cao hơn 1080p sẽ chờ bạn chạm, để bảng tin không ngốn dữ liệu. Giới hạn thấp hơn khi kết nối chậm hoặc tiết kiệm dữ liệu.",
"arkLnRenewing": "Chưa gửi gì cả. {sats} sats đang được gia hạn và không thể chi tiêu cho đến khi việc gia hạn được xác nhận trên mạng Bitcoin: thường khoảng mười phút, đôi khi một giờ hoặc hơn. Hãy thử lại sau.",
"arkLnShort": "Không đủ số dư: khoản thanh toán này cần {need} sats gồm cả phí, và hiện có {have}.",
- "arkCoinsRenewLock": "Coin đang gia hạn không thể chi tiêu cho đến khi giao dịch của vòng được xác nhận trên mạng Bitcoin: thường khoảng mười phút, đôi khi một giờ hoặc hơn. Gia hạn mọi coin nghĩa là trong lúc đó không còn gì để thanh toán."
+ "arkRenewConfirmTitle": "Gia hạn các coin này?",
+ "arkRenewConfirmBody": "Ví của bạn tự gia hạn coin, miễn phí, khi chúng sắp hết hạn. Gia hạn ngay bây giờ là tùy chọn.",
+ "arkRenewConfirmFee": "Phí gia hạn",
+ "arkRenewConfirmLeft": "Còn lại để chi tiêu trong lúc đó",
+ "arkRenewConfirmAll": "Đây là toàn bộ số dư Chi tiêu của bạn. Bạn sẽ không thể thanh toán hay gửi từ Chi tiêu cho đến khi việc gia hạn được xác nhận.",
+ "arkRenewConfirmWait": "Coin đang gia hạn không thể chi tiêu cho đến khi giao dịch của vòng được xác nhận trên mạng Bitcoin: thường khoảng mười phút, đôi khi một giờ hoặc hơn. Việc gia hạn đã bắt đầu thì không thể hủy.",
+ "arkRenewViewTx": "Xem giao dịch của vòng"
}
diff --git a/src/locales/zh.json b/src/locales/zh.json
index b64861b..82b37f3 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -915,5 +915,11 @@
"videoLargeHelp": "超过 {mb} MB 或高于 1080p 的视频会等你点按再播放,以免动态耗尽流量。在慢速或省流量连接下,上限更低。",
"arkLnRenewing": "没有发送任何款项。{sats} sats 正在续期,在续期于比特币网络确认之前无法使用:通常约十分钟,有时一小时或更久。请到时再试。",
"arkLnShort": "余额不足:这笔付款连同手续费需要 {need} sats,可用 {have} sats。",
- "arkCoinsRenewLock": "续期中的币在该轮交易于比特币网络确认之前无法使用:通常约十分钟,有时一小时或更久。把所有币都续期,期间就没有可用于付款的余额。"
+ "arkRenewConfirmTitle": "续期这些币吗?",
+ "arkRenewConfirmBody": "钱包会在币快到期时自动免费续期。现在续期是可选的。",
+ "arkRenewConfirmFee": "续期手续费",
+ "arkRenewConfirmLeft": "期间可用余额",
+ "arkRenewConfirmAll": "这是你全部的消费余额。在续期确认之前,你无法从消费余额付款或转账。",
+ "arkRenewConfirmWait": "续期中的币在该轮交易于比特币网络确认之前无法使用:通常约十分钟,有时一小时或更久。续期一旦开始便无法取消。",
+ "arkRenewViewTx": "查看该轮交易"
}
diff --git a/tools/ark/renew-confirm-ui-test.js b/tools/ark/renew-confirm-ui-test.js
new file mode 100644
index 0000000..e5a68c8
--- /dev/null
+++ b/tools/ark/renew-confirm-ui-test.js
@@ -0,0 +1,132 @@
+// Renewing from the coins page asks first: the coins, the fee, what is left
+// to spend meanwhile — and says outright when that is nothing. A renewal in
+// flight links to its round's transaction once the round has run.
+// Real app with a local Ark manager stub holding the ten coins of the
+// 2026-10-03 report; nothing is sent anywhere.
+// Run: bun tools/ark/renew-confirm-ui-test.js
+import assert from 'node:assert/strict';
+import puppeteer from 'puppeteer-core';
+import { generateMnemonic } from '@scure/bip39';
+import { wordlist } from '@scure/bip39/wordlists/english';
+
+const bundle = await Bun.build({ entrypoints: ['src/app.js'], target: 'browser', minify: true,
+ plugins: [{ name: 'cooperative-exit-test', setup(build) {
+ build.onLoad({ filter: /src\/app\.js$/ }, async ({ path }) => ({ loader: 'js', contents: await Bun.file(path).text() + `
+window.coopApp = { ui, wallet, ctx, render, enterWallet, get ready() { return !_bootDeciding; },
+ setSpending: (on = true) => { const a = activeAccount(); if (a) { if (on) a.kind = 'spending'; else delete a.kind; } } };
+` }));
+ build.onLoad({ filter: /src\/features\/ark\.js$/ }, async ({ path }) => ({ loader: 'js', contents:
+ (await Bun.file(path).text()).replace(' arkReady() { return arkAvailable(); },', ' arkReady() { return window.forceArkReady || arkAvailable(); },').replace(" return {\n id: 'ark',", `
+ window.coopFeature = { setManager: (mgr) => { ark = mgr; }, offboard: doArkOffboard };
+ return {\n id: 'ark',`) }));
+ build.onLoad({ filter: /src\/video-compress\.js$/ }, () => ({ loader: 'js', contents: 'export async function compressVideo(f) { return f; }' }));
+ } }] });
+assert(bundle.success, bundle.logs.join('\n'));
+const html = `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${await Bun.file('src/style.css').text()}</style><div id="app"></div><script type="module">${await bundle.outputs[0].text()}</script>`;
+const server = Bun.serve({ port: 0, fetch: (req) => new URL(req.url).pathname === '/'
+ ? new Response(html, { headers: { 'Content-Type': 'text/html' } }) : new Response(null, { status: 404 }) });
+const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] });
+try {
+ const page = await browser.newPage(), errors = [];
+ page.on('pageerror', (e) => errors.push(e.message));
+ await page.setViewport({ width: 390, height: 844 });
+ const cdp = await page.createCDPSession();
+ await cdp.send('Network.enable');
+ await cdp.send('Network.setBlockedURLs', { urls: ['wss://*', 'ws://*'] });
+ await page.setRequestInterception(true);
+ page.on('request', (r) => r.url().startsWith(server.url.origin) || r.url().startsWith('data:') ? r.continue() : r.abort());
+ await page.evaluateOnNewDocument(() => {
+ localStorage.setItem('btc-wallet-network', 'regtest');
+ localStorage.setItem('btc-wallet-ark-provider:regtest', 'off');
+ });
+ await page.goto(server.url.href, { waitUntil: 'domcontentloaded' });
+ await page.waitForFunction(() => window.coopApp?.ready);
+ await page.evaluate(async (mn) => {
+ await coopApp.enterWallet(mn, '', { fresh: true, generated: true });
+ coopApp.ui.onb = null; localStorage.removeItem('btc-wallet-onb-step');
+ coopApp.wallet.scan = async () => {};
+ window.renewCalls = [];
+ const amounts = [3, 54, 54, 49500, 1000, 1000, 1000, 1000, 1000, 1000];
+ const table = [{ thresholdBlocks: 0, ppm: 0 }, { thresholdBlocks: 97, ppm: 250 }, { thresholdBlocks: 199, ppm: 1000 }, { thresholdBlocks: 2161, ppm: 2000 }];
+ window.coopManager = {
+ state: { vtxos: amounts.map((a, i) => ({ id: 'coin-' + i, amountSat: a, state: 'spendable', expiryHeight: 973921 })), actions: [], movements: [] },
+ info: { network: 'regtest', refreshFees: { baseFeeSat: 0, ppmExpiryTable: table } }, _tipH: 969751,
+ vtxos() { return this.state.vtxos; },
+ _decoded() { throw new Error('no exit transaction fixture'); },
+ refreshFee(inputs, tip) {
+ let u = 0;
+ for (const v of inputs) u += v.amountSat * (table.filter((e) => e.thresholdBlocks <= v.expiryHeight - tip).pop()?.ppm ?? 0);
+ return Math.ceil(u / 1e6);
+ },
+ unseenReceives() { return []; }, ackReceives() {},
+ address() { return 'test-ark-address'; },
+ async refresh(ids, opts) { renewCalls.push({ ids, opts }); return 'refresh-1'; },
+ };
+ coopFeature.setManager(coopManager);
+ coopApp.ui.screen = 'wallet'; coopApp.ui.tab = 'receive'; coopApp.ui.arkCoinsPage = true; coopApp.render();
+ }, generateMnemonic(wordlist));
+ await page.waitForSelector('.coin-table');
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+ const text = () => page.evaluate(() => document.body.innerText);
+ const calls = () => page.evaluate(() => renewCalls);
+
+ assert.match(await page.$eval('.ark-renew-ask', (b) => b.textContent), /112 sats/, 'the button prices all ten coins at 0.2%');
+ await page.click('.ark-renew-ask'); await sleep(200);
+ assert(await page.$('.ark-renew-confirm'), 'the tap opens a confirmation');
+ assert.deepEqual(await calls(), [], 'nothing is renewed by the first tap');
+ let tx = await text();
+ assert.match(tx, /Renew these coins\?/);
+ assert.match(tx, /Coins\s*10/); assert.match(tx, /Amount\s*55,611 sats/); assert.match(tx, /Renewal fee\s*112 sats/);
+ assert.match(tx, /Left to spend meanwhile\s*0 sats/);
+ assert.match(tx, /This is your whole Spending balance/, 'renewing everything is called out');
+ assert.match(tx, /can’t be spent until the round’s transaction confirms/);
+ assert.match(tx, /can’t be cancelled/);
+ if (process.env.SHOT) await page.screenshot({ path: process.env.SHOT });
+ console.log('✓ Renew asks first: 10 coins, 55,611 sats, 112 fee, nothing left to spend — said outright');
+
+ await page.evaluate(() => [...document.querySelectorAll('.ark-renew-confirm button')].find((b) => b.textContent === 'Cancel').click()); await sleep(200);
+ assert(!(await page.$('.ark-renew-confirm')) && await page.$('.coin-table'), 'Cancel returns to the coins');
+ assert.deepEqual(await calls(), []);
+ console.log('✓ Cancel goes back; nothing renewed');
+
+ // leave the big coin out: something stays spendable, no whole-balance warning
+ await page.evaluate(() => { coopApp.ui.arkCoinsSel.delete('coin-3'); coopApp.render(); }); await sleep(100);
+ await page.click('.ark-renew-ask'); await sleep(200);
+ tx = await text();
+ assert.match(tx, /Coins\s*9/); assert.match(tx, /Left to spend meanwhile\s*49,500 sats/);
+ assert.doesNotMatch(tx, /This is your whole Spending balance/);
+ await page.click('.ark-renew-go'); await sleep(300);
+ const c = await calls();
+ assert.equal(c.length, 1); assert.equal(c[0].ids.length, 9); assert(!c[0].ids.includes('coin-3')); assert.deepEqual(c[0].opts, { manual: true });
+ assert(!(await page.$('.ark-renew-confirm')), 'the confirmation closes');
+ console.log('✓ With a coin left out it says what stays spendable; confirming renews exactly the selection');
+
+ // a renewal in flight: no link until the round has run, then its transaction
+ const txid = 'ab'.repeat(32);
+ await page.evaluate(() => {
+ const m = coopManager;
+ for (const v of m.state.vtxos) if (v.id !== 'coin-3') v.state = 'pending';
+ m.state.actions = [{ id: 'refresh-' + Date.now(), type: 'refresh', step: 'submitted', manual: true, inputIds: m.state.vtxos.filter((v) => v.state === 'pending').map((v) => v.id), inAmountSat: 6111, outAmountSat: 6098, feeSat: 13 }];
+ coopApp.ui.arkCoinsPage = true; coopApp.render();
+ }); await sleep(200);
+ assert.match(await text(), /In a round/);
+ assert(!(await page.$('.ark-renew-tx')), 'no link before the round has a transaction');
+ await page.evaluate((txid) => { coopManager.state.actions[0].fundingTxid = txid; coopApp.render(); }, txid); await sleep(200);
+ const href = await page.$eval('.ark-renew-tx', (a) => a.href);
+ assert(href.includes(txid), 'the link goes to the round transaction: ' + href);
+ console.log('✓ A renewal in flight links to its round\'s transaction once the round has run');
+ // ...and the finished renewal's history row carries the same transaction
+ await page.evaluate((txid) => {
+ const m = coopManager;
+ m.state.actions = [];
+ for (const v of m.state.vtxos) v.state = 'spendable';
+ m.state.movements = [{ id: 'mv-renew', type: 'refresh', amountSat: 6098, feeSat: 13, manual: true, status: 'complete', ts: Date.now(), txid, unlockHash: '22'.repeat(32), inputIds: [] }];
+ coopApp.ui.arkCoinsPage = null; coopApp.ui.screen = 'wallet'; coopApp.ui.tab = 'history'; coopApp.ui.arkMoveDetail = 'mv-renew'; coopApp.render();
+ }, txid); await sleep(300);
+ const detail = await page.evaluate((txid) => ({ shown: document.body.innerText.includes(txid), link: [...document.querySelectorAll('a')].some((a) => a.href.includes(txid)) }), txid);
+ if (!detail.shown || !detail.link) console.log(' (detail text: ' + (await text()).slice(0, 300).replace(/\n+/g, ' | ') + ')');
+ assert(detail.shown && detail.link, 'the renewal\'s detail shows and links its transaction: ' + JSON.stringify(detail));
+ console.log('✓ A finished renewal\'s history detail shows the transaction and links to it');
+ assert.deepEqual(errors, [], 'no page errors');
+ console.log('\n✅ renewing asks first and shows its transaction');
+} finally { await browser.close(); server.stop(true); }
diff --git a/tools/ark/renew-txid-test.js b/tools/ark/renew-txid-test.js
new file mode 100644
index 0000000..ea0be35
--- /dev/null
+++ b/tools/ark/renew-txid-test.js
@@ -0,0 +1,45 @@
+// A renewal remembers its round's transaction as soon as the round has run —
+// before that transaction confirms, which is exactly when someone wants to
+// look it up — so the coins page (and later the history row) can link to it.
+// Run: bun tools/ark/renew-txid-test.js
+import assert from 'node:assert/strict';
+import { mock } from 'bun:test';
+import { hex } from '@scure/base';
+
+const refresh = await import('../../src/ark/refresh.js');
+// version 2, one input, one 1000-sat OP_TRUE output
+const rawTx = hex.decode('02000000' + '01' + '11'.repeat(32) + '00000000' + '00' + 'ffffffff' + '01' + 'e803000000000000' + '0151' + '00000000');
+const wantTxid = refresh.parseTx(rawTx).txid;
+let roundRan = false;
+mock.module('../../src/ark/refresh.js', () => ({
+ ...refresh,
+ roundParticipationStatus: async () => (roundRan ? { status: 1, fundingTx: rawTx, outputVtxos: [] } : { status: 0 }),
+}));
+const { ArkManager } = await import('../../src/ark/manager.js');
+let saves = 0;
+const mgr = new ArkManager({ storage: { save() { saves++; } }, arkUrl: 'http://test.invalid' });
+const asked = [];
+Object.defineProperty(mgr, 'chain', { value: { getTxStatus: async (txid) => { asked.push(txid); return { confirmed: false }; } } });
+const action = { id: 'refresh-1', type: 'refresh', step: 'submitted', inputIds: ['a:0'], outKeyIndex: 1, outAmountSat: 900, feeSat: 0, unlockHash: '22'.repeat(32) };
+mgr.state = { nextKeyIndex: 2, movements: [], actions: [action], vtxos: [{ id: 'a:0', amountSat: 900, expiryHeight: 500, state: 'pending' }] };
+mgr._refreshOutputs = () => [];
+
+await mgr._driveRefresh(action);
+assert.equal(action.fundingTxid, undefined, 'no transaction before the round has run');
+assert.equal(action.step, 'submitted');
+console.log('✓ Before the round runs there is no transaction to point at');
+
+roundRan = true;
+const before = saves;
+await mgr._driveRefresh(action);
+assert.equal(action.fundingTxid, wantTxid, 'the round transaction is kept');
+assert.equal(action.step, 'submitted', 'still waiting for the confirmation');
+assert.deepEqual(asked, [wantTxid]);
+assert(saves > before, 'and saved, so a reload still has it');
+console.log('✓ Once the round has run its transaction id is kept, though unconfirmed: ' + wantTxid.slice(0, 16) + '…');
+
+const again = saves;
+await mgr._driveRefresh(action);
+assert.equal(saves, again, 'not saved again on every sync while it waits');
+console.log('✓ ...and waiting for the confirmation writes nothing more');
+console.log('\n✅ a renewal knows its round transaction');