ui: skin web3 airdrop, predict, intel and mixer pages

This commit is contained in:
ZHU MINCHAO committed 2026-11-04 10:06:07 +08:00
1 parent b217620d40
commit 96be937daa
1 file changed
+46 -40
+46 -40
View File
@@ -492,23 +492,25 @@ namespace Decentralized
{ {
l.AddHeader(L.T("air.h")); l.AddHeader(L.T("air.h"));
var list = d.Scams.Airdrops.Where(a => !a.Claimed && a.Expire >= day).ToList(); var list = d.Scams.Airdrops.Where(a => !a.Claimed && a.Expire >= day).ToList();
if (list.Count == 0) l.AddLabel(L.T("air.none")); if (list.Count == 0) l.AddLabel(L.T("air.none"), "air_none_dim");
foreach (var a in list) foreach (var a in list)
{ {
var ad = a; var ad = a;
l.AddLabel(L.T("air.row", a.Name, Fmt.Num(a.Reward), ApproveText(a.Approve, a.Asset), a.Expire)); l.AddLabel(L.T("air.row", a.Name, Fmt.Num(a.Reward), ApproveText(a.Approve, a.Asset), a.Expire));
l.AddButton(L.T("air.claim"), () => ClaimAirdrop(ad)); l.AddButton(L.T("air.claim"), () => ClaimAirdrop(ad));
l.AddSpace(4f);
} }
}, r => }, r =>
{ {
r.AddHeader(L.T("air.approvals", WalletWindow.NameOf(w))); r.AddHeader(L.T("air.approvals", WalletWindow.NameOf(w)));
var aps = d.Scams.Approvals.Where(x => ChainAddress.Same(x.Wallet, w.Address)).ToList(); var aps = d.Scams.Approvals.Where(x => ChainAddress.Same(x.Wallet, w.Address)).ToList();
if (aps.Count == 0) r.AddLabel(L.T("air.approvals.none")); if (aps.Count == 0) r.AddLabel(L.T("air.approvals.none"), "air_app_none_dim");
foreach (var ap in aps) foreach (var ap in aps)
{ {
int id = ap.Id; int id = ap.Id;
r.AddLabel(L.T("air.approval", ap.Spender, ApproveText(ap.Limit, ap.Asset))); r.AddLabel(L.T("air.approval", ap.Spender, ApproveText(ap.Limit, ap.Asset)));
r.AddButton(L.T("air.revoke"), () => RevokeApproval(id)); r.AddButton(L.T("air.revoke"), () => RevokeApproval(id));
r.AddSpace(4f);
} }
}); });
} }
@@ -688,12 +690,12 @@ namespace Decentralized
if (_predTab == PredTab.Intel && !Unlock.BlackUnlocked) _predTab = PredTab.Markets; if (_predTab == PredTab.Intel && !Unlock.BlackUnlocked) _predTab = PredTab.Markets;
if (Unlock.BlackUnlocked) if (Unlock.BlackUnlocked)
{ {
b.BeginRow(); b.BeginRow(4f);
foreach (PredTab t in Enum.GetValues(typeof(PredTab))) foreach (PredTab t in Enum.GetValues(typeof(PredTab)))
{ {
var tab = t; var tab = t;
string name = L.T("predict.tab." + t.ToString().ToLowerInvariant()); string name = L.T("predict.tab." + t.ToString().ToLowerInvariant());
b.AddButton(tab == _predTab ? $"[{name}]" : name, () => { _predTab = tab; _message = null; Open(); }); PledgerSkin.Pick(b, name, tab == _predTab, () => { _predTab = tab; _message = null; Open(); }, _picked);
} }
b.End(); b.End();
} }
@@ -712,31 +714,31 @@ namespace Decentralized
l.AddSeparator(); l.AddSeparator();
// 选中 PSC 价格盘时,在列表上方画日 K 和阈值线 // 选中 PSC 价格盘时,在列表上方画日 K 和阈值线
if (sel != null && sel.Kind == PredictKind.Price) { CandleChart.Add(l, "predict", Assets.PSC, sel.Param); l.AddSeparator(); } if (sel != null && sel.Kind == PredictKind.Price) { CandleChart.Add(l, "predict", Assets.PSC, sel.Param); l.AddSeparator(); }
if (open.Count == 0) l.AddLabel(L.T("predict.none")); if (open.Count == 0) l.AddLabel(L.T("predict.none"), "pred_none_dim");
foreach (var m in open) foreach (var m in open)
{ {
var mk = m; var mk = m;
var all = bk.PosAll(m.Id); var all = bk.PosAll(m.Id);
string q = PredictGame.Question(m); string q = PredictGame.Question(m);
l.BeginRow(4f); l.BeginRow(4f);
l.AddButton(m.Id == _predPick ? "[" + q + "]" : q, () => { _predPick = mk.Id; _message = null; Open(); }); PledgerSkin.Pick(l, q, m.Id == _predPick, () => { _predPick = mk.Id; _message = null; Open(); }, _picked);
l.End(); l.End();
string state = m.IsOpen(day) ? "" : " " + L.T("predict.closed"); string state = m.IsOpen(day) ? "" : " " + L.T("predict.closed");
string move = m.PrevP >= 0 && Math.Abs(m.PYes - m.PrevP) >= 0.02 ? L.T("predict.row.move", P2(m.PrevP), m.PYes > m.PrevP ? "↑" : "↓") : ""; string move = m.PrevP >= 0 && Math.Abs(m.PYes - m.PrevP) >= 0.02 ? L.T("predict.row.move", P2(m.PrevP), m.PYes > m.PrevP ? "↑" : "↓") : "";
l.AddLabel(L.T("predict.row", P2(m.PYes), P2(1 - m.PYes)) + move + (all.Count > 0 ? " " + L.T("predict.held", L.T(all[0].Yes ? "predict.yes" : "predict.no"), Fmt.Num(all.Sum(x => x.Shares)), Fmt.Num(all.Sum(x => x.Cost))) : "") + state); l.AddLabel(L.T("predict.row", P2(m.PYes), P2(1 - m.PYes)) + move + (all.Count > 0 ? " " + L.T("predict.held", L.T(all[0].Yes ? "predict.yes" : "predict.no"), Fmt.Num(all.Sum(x => x.Shares)), Fmt.Num(all.Sum(x => x.Cost))) : "") + state, "pred_row_dim");
} }
if (bk.Stats.Count > 0) if (bk.Stats.Count > 0)
{ {
l.AddSeparator(); l.AddSeparator();
l.AddLabel(L.T("predict.stats.h")); l.AddLabel(L.T("predict.stats.h"));
foreach (var kv in bk.Stats.OrderByDescending(k => k.Value.Count)) foreach (var kv in bk.Stats.OrderByDescending(k => k.Value.Count))
l.AddLabel(L.T("predict.stats.row", L.T("predict.kind." + kv.Key), kv.Value.Count, P2(kv.Value.AvgP0), (kv.Value.HitRate * 100).ToString("0", CultureInfo.InvariantCulture))); l.AddLabel(L.T("predict.stats.row", L.T("predict.kind." + kv.Key), kv.Value.Count, P2(kv.Value.AvgP0), (kv.Value.HitRate * 100).ToString("0", CultureInfo.InvariantCulture)), "pred_stat_dim");
} }
if (bk.Recent.Count > 0) if (bk.Recent.Count > 0)
{ {
l.AddSeparator(); l.AddSeparator();
foreach (var r in Enumerable.Reverse(bk.Recent)) foreach (var r in Enumerable.Reverse(bk.Recent))
l.AddLabel(L.T("predict.result", r.Day, r.Title, r.Outcome == 2 ? L.T("predict.void") : L.T(r.Outcome == 1 ? "predict.yes" : "predict.no"), r.Held ? (r.Pnl >= 0 ? "+" : "") + Fmt.Num(r.Pnl) : "—")); l.AddLabel(L.T("predict.result", r.Day, r.Title, r.Outcome == 2 ? L.T("predict.void") : L.T(r.Outcome == 1 ? "predict.yes" : "predict.no"), r.Held ? (r.Pnl >= 0 ? "+" : "") + Fmt.Num(r.Pnl) : "—"), "pred_res_dim");
} }
}, r => }, r =>
{ {
@@ -744,32 +746,33 @@ namespace Decentralized
r.AddHeader(L.T("predict.pick")); r.AddHeader(L.T("predict.pick"));
r.AddLabel(PredictGame.Question(sel)); r.AddLabel(PredictGame.Question(sel));
r.AddLabel(L.T("predict.price", P2(sel.PYes), P2(1 - sel.PYes))); r.AddLabel(L.T("predict.price", P2(sel.PYes), P2(1 - sel.PYes)));
foreach (var clue in PredictGame.Clues(sel)) r.AddLabel("· " + clue); foreach (var clue in PredictGame.Clues(sel)) r.AddLabel("· " + clue, "pred_clue_dim");
foreach (var pos in bk.PosAll(sel.Id)) foreach (var pos in bk.PosAll(sel.Id))
{ {
var ow = d.Own(pos.Wallet); var ow = d.Own(pos.Wallet);
r.AddLabel(L.T("predict.pos", ow != null ? WalletWindow.NameOf(ow) : pos.Wallet, L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares))); r.AddLabel(L.T("predict.pos", ow != null ? WalletWindow.NameOf(ow) : pos.Wallet, L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares)));
if (pos.Wallet == w.Address) if (pos.Wallet == w.Address)
{ {
r.AddLabel(L.T("predict.pos3", P2(pos.Cost / pos.Shares), P2(sel.PriceOf(pos.Yes)))); r.AddLabel(L.T("predict.pos3", P2(pos.Cost / pos.Shares), P2(sel.PriceOf(pos.Yes))), "pred_pos_dim");
r.AddLabel(L.T("predict.pos2", Fmt.Num(pos.Cost), Fmt.Num(pos.Shares))); r.AddLabel(L.T("predict.pos2", Fmt.Num(pos.Cost), Fmt.Num(pos.Shares)), "pred_pos_dim");
r.AddLabel(L.T("predict.pos4", Fmt.Num(sel.SellValue(pos.Yes, pos.Shares) * (1 - Tuning.PredictFee)))); r.AddLabel(L.T("predict.pos4", Fmt.Num(sel.SellValue(pos.Yes, pos.Shares) * (1 - Tuning.PredictFee))), "pred_pos_dim");
} }
} }
if (!sel.IsOpen(day)) { r.AddLabel(L.T("predict.closed.note")); return; } if (!sel.IsOpen(day)) { r.AddLabel(L.T("predict.closed.note"), "pred_closed_note"); return; }
AmountRow(r); AmountRow(r);
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0 && v < 1e7) if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0 && v < 1e7)
{ {
foreach (bool yes in new[] { true, false }) foreach (bool yes in new[] { true, false })
{ {
double shares = sel.SharesFor(yes, v / (1 + Tuning.PredictFee)); double shares = sel.SharesFor(yes, v / (1 + Tuning.PredictFee));
r.AddLabel(L.T("predict.preview", L.T(yes ? "predict.yes" : "predict.no"), Fmt.Num(shares), P2(v / shares), Fmt.Num(shares - v))); r.AddLabel(L.T("predict.preview", L.T(yes ? "predict.yes" : "predict.no"), Fmt.Num(shares), P2(v / shares), Fmt.Num(shares - v)), "pred_prev_dim");
} }
} }
r.BeginRow(); r.BeginRow(6f);
r.AddButton(L.T("predict.buy.yes"), () => PredictBuy(sel, true)); r.AddButton(L.T("predict.buy.yes"), () => PredictBuy(sel, true));
r.AddButton(L.T("predict.buy.no"), () => PredictBuy(sel, false)); r.AddButton(L.T("predict.buy.no"), () => PredictBuy(sel, false));
r.End(); r.End();
r.AddSpace(4f);
r.AddButton(L.T("predict.sell"), () => PredictSell(sel)); r.AddButton(L.T("predict.sell"), () => PredictSell(sel));
}); });
} }
@@ -783,33 +786,35 @@ namespace Decentralized
Columns(b, l => Columns(b, l =>
{ {
l.AddHeader(L.T("intel.h")); l.AddHeader(L.T("intel.h"));
if (!PredictGame.BrokerOpen) { l.AddLabel(L.T("intel.locked", Tuning.IntelRep)); return; } if (!PredictGame.BrokerOpen) { l.AddLabel(L.T("intel.locked", Tuning.IntelRep), "intel_locked_note"); return; }
l.AddLabel(L.T("predict.acct.crt", WalletWindow.NameOf(w), Fmt.Num(w.Avail(Assets.CRT)))); l.AddLabel(L.T("predict.acct.crt", WalletWindow.NameOf(w), Fmt.Num(w.Avail(Assets.CRT))));
l.AddButton(L.T("intel.brief.buy", Fmt.Num(Tuning.IntelBriefPrice)), IntelBuyBrief); l.AddButton(L.T("intel.brief.buy", Fmt.Num(Tuning.IntelBriefPrice)), IntelBuyBrief);
l.AddSeparator(); l.AddSeparator();
if (d.Brief.Count == 0) l.AddLabel(L.T("intel.brief.none")); if (d.Brief.Count == 0) l.AddLabel(L.T("intel.brief.none"), "intel_brief_dim");
else else
{ {
l.AddLabel(L.T("intel.brief.day", d.BriefDay)); l.AddLabel(L.T("intel.brief.day", d.BriefDay), "intel_day_dim");
foreach (var line in d.Brief) l.AddLabel(line); foreach (var line in d.Brief) l.AddLabel(line, "intel_line_dim");
} }
}, r => }, r =>
{ {
r.AddHeader(L.T("intel.tips.h")); r.AddHeader(L.T("intel.tips.h"));
if (!PredictGame.BrokerOpen) return; if (!PredictGame.BrokerOpen) return;
var list = d.Predict.Markets.Where(m => PredictGame.Tippable(m, day)).OrderBy(m => m.FromDay).ToList(); var list = d.Predict.Markets.Where(m => PredictGame.Tippable(m, day)).OrderBy(m => m.FromDay).ToList();
if (list.Count == 0) r.AddLabel(L.T("intel.tips.none")); if (list.Count == 0) r.AddLabel(L.T("intel.tips.none"), "intel_tips_none_dim");
foreach (var m in list) foreach (var m in list)
{ {
var mk = m; var mk = m;
r.AddLabel(PredictGame.Question(m)); r.AddLabel(PredictGame.Question(m));
var tip = d.Predict.TipFor(m.Id); var tip = d.Predict.TipFor(m.Id);
if (tip != null) { r.AddLabel(L.T("predict.clue.tip", L.T("predict.tier." + tip.Tier), L.T(tip.Claim ? "predict.yes" : "predict.no"))); continue; } if (tip != null) { r.AddLabel(L.T("predict.clue.tip", L.T("predict.tier." + tip.Tier), L.T(tip.Claim ? "predict.yes" : "predict.no")), "intel_tip_dim"); continue; }
r.BeginRow(4f);
for (int t = 0; t < 3; t++) for (int t = 0; t < 3; t++)
{ {
int tier = t; int tier = t;
r.AddButton(L.T("intel.tip.buy", (int)Math.Round(Tuning.IntelTipAcc[t] * 100), Fmt.Num(Tuning.IntelTipPrice[t] * m.MaxLoss)), () => IntelBuyTip(mk, tier)); r.AddButton(L.T("intel.tip.buy", (int)Math.Round(Tuning.IntelTipAcc[t] * 100), Fmt.Num(Tuning.IntelTipPrice[t] * m.MaxLoss)), () => IntelBuyTip(mk, tier));
} }
r.End();
} }
}); });
} }
@@ -1580,6 +1585,7 @@ namespace Decentralized
private static string _mixAsset = Assets.PSC; private static string _mixAsset = Assets.PSC;
private static int _mixRounds = 2; private static int _mixRounds = 2;
private static readonly float[] MixW = { 70f, 100f, 100f, 80f }; private static readonly float[] MixW = { 70f, 100f, 100f, 80f };
private static readonly TextAnchor[] MixAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
/// <summary>混币器:把币存进去,几轮之后提回同一个地址,污点降低,每轮扣手续费。</summary> /// <summary>混币器:把币存进去,几轮之后提回同一个地址,污点降低,每轮扣手续费。</summary>
private static void MixerPage(CustomUIBuilder b) private static void MixerPage(CustomUIBuilder b)
@@ -1590,42 +1596,42 @@ namespace Decentralized
Columns(b, l => Columns(b, l =>
{ {
l.AddHeader(L.T("mixer.h")); l.AddHeader(L.T("mixer.h"));
l.AddLabel(d.Story.MixerClosed ? L.T("mixer.status.closed") : d.Mixer.Sanctioned(day) ? L.T("mixer.sanctioned", d.Mixer.SanctionedUntil - day + 1) : L.T("mixer.ok")); l.AddLabel(d.Story.MixerClosed ? L.T("mixer.status.closed") : d.Mixer.Sanctioned(day) ? L.T("mixer.sanctioned", d.Mixer.SanctionedUntil - day + 1) : L.T("mixer.ok"), d.Story.MixerClosed || d.Mixer.Sanctioned(day) ? "mixer_status_note" : "mixer_status_dim");
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint))); if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)), "taint_note");
l.AddSeparator(); l.AddSeparator();
l.AddHeader(L.T("mixer.pending")); l.AddHeader(L.T("mixer.pending"));
if (d.Mixer.Pending.Count == 0) l.AddLabel(L.T("mixer.none")); if (d.Mixer.Pending.Count == 0) l.AddLabel(L.T("mixer.none"), "mixer_none_dim");
else else
{ {
TableHeader(l, MixW, L.T("col.asset"), L.T("col.amount"), L.T("mixer.col.taint"), L.T("mixer.col.ready")); TableHeader(l, MixW.Sum() + 8f, MixW, MixAligns, L.T("col.asset"), L.T("col.amount"), L.T("mixer.col.taint"), L.T("mixer.col.ready"));
foreach (var p in d.Mixer.Pending) foreach (var p in d.Mixer.Pending)
TableRow(l, MixW, p.Asset, Fmt.Num(p.Amount), Fmt.Credits(p.Taint), L.T("mixer.day", p.ReadyDay)); TableRow(l, MixW.Sum() + 8f, MixW, MixAligns, new[] { p.Asset, Fmt.Num(p.Amount), Fmt.Credits(p.Taint), L.T("mixer.day", p.ReadyDay) });
} }
}, r => }, r =>
{ {
r.AddHeader(L.T("mixer.deposit")); r.AddHeader(L.T("mixer.deposit"));
if (d.Story.MixerClosed) { r.AddLabel(L.T("mixer.closed")); return; } if (d.Story.MixerClosed) { r.AddLabel(L.T("mixer.closed"), "mixer_closed_note"); return; }
r.BeginRow(); r.BeginRow(4f);
foreach (var a in new[] { Assets.PSC, Assets.CRC, Assets.CRT, Assets.CRA }) foreach (var a in new[] { Assets.PSC, Assets.CRC, Assets.CRT, Assets.CRA })
{ {
var asset = a; var asset = a;
r.AddButton(asset == _mixAsset ? $"[{asset}]" : asset, () => { _mixAsset = asset; Open(); }); PledgerSkin.Pick(r, asset, asset == _mixAsset, () => { _mixAsset = asset; Open(); }, _picked);
} }
r.End(); r.End();
r.AddLabel(L.T("mixer.balance", _mixAsset, Fmt.Num(w.Avail(_mixAsset)))); r.AddLabel(L.T("mixer.balance", _mixAsset, Fmt.Num(w.Avail(_mixAsset))));
AmountRow(r); AmountRow(r);
r.AddLabel(L.T("mixer.rounds")); r.AddLabel(L.T("mixer.rounds"));
r.BeginRow(); r.BeginRow(4f);
for (int n = 1; n <= Tuning.MixerMaxRounds; n++) for (int n = 1; n <= Tuning.MixerMaxRounds; n++)
{ {
int rounds = n; int rounds = n;
r.AddButton(rounds == _mixRounds ? $"[{rounds}]" : rounds.ToString(), () => { _mixRounds = rounds; Open(); }); PledgerSkin.Pick(r, rounds.ToString(), rounds == _mixRounds, () => { _mixRounds = rounds; Open(); }, _picked);
} }
r.End(); r.End();
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0) if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0)
{ {
double rate = Mixer.RoundRate(v * Session.Price(_mixAsset)); double rate = Mixer.RoundRate(v * Session.Price(_mixAsset));
r.AddLabel(L.T("mixer.preview", Fmt.Num(v * Math.Pow(1 - Mixer.Fee, _mixRounds)), _mixAsset, (int)Math.Round((1 - Math.Pow(1 - rate, _mixRounds)) * 100), day + _mixRounds)); r.AddLabel(L.T("mixer.preview", Fmt.Num(v * Math.Pow(1 - Mixer.Fee, _mixRounds)), _mixAsset, (int)Math.Round((1 - Math.Pow(1 - rate, _mixRounds)) * 100), day + _mixRounds), "mixer_preview_dim");
} }
r.AddButton(L.T("mixer.go"), () => r.AddButton(L.T("mixer.go"), () =>
{ {
@@ -1755,15 +1761,15 @@ namespace Decentralized
private static void ReportPage(CustomUIBuilder b) private static void ReportPage(CustomUIBuilder b)
{ {
var r = Session.Data.LastNight; var r = Session.Data.LastNight;
if (r == null) { b.AddLabel(L.T("report.none")); return; } if (r == null) { b.AddLabel(L.T("report.none"), "report_none_dim"); return; }
b.AddHeader(L.T("report.day", r.Day)); b.AddHeader(L.T("report.day", r.Day));
b.AddLabel(L.T("report.rigs", r.PoweredRigs, r.Rigs)); b.AddLabel(L.T("report.rigs", r.PoweredRigs, r.Rigs), "report_dim");
b.AddLabel(L.T("report.hash", Fmt.Num(r.Hash * 10), Fmt.Num(r.NetworkHash * 10))); b.AddLabel(L.T("report.hash", Fmt.Num(r.Hash * 10), Fmt.Num(r.NetworkHash * 10)), "report_dim");
b.AddLabel(L.T("report.mined", Fmt.Num(r.Mined), Fmt.Credits(r.Mined * r.Price))); b.AddLabel(L.T("report.mined", Fmt.Num(r.Mined), Fmt.Credits(r.Mined * r.Price)));
b.AddLabel(L.T("report.energy", Fmt.Num(r.Kj))); b.AddLabel(L.T("report.energy", Fmt.Num(r.Kj)), "report_dim");
b.AddLabel(L.T("report.burnt", r.Burnt.Count)); b.AddLabel(L.T("report.burnt", r.Burnt.Count), r.Burnt.Count > 0 ? "report_burnt_note" : "report_burnt_dim");
foreach (var n in r.Burnt) b.AddLabel(" " + n); foreach (var n in r.Burnt) b.AddLabel(" " + n, "report_burnt_note");
b.AddLabel(L.T("report.price", Fmt.Num(r.Price))); b.AddLabel(L.T("report.price", Fmt.Num(r.Price)), "report_dim");
} }
private static bool ReadAmount(out double v) private static bool ReadAmount(out double v)