openvibers/openvibe.live Commits
Chat tools parity: vibes/points/TTS/subscribe on offline chat + popout
Changes
@@@ -977,6 +977,28 @@<button class="emote-picker-btn" onclick="openChannelUploadModal()" title="Add channel emote / sound"><i class="fa-solid fa-plus"></i></button>++ <button class="chat-tts-toggle tts-active" onclick="toggleChatTTS(this)" title="TTS On (click to mute)">++ <i class="fa-solid fa-volume-high"></i>++ </button>++ <button class="btn btn-openvibe-coins-mini chat-nickels-float" onclick="toggleRewardsPanel()" title="OpenCoins — this channel's points (click for rewards)">++ <i class="fa-solid fa-coins"></i> <span class="rewards-coin-balance">0</span>++ </button>++ <button class="btn btn-openvibe-bucks-mini" onclick="showModal('donate')" title="Send Vibes">++ <i class="fa-solid fa-dollar-sign"></i>++ </button>++ <button class="btn btn-openvibe-bucks-mini" onclick="openSubscribeForCurrentStream()" title="Subscribe to this channel">++ <i class="fa-solid fa-star"></i>++ </button>++ </div>++ <!-- Rewards Panel (channel points) — offline chat's own instance -->++ <div class="rewards-panel" style="display:none">++ <div class="rewards-panel-header">++ <span><i class="fa-solid fa-coins rewards-cp-icon"></i> <span class="rewards-cp-label">Channel Points</span> Rewards</span>++ <span class="rewards-coin-bal"><i class="fa-solid fa-coins"></i> <span class="rewards-coin-balance">0</span></span>++ </div>++ <div class="rewards-grid ch-rewards-grid">++ <p class="muted">No rewards configured for this channel</p>++ </div></div></div></div>@@@ -115,9 +115,13 @@ function toggleRewardsPanel() {}async function loadRewardsPanel() {-- if (!currentStreamData) {++ // Channel points are per-CHANNEL, not per live session — resolve the owner from++ // the live stream OR the channel context (offline channel page / popout chat).++ const ownerId = (typeof currentStreamData !== 'undefined' && currentStreamData && currentStreamData.user_id)++ || (typeof _activeChannelUserId !== 'undefined' && _activeChannelUserId) || null;++ if (!ownerId) {document.querySelectorAll('.rewards-grid').forEach(g => {-- g.innerHTML = '<p class="muted" style="padding:8px">Join a stream to see rewards</p>';++ g.innerHTML = '<p class="muted" style="padding:8px">Join a channel to see rewards</p>';});return;}@@@ -125,7 +129,7 @@ async function loadRewardsPanel() {if (!grids.length) return;try {-- const data = await api(`/coins/rewards/${currentStreamData.user_id}`);++ const data = await api(`/coins/rewards/${ownerId}`);const rewards = data.rewards || [];_applyChannelPointsBranding(data.config);@@@ -152,7 +156,7 @@ async function loadRewardsPanel() {// Update this channel's points balance in all reward panelstry {-- const coinData = await api(`/coins/channel-balance?streamerId=${currentStreamData.user_id}`);++ const coinData = await api(`/coins/channel-balance?streamerId=${ownerId}`);document.querySelectorAll('.rewards-coin-balance').forEach(el => {el.textContent = (coinData.balance || 0).toLocaleString();});@@@ -171,7 +171,10 @@ function showSubscribeModal(username) {function openSubscribeForCurrentStream() {const d = (typeof currentStreamData !== 'undefined' && currentStreamData) || {};-- const username = d.username || d.user_username || d.streamer_username || d.channel_username || d.owner_username;++ const username = d.username || d.user_username || d.streamer_username || d.channel_username || d.owner_username++ // Offline channel page / popout chat — the channel identity lives here instead.++ || (typeof currentChannelUsername !== 'undefined' && currentChannelUsername)++ || (typeof chatChannel !== 'undefined' && chatChannel) || null;if (!username) return toast('No channel selected', 'error');showSubscribeModal(username);}@@@ -171,12 +171,15 @@<button class="chat-tts-toggle tts-active" id="pc-tts-btn" onclick="toggleChatTTS(this)" title="TTS On (click to mute)"><i class="fa-solid fa-volume-high"></i></button>-- <button class="btn btn-openvibe-coins-mini" onclick="if(typeof toggleRewardsPanel==='function')toggleRewardsPanel()" title="Redeem OpenCoins">-- <i class="fa-solid fa-coins"></i>++ <button class="btn btn-openvibe-coins-mini chat-nickels-float" onclick="if(typeof toggleRewardsPanel==='function')toggleRewardsPanel()" title="Channel points (click for rewards)">++ <i class="fa-solid fa-coins"></i> <span class="rewards-coin-balance">0</span></button><button class="btn btn-openvibe-bucks-mini" onclick="if(typeof showModal==='function')showModal('donate')" title="Send Vibes"><i class="fa-solid fa-dollar-sign"></i></button>++ <button class="btn btn-openvibe-bucks-mini" onclick="if(typeof openSubscribeForCurrentStream==='function')openSubscribeForCurrentStream()" title="Subscribe to this channel">++ <i class="fa-solid fa-star"></i>++ </button></div><!-- Rewards Panel (OpenCoins) --><div class="rewards-panel" id="ch-rewards-panel" style="display:none">
@@ -977,6 +977,28 @@
@@ -977,6 +977,28 @@
<button class="emote-picker-btn" onclick="openChannelUploadModal()" title="Add channel emote / sound">
<button class="emote-picker-btn" onclick="openChannelUploadModal()" title="Add channel emote / sound">
<i class="fa-solid fa-plus"></i>
<i class="fa-solid fa-plus"></i>
</button>
</button>
+ <button class="chat-tts-toggle tts-active" onclick="toggleChatTTS(this)" title="TTS On (click to mute)">
+ <i class="fa-solid fa-volume-high"></i>
+ </button>
+ <button class="btn btn-openvibe-coins-mini chat-nickels-float" onclick="toggleRewardsPanel()" title="OpenCoins — this channel's points (click for rewards)">
+ <i class="fa-solid fa-coins"></i> <span class="rewards-coin-balance">0</span>
+ </button>
+ <button class="btn btn-openvibe-bucks-mini" onclick="showModal('donate')" title="Send Vibes">
+ <i class="fa-solid fa-dollar-sign"></i>
+ </button>
+ <button class="btn btn-openvibe-bucks-mini" onclick="openSubscribeForCurrentStream()" title="Subscribe to this channel">
+ <i class="fa-solid fa-star"></i>
+ </button>
+ </div>
+ <!-- Rewards Panel (channel points) — offline chat's own instance -->
+ <div class="rewards-panel" style="display:none">
+ <div class="rewards-panel-header">
+ <span><i class="fa-solid fa-coins rewards-cp-icon"></i> <span class="rewards-cp-label">Channel Points</span> Rewards</span>
+ <span class="rewards-coin-bal"><i class="fa-solid fa-coins"></i> <span class="rewards-coin-balance">0</span></span>
+ </div>
+ <div class="rewards-grid ch-rewards-grid">
+ <p class="muted">No rewards configured for this channel</p>
+ </div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -115,9 +115,13 @@ function toggleRewardsPanel() {
@@ -115,9 +115,13 @@ function toggleRewardsPanel() {
}
}
async function loadRewardsPanel() {
async function loadRewardsPanel() {
- if (!currentStreamData) {
+ // Channel points are per-CHANNEL, not per live session — resolve the owner from
+ // the live stream OR the channel context (offline channel page / popout chat).
+ const ownerId = (typeof currentStreamData !== 'undefined' && currentStreamData && currentStreamData.user_id)
+ || (typeof _activeChannelUserId !== 'undefined' && _activeChannelUserId) || null;
+ if (!ownerId) {
document.querySelectorAll('.rewards-grid').forEach(g => {
document.querySelectorAll('.rewards-grid').forEach(g => {
- g.innerHTML = '<p class="muted" style="padding:8px">Join a stream to see rewards</p>';
+ g.innerHTML = '<p class="muted" style="padding:8px">Join a channel to see rewards</p>';
});
});
return;
return;
}
}
@@ -125,7 +129,7 @@ async function loadRewardsPanel() {
@@ -125,7 +129,7 @@ async function loadRewardsPanel() {
if (!grids.length) return;
if (!grids.length) return;
try {
try {
- const data = await api(`/coins/rewards/${currentStreamData.user_id}`);
+ const data = await api(`/coins/rewards/${ownerId}`);
const rewards = data.rewards || [];
const rewards = data.rewards || [];
_applyChannelPointsBranding(data.config);
_applyChannelPointsBranding(data.config);
@@ -152,7 +156,7 @@ async function loadRewardsPanel() {
@@ -152,7 +156,7 @@ async function loadRewardsPanel() {
// Update this channel's points balance in all reward panels
// Update this channel's points balance in all reward panels
try {
try {
- const coinData = await api(`/coins/channel-balance?streamerId=${currentStreamData.user_id}`);
+ const coinData = await api(`/coins/channel-balance?streamerId=${ownerId}`);
document.querySelectorAll('.rewards-coin-balance').forEach(el => {
document.querySelectorAll('.rewards-coin-balance').forEach(el => {
el.textContent = (coinData.balance || 0).toLocaleString();
el.textContent = (coinData.balance || 0).toLocaleString();
});
});
@@ -171,7 +171,10 @@ function showSubscribeModal(username) {
@@ -171,7 +171,10 @@ function showSubscribeModal(username) {
function openSubscribeForCurrentStream() {
function openSubscribeForCurrentStream() {
const d = (typeof currentStreamData !== 'undefined' && currentStreamData) || {};
const d = (typeof currentStreamData !== 'undefined' && currentStreamData) || {};
- const username = d.username || d.user_username || d.streamer_username || d.channel_username || d.owner_username;
+ const username = d.username || d.user_username || d.streamer_username || d.channel_username || d.owner_username
+ // Offline channel page / popout chat — the channel identity lives here instead.
+ || (typeof currentChannelUsername !== 'undefined' && currentChannelUsername)
+ || (typeof chatChannel !== 'undefined' && chatChannel) || null;
if (!username) return toast('No channel selected', 'error');
if (!username) return toast('No channel selected', 'error');
showSubscribeModal(username);
showSubscribeModal(username);
}
}
@@ -171,12 +171,15 @@
@@ -171,12 +171,15 @@
<button class="chat-tts-toggle tts-active" id="pc-tts-btn" onclick="toggleChatTTS(this)" title="TTS On (click to mute)">
<button class="chat-tts-toggle tts-active" id="pc-tts-btn" onclick="toggleChatTTS(this)" title="TTS On (click to mute)">
<i class="fa-solid fa-volume-high"></i>
<i class="fa-solid fa-volume-high"></i>
</button>
</button>
- <button class="btn btn-openvibe-coins-mini" onclick="if(typeof toggleRewardsPanel==='function')toggleRewardsPanel()" title="Redeem OpenCoins">
- <i class="fa-solid fa-coins"></i>
+ <button class="btn btn-openvibe-coins-mini chat-nickels-float" onclick="if(typeof toggleRewardsPanel==='function')toggleRewardsPanel()" title="Channel points (click for rewards)">
+ <i class="fa-solid fa-coins"></i> <span class="rewards-coin-balance">0</span>
</button>
</button>
<button class="btn btn-openvibe-bucks-mini" onclick="if(typeof showModal==='function')showModal('donate')" title="Send Vibes">
<button class="btn btn-openvibe-bucks-mini" onclick="if(typeof showModal==='function')showModal('donate')" title="Send Vibes">
<i class="fa-solid fa-dollar-sign"></i>
<i class="fa-solid fa-dollar-sign"></i>
</button>
</button>
+ <button class="btn btn-openvibe-bucks-mini" onclick="if(typeof openSubscribeForCurrentStream==='function')openSubscribeForCurrentStream()" title="Subscribe to this channel">
+ <i class="fa-solid fa-star"></i>
+ </button>
</div>
</div>
<!-- Rewards Panel (OpenCoins) -->
<!-- Rewards Panel (OpenCoins) -->
<div class="rewards-panel" id="ch-rewards-panel" style="display:none">
<div class="rewards-panel" id="ch-rewards-panel" style="display:none">