This commit is contained in:
Kieran 2023-08-28 15:54:50 +01:00
parent 2754cb1581
commit c9cd7ffc13
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
47 changed files with 197 additions and 167 deletions

View File

@ -59,4 +59,4 @@ steps:
volumes:
- name: cache
claim:
name: docker-cache
name: docker-cache

View File

@ -78,7 +78,11 @@ export function FileUploader({ defaultImage, onClear, onFileUpload }: FileUpload
<div className="file-uploader-container">
<label className="file-uploader">
<input type="file" onChange={onFileChange} />
{isUploading ? <FormattedMessage defaultMessage="Uploading..." /> : <FormattedMessage defaultMessage="Add File" />}
{isUploading ? (
<FormattedMessage defaultMessage="Uploading..." />
) : (
<FormattedMessage defaultMessage="Add File" />
)}
</label>
<div className="file-uploader-preview">
{img?.length > 0 && (

View File

@ -129,10 +129,7 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
</div>
<div>
<small>
<FormattedMessage
defaultMessage="Zap amount in {currency}"
values={{ amount: isFiat ? "USD" : "sats" }}
/>
<FormattedMessage defaultMessage="Zap amount in {currency}" values={{ amount: isFiat ? "USD" : "sats" }} />
</small>
<div className="amounts">
{(isFiat ? usdAmounts : satsAmounts).map(a => (

View File

@ -184,9 +184,7 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
return (
<div className="new-card">
<h3>
{header || <FormattedMessage defaultMessage="Add card" />}
</h3>
<h3>{header || <FormattedMessage defaultMessage="Add card" />}</h3>
<div className="form-control">
<label htmlFor="card-title">
<FormattedMessage defaultMessage="Title" />
@ -224,7 +222,8 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
<textarea
placeholder={formatMessage({ defaultMessage: "Start typing" })}
value={content}
onChange={e => setContent(e.target.value)} />
onChange={e => setContent(e.target.value)}
/>
<span className="help-text">
<FormattedMessage
defaultMessage="Supports {markdown}"

View File

@ -13,9 +13,11 @@ export function StreamTimer({ ev }: { ev?: NostrEvent }) {
const hour = min * 60;
const hours = Math.floor(diff / hour);
const mins = Math.floor((diff % hour) / min);
const mins = Math.floor((diff % hour) / min);
const secs = Math.floor(diff % min);
setTime(`${hours.toFixed(0).padStart(2, "0")}:${mins.toFixed(0).padStart(2, "0")}:${secs.toFixed(0).padStart(2, "0")}`);
setTime(
`${hours.toFixed(0).padStart(2, "0")}:${mins.toFixed(0).padStart(2, "0")}:${secs.toFixed(0).padStart(2, "0")}`
);
}
useEffect(() => {

View File

@ -4,24 +4,24 @@ import { formatSats } from "number";
import { Icon } from "./icon";
import { Profile } from "./profile";
export function TopZappers({ zaps, limit }: { zaps: ParsedZap[], limit?: number }) {
const zappers = useTopZappers(zaps);
export function TopZappers({ zaps, limit }: { zaps: ParsedZap[]; limit?: number }) {
const zappers = useTopZappers(zaps);
return (
<>
{zappers.slice(0, limit ?? 10).map(({ pubkey, total }) => {
return (
<div className="top-zapper" key={pubkey}>
{pubkey === "anon" ? (
<p className="top-zapper-name">Anon</p>
) : (
<Profile pubkey={pubkey} options={{ showName: false }} />
)}
<Icon name="zap-filled" className="zap-icon" />
<p className="top-zapper-amount">{formatSats(total)}</p>
</div>
);
})}
</>
);
}
return (
<>
{zappers.slice(0, limit ?? 10).map(({ pubkey, total }) => {
return (
<div className="top-zapper" key={pubkey}>
{pubkey === "anon" ? (
<p className="top-zapper-name">Anon</p>
) : (
<Profile pubkey={pubkey} options={{ showName: false }} />
)}
<Icon name="zap-filled" className="zap-icon" />
<p className="top-zapper-amount">{formatSats(total)}</p>
</div>
);
})}
</>
);
}

View File

@ -69,7 +69,7 @@ const router = createBrowserRouter([
},
{
path: "/widgets",
element: <WidgetsPage />
element: <WidgetsPage />,
},
{
path: "*",
@ -92,7 +92,7 @@ const router = createBrowserRouter([
await System.Init();
return null;
},
}
},
]);
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLDivElement);
root.render(

View File

@ -77,6 +77,9 @@
"C81/uG": {
"defaultMessage": "Logout"
},
"Dn82AL": {
"defaultMessage": "Live"
},
"ESyhzp": {
"defaultMessage": "Your comment for {name}"
},
@ -98,6 +101,9 @@
"IJDKz3": {
"defaultMessage": "Zap amount in {currency}"
},
"JEsxDw": {
"defaultMessage": "Uploading..."
},
"Jq3FDz": {
"defaultMessage": "Content"
},
@ -155,6 +161,9 @@
"RrCui3": {
"defaultMessage": "Summary"
},
"TP/cMX": {
"defaultMessage": "Ended"
},
"TaTRKo": {
"defaultMessage": "Start Stream"
},
@ -179,6 +188,9 @@
"acrOoz": {
"defaultMessage": "Continue"
},
"cPIKU2": {
"defaultMessage": "Following"
},
"cvAsEh": {
"defaultMessage": "Streamed on {date}"
},
@ -194,6 +206,9 @@
"fBI91o": {
"defaultMessage": "Zap"
},
"fc2iho": {
"defaultMessage": "Add File"
},
"hGQqkW": {
"defaultMessage": "Schedule"
},
@ -215,6 +230,12 @@
"jvo0vs": {
"defaultMessage": "Save"
},
"k21gTS": {
"defaultMessage": "e.g. about me"
},
"kp0NPF": {
"defaultMessage": "Planned"
},
"lZpRMR": {
"defaultMessage": "Check here if this stream contains nudity or pornographic content."
},
@ -275,6 +296,9 @@
"vrTOHJ": {
"defaultMessage": "{amount} sats"
},
"w0Xm2F": {
"defaultMessage": "Start typing"
},
"wCIL7o": {
"defaultMessage": "Broadcast on Nostr"
},

View File

@ -1,5 +1,5 @@
.zap-alert-widgets .zap-alert {
animation: cssAnimation 0s ease-in 5s forwards;
animation: cssAnimation 0s ease-in 10s forwards;
animation-fill-mode: forwards;
}

View File

@ -7,23 +7,23 @@ import { Views } from "./widgets/views";
import { TopZappersWidget } from "./widgets/top-zappers";
export function AlertsPage() {
const params = useParams();
const link = useStreamLink();
const params = useParams();
const link = useStreamLink();
if (!link) {
return <Spinner />
}
if (!link) {
return <Spinner />;
}
switch (params.type) {
case "zaps": {
return <ZapAlerts link={link} />
}
case "views": {
return <Views link={link} />
}
case "top-zappers": {
return <TopZappersWidget link={link} />
}
switch (params.type) {
case "zaps": {
return <ZapAlerts link={link} />;
}
return null;
case "views": {
return <Views link={link} />;
}
case "top-zappers": {
return <TopZappersWidget link={link} />;
}
}
return null;
}

View File

@ -24,6 +24,7 @@ import { ShareMenu } from "element/share-menu";
import { ContentWarningOverlay, isContentWarningAccepted } from "element/content-warning";
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
import { useStreamLink } from "hooks/stream-link";
import { FormattedMessage } from "react-intl";
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent }) {
const login = useLogin();
@ -54,7 +55,11 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent })
<p>{findTag(ev, "summary")}</p>
<div className="tags">
<StatePill state={status as StreamState} />
{viewers > 0 && <span className="pill viewers">{formatSats(viewers)} viewers</span>}
{viewers > 0 && (
<span className="pill viewers">
<FormattedMessage defaultMessage="{n} viewers" values={{ n: formatSats(viewers) }} />
</span>
)}
{status === StreamState.Live && (
<span className="pill">
<StreamTimer ev={ev} />
@ -66,7 +71,7 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent })
<div className="actions">
{ev && <NewStreamDialog text="Edit" ev={ev} btnClassName="btn" />}
<AsyncButton type="button" className="btn btn-warning" onClick={deleteStream}>
Delete
<FormattedMessage defaultMessage="Delete" />
</AsyncButton>
</div>
)}

View File

@ -1,10 +1,10 @@
.widgets {
display: grid;
grid-template-columns: repeat(4, 1fr);
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.widgets > div {
background-color: #3f3f3f;
border-radius: 16px;
padding: 8px 12px;
}
background-color: #3f3f3f;
border-radius: 16px;
padding: 8px 12px;
}

View File

@ -11,48 +11,52 @@ import { TopZappersWidget } from "./widgets/top-zappers";
import { Views } from "./widgets/views";
export function WidgetsPage() {
const login = useLogin();
const profileLink = createNostrLink(NostrPrefix.PublicKey, login?.pubkey ?? "");
const current = useCurrentStreamFeed(profileLink);
const currentLink = current ? eventToLink(current) : undefined;
const npub = hexToBech32("npub", login?.pubkey);
const login = useLogin();
const profileLink = createNostrLink(NostrPrefix.PublicKey, login?.pubkey ?? "");
const current = useCurrentStreamFeed(profileLink);
const currentLink = current ? eventToLink(current) : undefined;
const npub = hexToBech32("npub", login?.pubkey);
const baseUrl = `${window.location.protocol}//${window.location.host}`;
return <div className="widgets g8">
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Chat Widget" />
</h3>
<Copy text={`${baseUrl}/chat/${npub}`} />
</div>
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Zap Alert" />
</h3>
<Copy text={`${baseUrl}/alert/${npub}/zaps`} />
<ZapAlertItem item={{
id: "",
valid: true,
zapService: "",
anonZap: false,
errors: [],
sender: login?.pubkey,
amount: 1_000_000
}} />
</div>
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Top Zappers" />
</h3>
<Copy text={`${baseUrl}/alert/${npub}/top-zappers`} />
{currentLink && <TopZappersWidget link={currentLink} />}
</div>
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Current Viewers" />
</h3>
<Copy text={`${baseUrl}/alert/${npub}/views`} />
{currentLink && <Views link={currentLink} />}
</div>
const baseUrl = `${window.location.protocol}//${window.location.host}`;
return (
<div className="widgets g8">
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Chat Widget" />
</h3>
<Copy text={`${baseUrl}/chat/${npub}`} />
</div>
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Zap Alert" />
</h3>
<Copy text={`${baseUrl}/alert/${npub}/zaps`} />
<ZapAlertItem
item={{
id: "",
valid: true,
zapService: "",
anonZap: false,
errors: [],
sender: login?.pubkey,
amount: 1_000_000,
}}
/>
</div>
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Top Zappers" />
</h3>
<Copy text={`${baseUrl}/alert/${npub}/top-zappers`} />
{currentLink && <TopZappersWidget link={currentLink} />}
</div>
<div className="flex f-col g8">
<h3>
<FormattedMessage defaultMessage="Current Viewers" />
</h3>
<Copy text={`${baseUrl}/alert/${npub}/views`} />
{currentLink && <Views link={currentLink} />}
</div>
</div>
}
);
}

View File

@ -6,14 +6,16 @@ import { FormattedMessage } from "react-intl";
import { eventToLink } from "utils";
export function TopZappersWidget({ link }: { link: NostrLink }) {
const currentEvent = useCurrentStreamFeed(link, true);
const zaps = useZaps(currentEvent ? eventToLink(currentEvent) : undefined, true);
return <div className="top-zappers-widget">
<div>
<FormattedMessage defaultMessage="Top Zappers" />
</div>
<div className="flex g8">
<TopZappers zaps={zaps} limit={3} />
</div>
</div>;
}
const currentEvent = useCurrentStreamFeed(link, true);
const zaps = useZaps(currentEvent ? eventToLink(currentEvent) : undefined, true);
return (
<div className="top-zappers-widget">
<div>
<FormattedMessage defaultMessage="Top Zappers" />
</div>
<div className="flex g8">
<TopZappers zaps={zaps} limit={3} />
</div>
</div>
);
}

View File

@ -4,10 +4,12 @@ import { FormattedMessage } from "react-intl";
import { findTag } from "utils";
export function Views({ link }: { link: NostrLink }) {
const current = useCurrentStreamFeed(link, true);
const current = useCurrentStreamFeed(link, true);
const viewers = findTag(current, "current_participants");
return <div className="views">
<FormattedMessage defaultMessage="{n} viewers" values={{ n: Number(viewers) }} />
const viewers = findTag(current, "current_participants");
return (
<div className="views">
<FormattedMessage defaultMessage="{n} viewers" values={{ n: Number(viewers) }} />
</div>
}
);
}

View File

@ -8,27 +8,40 @@ import { FormattedMessage } from "react-intl";
import { eventToLink } from "utils";
export function ZapAlerts({ link }: { link: NostrLink }) {
const currentEvent = useCurrentStreamFeed(link, true);
const currentLink = currentEvent ? eventToLink(currentEvent) : undefined;
const zaps = useZaps(currentLink, true);
const currentEvent = useCurrentStreamFeed(link, true);
const currentLink = currentEvent ? eventToLink(currentEvent) : undefined;
const zaps = useZaps(currentLink, true);
return <div className="flex f-center f-col zap-alert-widgets">
{zaps.slice(0, 5).map(v => <ZapAlertItem key={v.id} item={v} />)}
return (
<div className="flex f-center f-col zap-alert-widgets">
{zaps.slice(0, 5).map(v => (
<ZapAlertItem key={v.id} item={v} />
))}
</div>
);
}
export function ZapAlertItem({ item }: { item: ParsedZap }) {
const profile = useUserProfile(item.sender);
if (!profile) return;
return <div className="zap-alert">
<div>
<FormattedMessage defaultMessage="Incoming Zap" />
</div>
<div>
<FormattedMessage defaultMessage="{name} with {amount}" values={{
name: <span className="highlight">{profile?.name ?? hexToBech32("npub", item?.sender ?? "").slice(0, 12)}&nbsp;</span>,
amount: <span className="highlight">&nbsp;{formatSats(item.amount)}</span>
}} />
</div>
const profile = useUserProfile(item.sender);
if (!profile) return;
return (
<div className="zap-alert">
<div>
<FormattedMessage defaultMessage="Incoming Zap" />
</div>
<div>
<FormattedMessage
defaultMessage="{name} with {amount}"
values={{
name: (
<span className="highlight">
{profile?.name ?? hexToBech32("npub", item?.sender ?? "").slice(0, 12)}&nbsp;
</span>
),
amount: <span className="highlight">&nbsp;{formatSats(item.amount)}</span>,
}}
/>
</div>
</div>
}
);
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -25,6 +25,7 @@
"AyGauy": "Login",
"BGxpTN": "Stream Chat",
"C81/uG": "Logout",
"Dn82AL": "Live",
"ESyhzp": "Your comment for {name}",
"G/yZLu": "Remove",
"Gq6x9o": "Cover Image",
@ -32,6 +33,7 @@
"HAlOn1": "Name",
"I1kjHI": "Supports {markdown}",
"IJDKz3": "Zap amount in {currency}",
"JEsxDw": "Uploading...",
"Jq3FDz": "Content",
"K3r6DQ": "Delete",
"K3uH1C": "offline",
@ -51,6 +53,7 @@
"RJOmzk": "I have read and agree with {provider}''s {terms}.",
"RXQdxR": "Please login to write messages!",
"RrCui3": "Summary",
"TP/cMX": "Ended",
"TaTRKo": "Start Stream",
"UJBFYK": "Add Card",
"UfSot5": "Past Streams",
@ -59,11 +62,13 @@
"X2PZ7D": "Create Goal",
"ZmqxZs": "You can change this later",
"acrOoz": "Continue",
"cPIKU2": "Following",
"cvAsEh": "Streamed on {date}",
"cyR7Kh": "Back",
"dVD/AR": "Top Zappers",
"ebmhes": "Nostr Extension",
"fBI91o": "Zap",
"fc2iho": "Add File",
"hGQqkW": "Schedule",
"hpl4BP": "Chat Widget",
"ieGrWo": "Follow",
@ -71,6 +76,8 @@
"izWS4J": "Unfollow",
"jr4+vD": "Markdown",
"jvo0vs": "Save",
"k21gTS": "e.g. about me",
"kp0NPF": "Planned",
"lZpRMR": "Check here if this stream contains nudity or pornographic content.",
"ljmS5P": "Endpoint",
"mtNGwh": "A short description of the content",
@ -91,10 +98,11 @@
"tzMNF3": "Status",
"uYw2LD": "Stream",
"vrTOHJ": "{amount} sats",
"w0Xm2F": "Start typing",
"wCIL7o": "Broadcast on Nostr",
"wEQDC6": "Edit",
"wOy57k": "Add stream goal",
"wzWWzV": "Top zappers",
"x82IOl": "Mute",
"zVDHAu": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}

View File

@ -294,4 +294,3 @@
"defaultMessage": "Zap Alert"
}
}