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

View File

@ -59,4 +59,4 @@ steps:
volumes: volumes:
- name: cache - name: cache
claim: 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"> <div className="file-uploader-container">
<label className="file-uploader"> <label className="file-uploader">
<input type="file" onChange={onFileChange} /> <input type="file" onChange={onFileChange} />
{isUploading ? <FormattedMessage defaultMessage="Uploading..." /> : <FormattedMessage defaultMessage="Add File" />} {isUploading ? (
<FormattedMessage defaultMessage="Uploading..." />
) : (
<FormattedMessage defaultMessage="Add File" />
)}
</label> </label>
<div className="file-uploader-preview"> <div className="file-uploader-preview">
{img?.length > 0 && ( {img?.length > 0 && (

View File

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

View File

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

View File

@ -13,9 +13,11 @@ export function StreamTimer({ ev }: { ev?: NostrEvent }) {
const hour = min * 60; const hour = min * 60;
const hours = Math.floor(diff / hour); 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); 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(() => { useEffect(() => {

View File

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

View File

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

View File

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

View File

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

View File

@ -7,23 +7,23 @@ import { Views } from "./widgets/views";
import { TopZappersWidget } from "./widgets/top-zappers"; import { TopZappersWidget } from "./widgets/top-zappers";
export function AlertsPage() { export function AlertsPage() {
const params = useParams(); const params = useParams();
const link = useStreamLink(); const link = useStreamLink();
if (!link) { if (!link) {
return <Spinner /> return <Spinner />;
} }
switch (params.type) { switch (params.type) {
case "zaps": { case "zaps": {
return <ZapAlerts link={link} /> return <ZapAlerts link={link} />;
}
case "views": {
return <Views link={link} />
}
case "top-zappers": {
return <TopZappersWidget 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 { ContentWarningOverlay, isContentWarningAccepted } from "element/content-warning";
import { useCurrentStreamFeed } from "hooks/current-stream-feed"; import { useCurrentStreamFeed } from "hooks/current-stream-feed";
import { useStreamLink } from "hooks/stream-link"; import { useStreamLink } from "hooks/stream-link";
import { FormattedMessage } from "react-intl";
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent }) { function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent }) {
const login = useLogin(); const login = useLogin();
@ -54,7 +55,11 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent })
<p>{findTag(ev, "summary")}</p> <p>{findTag(ev, "summary")}</p>
<div className="tags"> <div className="tags">
<StatePill state={status as StreamState} /> <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 && ( {status === StreamState.Live && (
<span className="pill"> <span className="pill">
<StreamTimer ev={ev} /> <StreamTimer ev={ev} />
@ -66,7 +71,7 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedNostrEvent })
<div className="actions"> <div className="actions">
{ev && <NewStreamDialog text="Edit" ev={ev} btnClassName="btn" />} {ev && <NewStreamDialog text="Edit" ev={ev} btnClassName="btn" />}
<AsyncButton type="button" className="btn btn-warning" onClick={deleteStream}> <AsyncButton type="button" className="btn btn-warning" onClick={deleteStream}>
Delete <FormattedMessage defaultMessage="Delete" />
</AsyncButton> </AsyncButton>
</div> </div>
)} )}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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