mirror of
https://github.com/hoornet/vega.git
synced 2026-05-07 04:39:12 -07:00
Add V4V sidebar section with dashboard, settings, and history
- New V4V store with budget tracking, cap enforcement, and history - Dashboard: live streaming status, per-episode + weekly budget bars, stats - Settings: auto-enable toggle (guarded by caps), per-episode cap, weekly budget, default rate, info icons - History: expandable list of past V4V episodes with recipient breakdowns - Budget enforcement: streaming stops with toast when cap is hit - Auto-streaming: starts automatically for V4V episodes when enabled - Cap reached state: dashboard shows red card, ticker shows "(capped)" - V4VIndicator: slimmed popup, AUTO badge, "open v4v" nav link - Fix duplicate history entries on cap stop
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
import type { PodcastEpisode, V4VRecipient } from "../../types/podcast";
|
||||
import { payInvoiceViaNWC, payKeysendViaNWC } from "../lightning/nwc";
|
||||
import { useV4VStore } from "../../stores/v4v";
|
||||
import { useToastStore } from "../../stores/toast";
|
||||
|
||||
const LNURL_CACHE: Record<string, string> = {};
|
||||
|
||||
@@ -74,6 +76,8 @@ async function payRecipient(
|
||||
let streamingInterval: number | null = null;
|
||||
let accumulatedSats = 0;
|
||||
let accumulatedMinutes = 0;
|
||||
let currentStreamingEpisode: PodcastEpisode | null = null;
|
||||
let sessionRecipientSats: Record<string, number> = {};
|
||||
|
||||
export function startStreaming(
|
||||
episode: PodcastEpisode,
|
||||
@@ -85,14 +89,31 @@ export function startStreaming(
|
||||
|
||||
accumulatedSats = 0;
|
||||
accumulatedMinutes = 0;
|
||||
currentStreamingEpisode = episode;
|
||||
sessionRecipientSats = {};
|
||||
const recipients = getRecipients(episode);
|
||||
|
||||
if (recipients.length === 0) return -1;
|
||||
|
||||
const v4vStore = useV4VStore.getState();
|
||||
v4vStore.resetCurrentEpisodeSats();
|
||||
|
||||
// Normalize splits to sum to 100
|
||||
const totalSplit = recipients.reduce((sum, r) => sum + r.split, 0);
|
||||
|
||||
streamingInterval = window.setInterval(async () => {
|
||||
// Check budget caps before accumulating
|
||||
const v4v = useV4VStore.getState();
|
||||
if (v4v.isCapReached()) {
|
||||
const reason = v4v.perEpisodeCap > 0 && v4v.currentEpisodeSats >= v4v.perEpisodeCap
|
||||
? "Per-episode cap reached"
|
||||
: "Weekly budget reached";
|
||||
useToastStore.getState().addToast(reason, "warning", 6000);
|
||||
v4v.setCapReachedReason(reason);
|
||||
stopStreaming();
|
||||
return;
|
||||
}
|
||||
|
||||
accumulatedMinutes += 1;
|
||||
accumulatedSats += satsPerMinute;
|
||||
|
||||
@@ -110,7 +131,12 @@ export function startStreaming(
|
||||
|
||||
try {
|
||||
const success = await payRecipient(recipient, amountMsats, nwcUri);
|
||||
if (success) onPayment(recipientSats);
|
||||
if (success) {
|
||||
onPayment(recipientSats);
|
||||
useV4VStore.getState().addCurrentEpisodeSats(recipientSats);
|
||||
const key = recipient.name || recipient.address || "unknown";
|
||||
sessionRecipientSats[key] = (sessionRecipientSats[key] || 0) + recipientSats;
|
||||
}
|
||||
} catch {
|
||||
// Payment failed — silently continue
|
||||
}
|
||||
@@ -120,13 +146,41 @@ export function startStreaming(
|
||||
return streamingInterval;
|
||||
}
|
||||
|
||||
function recordHistory() {
|
||||
const episode = currentStreamingEpisode;
|
||||
if (!episode) return;
|
||||
|
||||
const v4v = useV4VStore.getState();
|
||||
const totalStreamed = v4v.currentEpisodeSats;
|
||||
if (totalStreamed <= 0) return;
|
||||
|
||||
const recipients = Object.entries(sessionRecipientSats).map(([name, sats]) => ({
|
||||
name,
|
||||
address: "",
|
||||
sats,
|
||||
}));
|
||||
|
||||
v4v.addHistoryEntry({
|
||||
episodeGuid: episode.guid,
|
||||
episodeTitle: episode.title,
|
||||
showTitle: episode.showTitle || "",
|
||||
satsStreamed: totalStreamed,
|
||||
satsBoosted: 0,
|
||||
recipients,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
export function stopStreaming() {
|
||||
if (streamingInterval !== null) {
|
||||
recordHistory();
|
||||
clearInterval(streamingInterval);
|
||||
streamingInterval = null;
|
||||
}
|
||||
accumulatedSats = 0;
|
||||
accumulatedMinutes = 0;
|
||||
currentStreamingEpisode = null;
|
||||
sessionRecipientSats = {};
|
||||
}
|
||||
|
||||
export async function boost(
|
||||
|
||||
Reference in New Issue
Block a user