openvibers/openvibe.live Commits

Donate modal: zero balance no longer hides the direct PowerChat route

OP OpenVibers committed 27 Aug 2026 · dfcc18ce8b · 3 files, +25 −25

The balance-first redirect fired before the direct-tip availability check
even resolved, so a broke viewer on a PowerChat-connected channel got
bounced to Buy Vibes with no way to tip directly. The modal now resolves
balance and direct availability together: with 0 Vibes on a connected
channel you get BOTH options (tip directly, or buy Vibes first, with a
buy link inline in the balance line); the auto-redirect to Buy Vibes only
happens when there is no direct route.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AyeSFu7qdLdR64Zv99QNm

Changes

ddiff --git a/public/index.html b/public/index.html
iindex 5e54f658..b6229e97 100644
---- a/public/index.html
++++ b/public/index.html
@@@ -5051,7 +5051,7 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
</div>
</div>
--<script src="/js/vibes.js?v=9" defer></script>
++<script src="/js/vibes.js?v=10" defer></script>
<script src="/js/opencoins.js?v=6" defer></script>
<script src="/js/cosmetics-ui.js?v=2" defer></script>
<!-- Broadcast modules — load order matters: state → devices → vods → workspace → core -->
ddiff --git a/public/js/vibes.js b/public/js/vibes.js
iindex dcd0bf3d..dbf40797 100644
---- a/public/js/vibes.js
++++ b/public/js/vibes.js
@@@ -230,31 +230,31 @@ function openvibeBucksDonateModal() {
</div>`;
}
--// Donate modal boot: balance-first. No Vibes → send the viewer straight to Buy Vibes
--// (works on every channel; includes the PowerChat purchase rail); with a balance,
--// show it. Separately, streamers with their OWN PowerChat get the direct-tip option.
++// Donate modal boot: balance-first, but NEVER at the cost of the direct route.
++// - Balance ≥ 1: normal Vibes donate form (+ the direct-PowerChat option when the
++// streamer has their own PowerChat).
++// - Balance 0 + streamer HAS PowerChat: stay here and offer BOTH — tip them
++// directly (no Vibes needed) or go buy Vibes first.
++// - Balance 0 + no direct route: straight to Buy Vibes (any channel, all rails).
async function _initDonateModal() {
-- try {
-- const data = await api('/funds/balance');
-- const bal = Math.round(data.balance || 0);
-- const line = document.getElementById('donate-balance-line');
-- if (line) line.innerHTML = `Your balance: <strong>${bal.toLocaleString()}</strong> Vibes`;
-- if (bal < 1) {
-- toast('You don’t have any Vibes yet — grab some first, then come back to donate! 🛒', 'info');
-- showModal('buy-funds');
-- return;
-- }
-- } catch { /* not logged in / balance unavailable — leave the form as-is */ }
-- _initDonatePowerchat();
--}
--async function _initDonatePowerchat() {
-- const box = document.getElementById('donate-powerchat-direct');
const streamerId = _donateStreamerId();
-- if (!box || !streamerId) return;
-- try {
-- const d = await api(`/powerchat/donate-link?streamer_id=${streamerId}`);
-- if (d && d.mode === 'direct') box.style.display = '';
-- } catch { /* streamer has no PowerChat — keep hidden */ }
++ const [bal, directAvailable] = await Promise.all([
++ api('/funds/balance').then(d => Math.round(d.balance || 0)).catch(() => null),
++ streamerId
++ ? api(`/powerchat/donate-link?streamer_id=${streamerId}`).then(d => !!(d && d.mode === 'direct')).catch(() => false)
++ : Promise.resolve(false),
++ ]);
++ const box = document.getElementById('donate-powerchat-direct');
++ if (box && directAvailable) box.style.display = '';
++ const line = document.getElementById('donate-balance-line');
++ if (bal === null) return; // not logged in / balance unavailable — leave the form as-is
++ if (line) line.innerHTML = `Your balance: <strong>${bal.toLocaleString()}</strong> Vibes${bal < 1 ? ' — <a href="#" onclick="showModal(\'buy-funds\');return false">buy some</a> to donate Vibes' : ''}`;
++ if (bal < 1 && !directAvailable) {
++ toast('You don’t have any Vibes yet — grab some first, then come back to donate! 🛒', 'info');
++ showModal('buy-funds');
++ } else if (bal < 1 && directAvailable) {
++ toast('No Vibes yet — tip this channel directly via PowerChat below, or buy Vibes first. ⚡', 'info');
++ }
}
// DIRECT real-money tip — only offered when the streamer has their own PowerChat.
ddiff --git a/public/popout-chat.html b/public/popout-chat.html
iindex d2c649ef..912bb87c 100644
---- a/public/popout-chat.html
++++ b/public/popout-chat.html
@@@ -230,7 +230,7 @@
<script src="/js/emotes.js?v=8"></script>
<script src="/js/gif-picker.js?v=1"></script>
<script src="/js/channel-uploads.js?v=12"></script>
-- <script src="/js/vibes.js?v=9"></script>
++ <script src="/js/vibes.js?v=10"></script>
<script src="/js/opencoins.js?v=6"></script>
<script src="/js/cosmetics-ui.js?v=2"></script>
<script src="/js/chat.js?v=75"></script>