chore: formatting
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
import { Button as AlbyZapsButton } from "@getalby/bitcoin-connect-react";
|
import { Button as AlbyZapsButton } from "@getalby/bitcoin-connect-react";
|
||||||
|
|
||||||
export default function AlbyButton() {
|
export default function AlbyButton() {
|
||||||
return <AlbyZapsButton />
|
return <AlbyZapsButton />;
|
||||||
}
|
}
|
||||||
|
@ -176,15 +176,15 @@ export function ChatMessage({
|
|||||||
style={
|
style={
|
||||||
isTablet
|
isTablet
|
||||||
? {
|
? {
|
||||||
display: showZapDialog || isHovering ? "flex" : "none",
|
display: showZapDialog || isHovering ? "flex" : "none",
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
top: topOffset ? topOffset - 12 : 0,
|
top: topOffset ? topOffset - 12 : 0,
|
||||||
left: leftOffset ? leftOffset - 32 : 0,
|
left: leftOffset ? leftOffset - 32 : 0,
|
||||||
opacity: showZapDialog || isHovering ? 1 : 0,
|
opacity: showZapDialog || isHovering ? 1 : 0,
|
||||||
pointerEvents: showZapDialog || isHovering ? "auto" : "none",
|
pointerEvents: showZapDialog || isHovering ? "auto" : "none",
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
{zapTarget && (
|
{zapTarget && (
|
||||||
<SendZapsDialog
|
<SendZapsDialog
|
||||||
|
@ -14,214 +14,214 @@ import { Profile } from "./profile";
|
|||||||
import { StatePill } from "./state-pill";
|
import { StatePill } from "./state-pill";
|
||||||
|
|
||||||
interface StatSlot {
|
interface StatSlot {
|
||||||
time: number;
|
time: number;
|
||||||
zaps: number;
|
zaps: number;
|
||||||
messages: number;
|
messages: number;
|
||||||
reactions: number;
|
reactions: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StreamSummary({ link, preload }: { link: NostrLink; preload?: NostrEvent }) {
|
export default function StreamSummary({ link, preload }: { link: NostrLink; preload?: NostrEvent }) {
|
||||||
const ev = useCurrentStreamFeed(link, true, preload);
|
const ev = useCurrentStreamFeed(link, true, preload);
|
||||||
const thisLink = ev ? NostrLink.fromEvent(ev) : undefined;
|
const thisLink = ev ? NostrLink.fromEvent(ev) : undefined;
|
||||||
const data = useLiveChatFeed(thisLink, undefined, 5_000);
|
const data = useLiveChatFeed(thisLink, undefined, 5_000);
|
||||||
const reactions = useEventReactions(thisLink ?? link, data.reactions);
|
const reactions = useEventReactions(thisLink ?? link, data.reactions);
|
||||||
|
|
||||||
const chatSummary = useMemo(() => {
|
const chatSummary = useMemo(() => {
|
||||||
return Object.entries(
|
return Object.entries(
|
||||||
data.messages.reduce((acc, v) => {
|
data.messages.reduce((acc, v) => {
|
||||||
acc[v.pubkey] ??= [];
|
acc[v.pubkey] ??= [];
|
||||||
acc[v.pubkey].push(v);
|
acc[v.pubkey].push(v);
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, Array<NostrEvent>>)
|
}, {} as Record<string, Array<NostrEvent>>)
|
||||||
)
|
)
|
||||||
.map(([k, v]) => ({
|
.map(([k, v]) => ({
|
||||||
pubkey: k,
|
pubkey: k,
|
||||||
messages: v,
|
messages: v,
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => (a.messages.length > b.messages.length ? -1 : 1));
|
.sort((a, b) => (a.messages.length > b.messages.length ? -1 : 1));
|
||||||
}, [data.messages]);
|
}, [data.messages]);
|
||||||
|
|
||||||
const zapsSummary = useMemo(() => {
|
const zapsSummary = useMemo(() => {
|
||||||
return Object.entries(
|
return Object.entries(
|
||||||
reactions.zaps.reduce((acc, v) => {
|
reactions.zaps.reduce((acc, v) => {
|
||||||
if (!v.sender) return acc;
|
if (!v.sender) return acc;
|
||||||
acc[v.sender] ??= [];
|
acc[v.sender] ??= [];
|
||||||
acc[v.sender].push(v);
|
acc[v.sender].push(v);
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, Array<ParsedZap>>)
|
}, {} as Record<string, Array<ParsedZap>>)
|
||||||
)
|
)
|
||||||
.map(([k, v]) => ({
|
.map(([k, v]) => ({
|
||||||
pubkey: k,
|
pubkey: k,
|
||||||
zaps: v,
|
zaps: v,
|
||||||
total: v.reduce((acc, vv) => acc + vv.amount, 0),
|
total: v.reduce((acc, vv) => acc + vv.amount, 0),
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => (a.total > b.total ? -1 : 1));
|
.sort((a, b) => (a.total > b.total ? -1 : 1));
|
||||||
}, [reactions.zaps]);
|
}, [reactions.zaps]);
|
||||||
|
|
||||||
const title = findTag(ev, "title");
|
const title = findTag(ev, "title");
|
||||||
const summary = findTag(ev, "summary");
|
const summary = findTag(ev, "summary");
|
||||||
const status = findTag(ev, "status");
|
const status = findTag(ev, "status");
|
||||||
const starts = findTag(ev, "starts");
|
const starts = findTag(ev, "starts");
|
||||||
|
|
||||||
const Day = 60 * 60 * 24;
|
const Day = 60 * 60 * 24;
|
||||||
const startTime = starts ? Number(starts) : ev?.created_at ?? unixNow();
|
const startTime = starts ? Number(starts) : ev?.created_at ?? unixNow();
|
||||||
const endTime = status === StreamState.Live ? unixNow() : ev?.created_at ?? unixNow();
|
const endTime = status === StreamState.Live ? unixNow() : ev?.created_at ?? unixNow();
|
||||||
|
|
||||||
const streamLength = endTime - startTime;
|
const streamLength = endTime - startTime;
|
||||||
const windowSize = streamLength > Day ? Day : 60 * 10;
|
const windowSize = streamLength > Day ? Day : 60 * 10;
|
||||||
|
|
||||||
const stats = useMemo(() => {
|
const stats = useMemo(() => {
|
||||||
let min = unixNow();
|
let min = unixNow();
|
||||||
let max = 0;
|
let max = 0;
|
||||||
const ret = [...data.messages, ...data.reactions]
|
const ret = [...data.messages, ...data.reactions]
|
||||||
.sort((a, b) => (a.created_at > b.created_at ? -1 : 1))
|
.sort((a, b) => (a.created_at > b.created_at ? -1 : 1))
|
||||||
.reduce((acc, v) => {
|
.reduce((acc, v) => {
|
||||||
const time = Math.floor(v.created_at - (v.created_at % windowSize));
|
const time = Math.floor(v.created_at - (v.created_at % windowSize));
|
||||||
if (time < min) {
|
if (time < min) {
|
||||||
min = time;
|
min = time;
|
||||||
}
|
|
||||||
if (time > max) {
|
|
||||||
max = time;
|
|
||||||
}
|
|
||||||
const key = time.toString();
|
|
||||||
acc[key] ??= {
|
|
||||||
time,
|
|
||||||
zaps: 0,
|
|
||||||
messages: 0,
|
|
||||||
reactions: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (v.kind === LIVE_STREAM_CHAT) {
|
|
||||||
acc[key].messages++;
|
|
||||||
} else if (v.kind === EventKind.ZapReceipt) {
|
|
||||||
acc[key].zaps++;
|
|
||||||
} else if (v.kind === EventKind.Reaction) {
|
|
||||||
acc[key].reactions++;
|
|
||||||
} else {
|
|
||||||
console.debug("Uncounted stat", v);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, StatSlot>);
|
|
||||||
|
|
||||||
// fill empty time slots
|
|
||||||
for (let x = min; x < max; x += windowSize) {
|
|
||||||
ret[x.toString()] ??= {
|
|
||||||
time: x,
|
|
||||||
zaps: 0,
|
|
||||||
messages: 0,
|
|
||||||
reactions: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return ret;
|
if (time > max) {
|
||||||
}, [data]);
|
max = time;
|
||||||
|
}
|
||||||
|
const key = time.toString();
|
||||||
|
acc[key] ??= {
|
||||||
|
time,
|
||||||
|
zaps: 0,
|
||||||
|
messages: 0,
|
||||||
|
reactions: 0,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
if (v.kind === LIVE_STREAM_CHAT) {
|
||||||
<div className="stream-summary">
|
acc[key].messages++;
|
||||||
<h1>{title}</h1>
|
} else if (v.kind === EventKind.ZapReceipt) {
|
||||||
<p>{summary}</p>
|
acc[key].zaps++;
|
||||||
<div className="flex gap-1">
|
} else if (v.kind === EventKind.Reaction) {
|
||||||
<StatePill state={status as StreamState} />
|
acc[key].reactions++;
|
||||||
{streamLength > 0 && (
|
} else {
|
||||||
<FormattedMessage
|
console.debug("Uncounted stat", v);
|
||||||
defaultMessage="Stream Duration {duration} mins"
|
}
|
||||||
id="J/+m9y"
|
return acc;
|
||||||
values={{
|
}, {} as Record<string, StatSlot>);
|
||||||
duration: <FormattedNumber value={streamLength / 60} maximumFractionDigits={2} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<h2>
|
|
||||||
<FormattedMessage defaultMessage="Summary" id="RrCui3" />
|
|
||||||
</h2>
|
|
||||||
<ResponsiveContainer height={200}>
|
|
||||||
<BarChart data={Object.values(stats)} margin={{ left: 0, right: 0 }} style={{ userSelect: "none" }}>
|
|
||||||
<XAxis tick={false} />
|
|
||||||
<YAxis />
|
|
||||||
<Bar dataKey="messages" fill="green" stackId="" />
|
|
||||||
<Bar dataKey="zaps" fill="yellow" stackId="" />
|
|
||||||
<Bar dataKey="reactions" fill="red" stackId="" />
|
|
||||||
<Tooltip
|
|
||||||
cursor={{ fill: "rgba(255,255,255,0.2)" }}
|
|
||||||
content={({ active, payload }) => {
|
|
||||||
if (active && payload && payload.length) {
|
|
||||||
const data = payload[0].payload as StatSlot;
|
|
||||||
return (
|
|
||||||
<div className="plain-paper flex flex-col gap-2">
|
|
||||||
<div>
|
|
||||||
<FormattedDate value={data.time * 1000} timeStyle="short" dateStyle="short" />
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div>
|
|
||||||
<FormattedMessage defaultMessage="Messages" id="hMzcSq" />
|
|
||||||
</div>
|
|
||||||
<div>{data.messages}</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div>
|
|
||||||
<FormattedMessage defaultMessage="Reactions" id="XgWvGA" />
|
|
||||||
</div>
|
|
||||||
<div>{data.reactions}</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div>
|
|
||||||
<FormattedMessage defaultMessage="Zaps" id="OEW7yJ" />
|
|
||||||
</div>
|
|
||||||
<div>{data.zaps}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</BarChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
|
|
||||||
<div className="flex gap-1">
|
// fill empty time slots
|
||||||
<div className="plain-paper flex-1">
|
for (let x = min; x < max; x += windowSize) {
|
||||||
<h3>
|
ret[x.toString()] ??= {
|
||||||
<FormattedMessage defaultMessage="Top Chatters" id="GGaJMU" />
|
time: x,
|
||||||
</h3>
|
zaps: 0,
|
||||||
<div className="flex flex-col gap-2">
|
messages: 0,
|
||||||
{chatSummary.slice(0, 5).map(a => (
|
reactions: 0,
|
||||||
<div className="flex justify-between items-center" key={a.pubkey}>
|
};
|
||||||
<Profile pubkey={a.pubkey} />
|
}
|
||||||
<div>
|
return ret;
|
||||||
<FormattedMessage
|
}, [data]);
|
||||||
defaultMessage="{n} messages"
|
|
||||||
id="gzsn7k"
|
return (
|
||||||
values={{
|
<div className="stream-summary">
|
||||||
n: <FormattedNumber value={a.messages.length} />,
|
<h1>{title}</h1>
|
||||||
}}
|
<p>{summary}</p>
|
||||||
/>
|
<div className="flex gap-1">
|
||||||
</div>
|
<StatePill state={status as StreamState} />
|
||||||
</div>
|
{streamLength > 0 && (
|
||||||
))}
|
<FormattedMessage
|
||||||
|
defaultMessage="Stream Duration {duration} mins"
|
||||||
|
id="J/+m9y"
|
||||||
|
values={{
|
||||||
|
duration: <FormattedNumber value={streamLength / 60} maximumFractionDigits={2} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<h2>
|
||||||
|
<FormattedMessage defaultMessage="Summary" id="RrCui3" />
|
||||||
|
</h2>
|
||||||
|
<ResponsiveContainer height={200}>
|
||||||
|
<BarChart data={Object.values(stats)} margin={{ left: 0, right: 0 }} style={{ userSelect: "none" }}>
|
||||||
|
<XAxis tick={false} />
|
||||||
|
<YAxis />
|
||||||
|
<Bar dataKey="messages" fill="green" stackId="" />
|
||||||
|
<Bar dataKey="zaps" fill="yellow" stackId="" />
|
||||||
|
<Bar dataKey="reactions" fill="red" stackId="" />
|
||||||
|
<Tooltip
|
||||||
|
cursor={{ fill: "rgba(255,255,255,0.2)" }}
|
||||||
|
content={({ active, payload }) => {
|
||||||
|
if (active && payload && payload.length) {
|
||||||
|
const data = payload[0].payload as StatSlot;
|
||||||
|
return (
|
||||||
|
<div className="plain-paper flex flex-col gap-2">
|
||||||
|
<div>
|
||||||
|
<FormattedDate value={data.time * 1000} timeStyle="short" dateStyle="short" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex justify-between">
|
||||||
<div className="plain-paper flex-1">
|
<div>
|
||||||
<h3>
|
<FormattedMessage defaultMessage="Messages" id="hMzcSq" />
|
||||||
<FormattedMessage defaultMessage="Top Zappers" id="dVD/AR" />
|
</div>
|
||||||
</h3>
|
<div>{data.messages}</div>
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
{zapsSummary.slice(0, 5).map(a => (
|
|
||||||
<div className="flex justify-between items-center" key={a.pubkey}>
|
|
||||||
<Profile pubkey={a.pubkey} />
|
|
||||||
<div>
|
|
||||||
<FormattedMessage
|
|
||||||
defaultMessage="{n} sats"
|
|
||||||
id="CsCUYo"
|
|
||||||
values={{
|
|
||||||
n: formatSats(a.total),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<FormattedMessage defaultMessage="Reactions" id="XgWvGA" />
|
||||||
|
</div>
|
||||||
|
<div>{data.reactions}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div>
|
||||||
|
<FormattedMessage defaultMessage="Zaps" id="OEW7yJ" />
|
||||||
|
</div>
|
||||||
|
<div>{data.zaps}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<div className="plain-paper flex-1">
|
||||||
|
<h3>
|
||||||
|
<FormattedMessage defaultMessage="Top Chatters" id="GGaJMU" />
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{chatSummary.slice(0, 5).map(a => (
|
||||||
|
<div className="flex justify-between items-center" key={a.pubkey}>
|
||||||
|
<Profile pubkey={a.pubkey} />
|
||||||
|
<div>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="{n} messages"
|
||||||
|
id="gzsn7k"
|
||||||
|
values={{
|
||||||
|
n: <FormattedNumber value={a.messages.length} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
<div className="plain-paper flex-1">
|
||||||
|
<h3>
|
||||||
|
<FormattedMessage defaultMessage="Top Zappers" id="dVD/AR" />
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{zapsSummary.slice(0, 5).map(a => (
|
||||||
|
<div className="flex justify-between items-center" key={a.pubkey}>
|
||||||
|
<Profile pubkey={a.pubkey} />
|
||||||
|
<div>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="{n} sats"
|
||||||
|
id="CsCUYo"
|
||||||
|
values={{
|
||||||
|
n: formatSats(a.total),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,9 @@
|
|||||||
"GGaJMU": {
|
"GGaJMU": {
|
||||||
"defaultMessage": "Top Chatters"
|
"defaultMessage": "Top Chatters"
|
||||||
},
|
},
|
||||||
|
"Gmiwnd": {
|
||||||
|
"defaultMessage": "Refresh the page to use the latest version"
|
||||||
|
},
|
||||||
"Gq6x9o": {
|
"Gq6x9o": {
|
||||||
"defaultMessage": "Cover Image"
|
"defaultMessage": "Cover Image"
|
||||||
},
|
},
|
||||||
@ -227,6 +230,9 @@
|
|||||||
"Qe1MJu": {
|
"Qe1MJu": {
|
||||||
"defaultMessage": "{name} with {amount}"
|
"defaultMessage": "{name} with {amount}"
|
||||||
},
|
},
|
||||||
|
"RJ2VxG": {
|
||||||
|
"defaultMessage": "A new version has been detected"
|
||||||
|
},
|
||||||
"RJOmzk": {
|
"RJOmzk": {
|
||||||
"defaultMessage": "I have read and agree with {provider}''s {terms}."
|
"defaultMessage": "I have read and agree with {provider}''s {terms}."
|
||||||
},
|
},
|
||||||
@ -381,6 +387,9 @@
|
|||||||
"r2Jjms": {
|
"r2Jjms": {
|
||||||
"defaultMessage": "Log In"
|
"defaultMessage": "Log In"
|
||||||
},
|
},
|
||||||
|
"rELDbB": {
|
||||||
|
"defaultMessage": "Refresh"
|
||||||
|
},
|
||||||
"rWBFZA": {
|
"rWBFZA": {
|
||||||
"defaultMessage": "Sexually explicit material ahead!"
|
"defaultMessage": "Sexually explicit material ahead!"
|
||||||
},
|
},
|
||||||
|
@ -145,20 +145,25 @@ export function LayoutPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function NewVersionBanner() {
|
function NewVersionBanner() {
|
||||||
const newVersion = useSyncExternalStore(c => NewVersion.hook(c), () => NewVersion.snapshot());
|
const newVersion = useSyncExternalStore(
|
||||||
|
c => NewVersion.hook(c),
|
||||||
|
() => NewVersion.snapshot()
|
||||||
|
);
|
||||||
if (!newVersion) return;
|
if (!newVersion) return;
|
||||||
|
|
||||||
return <div className="fixed top-0 left-0 w-max flex bg-slate-800 py-2 px-4 opacity-95">
|
return (
|
||||||
<div className="grow">
|
<div className="fixed top-0 left-0 w-max flex bg-slate-800 py-2 px-4 opacity-95">
|
||||||
<h1>
|
<div className="grow">
|
||||||
<FormattedMessage defaultMessage="A new version has been detected" id="RJ2VxG" />
|
<h1>
|
||||||
</h1>
|
<FormattedMessage defaultMessage="A new version has been detected" id="RJ2VxG" />
|
||||||
<p>
|
</h1>
|
||||||
<FormattedMessage defaultMessage="Refresh the page to use the latest version" id="Gmiwnd" />
|
<p>
|
||||||
</p>
|
<FormattedMessage defaultMessage="Refresh the page to use the latest version" id="Gmiwnd" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<AsyncButton onClick={() => window.location.reload()} className="btn">
|
||||||
|
<FormattedMessage defaultMessage="Refresh" id="rELDbB" />
|
||||||
|
</AsyncButton>
|
||||||
</div>
|
</div>
|
||||||
<AsyncButton onClick={() => window.location.reload()} className="btn">
|
);
|
||||||
<FormattedMessage defaultMessage="Refresh" id="rELDbB" />
|
}
|
||||||
</AsyncButton>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
"Fodi9+": "Get paid by viewers",
|
"Fodi9+": "Get paid by viewers",
|
||||||
"G/yZLu": "Remove",
|
"G/yZLu": "Remove",
|
||||||
"GGaJMU": "Top Chatters",
|
"GGaJMU": "Top Chatters",
|
||||||
|
"Gmiwnd": "Refresh the page to use the latest version",
|
||||||
"Gq6x9o": "Cover Image",
|
"Gq6x9o": "Cover Image",
|
||||||
"H/bNs9": "Save this and keep it safe! If you lose this key, you won't be able to access your account ever again. Yep, it's that serious!",
|
"H/bNs9": "Save this and keep it safe! If you lose this key, you won't be able to access your account ever again. Yep, it's that serious!",
|
||||||
"H5+NAX": "Balance",
|
"H5+NAX": "Balance",
|
||||||
@ -75,6 +76,7 @@
|
|||||||
"QRRCp0": "Stream URL",
|
"QRRCp0": "Stream URL",
|
||||||
"QceMQZ": "Goal: {amount}",
|
"QceMQZ": "Goal: {amount}",
|
||||||
"Qe1MJu": "{name} with {amount}",
|
"Qe1MJu": "{name} with {amount}",
|
||||||
|
"RJ2VxG": "A new version has been detected",
|
||||||
"RJOmzk": "I have read and agree with {provider}''s {terms}.",
|
"RJOmzk": "I have read and agree with {provider}''s {terms}.",
|
||||||
"RXQdxR": "Please login to write messages!",
|
"RXQdxR": "Please login to write messages!",
|
||||||
"RrCui3": "Summary",
|
"RrCui3": "Summary",
|
||||||
@ -126,6 +128,7 @@
|
|||||||
"oZrFyI": "Stream type should be HLS",
|
"oZrFyI": "Stream type should be HLS",
|
||||||
"pO/lPX": "Scheduled for {date}",
|
"pO/lPX": "Scheduled for {date}",
|
||||||
"r2Jjms": "Log In",
|
"r2Jjms": "Log In",
|
||||||
|
"rELDbB": "Refresh",
|
||||||
"rWBFZA": "Sexually explicit material ahead!",
|
"rWBFZA": "Sexually explicit material ahead!",
|
||||||
"rbrahO": "Close",
|
"rbrahO": "Close",
|
||||||
"rfC1Zq": "Save card",
|
"rfC1Zq": "Save card",
|
||||||
|
Reference in New Issue
Block a user