hooks#useLoggedIn TypeScript Examples
The following examples show how to use
hooks#useLoggedIn.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From livepeer-com with MIT License | 6 votes |
Assets = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout
id="assets"
breadcrumbs={[{ title: "Assets" }]}
{...Content.metaData}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$8" }}>
<AssetsTable userId={user.id} tableId="dashboardAssetsTable" />
</Box>
</Box>
</Layout>
);
}
Example #2
Source File: api-keys.tsx From livepeer-com with MIT License | 6 votes |
ApiKeys = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout
id="developers"
breadcrumbs={[
{ title: "Developers", href: "/dashboard/developers/api-keys" },
{ title: "API Keys" },
]}
{...Content.metaData}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$8" }}>
<TokenTable userId={user.id} />
</Box>
</Box>
</Layout>
);
}
Example #3
Source File: media-server.tsx From livepeer-com with MIT License | 6 votes |
MediaServer = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout
id="developers/media-server"
breadcrumbs={[
{ title: "Developers", href: "/dashboard/developers/media-server" },
{ title: "Media Server" },
]}
{...Content.metaData}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$8" }}>
<MediaServerTable />
</Box>
</Box>
</Layout>
);
}
Example #4
Source File: index.tsx From livepeer-com with MIT License | 6 votes |
ApiKeys = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout
id="developers/webhooks"
breadcrumbs={[
{ title: "Developers", href: "/dashboard/developers/webhooks" },
{ title: "Webhooks" },
]}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$8" }}>
<WebhooksTable />
</Box>
</Box>
</Layout>
);
}
Example #5
Source File: index.tsx From livepeer-com with MIT License | 6 votes |
DashboardPage = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout id="home" breadcrumbs={[{ title: "Home" }]} {...Content.metaData}>
<Dashboard />
</Layout>
);
}
Example #6
Source File: index.tsx From livepeer-com with MIT License | 6 votes |
Sessions = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout id="streams/sessions" breadcrumbs={[{ title: "Sessions" }]}>
<Box
css={{
pb: "$9",
px: "$6",
pt: "$6",
"@bp4": {
p: "$6",
},
}}>
<AllSessionsTable />
</Box>
</Layout>
);
}
Example #7
Source File: index.tsx From livepeer-com with MIT License | 6 votes |
Streams = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout
id="streams"
breadcrumbs={[{ title: "Streams" }]}
{...Content.metaData}>
<Box
css={{
pb: "$9",
px: "$6",
pt: "$6",
"@bp4": {
p: "$6",
},
}}>
<StreamsTable
title="Streams"
userId={user.id}
pageSize={20}
tableId="streamsTable"
/>
</Box>
</Layout>
);
}
Example #8
Source File: plans.tsx From livepeer-com with MIT License | 5 votes |
PlansPage = () => {
useLoggedIn();
const { user } = useApi();
if (!user) {
return <Layout />;
}
return (
<Layout
id="billing/plans"
breadcrumbs={[
{ title: "Billing", href: "/dashboard/billing" },
{ title: "Plans" },
]}
{...Content.metaData}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$6" }}>
<Flex
justify="between"
align="end"
css={{
borderBottom: "1px solid",
borderColor: "$neutral6",
pb: "$4",
mb: "$5",
width: "100%",
}}>
<Heading size="2">
<Flex>
<Box
css={{
mr: "$3",
fontWeight: 600,
letterSpacing: "0",
}}>
Plans
</Box>
</Flex>
</Heading>
</Flex>
</Box>
<Plans
dashboard={true}
stripeProductId={
user?.stripeProductId ? user.stripeProductId : "prod_0"
}
/>
</Box>
</Layout>
);
}
Example #9
Source File: forgot-password.tsx From livepeer-com with MIT License | 5 votes |
ForgotPasswordPage = () => {
useLoggedIn(false);
const [errors, setErrors] = useState([]);
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const { makePasswordResetToken } = useApi();
const onSubmit = async ({ email }) => {
setLoading(true);
setErrors([]);
const res = await makePasswordResetToken(email);
if (res.errors) {
setLoading(false);
setErrors(res.errors);
} else {
setSuccess(true);
}
};
return (
<Layout {...Content.metaData}>
<Guides backgroundColor="$neutral2" />
{success ? (
<Box
css={{
minHeight: "calc(100vh - 510px)",
display: "flex",
alignItems: "center",
justifyContent: "center",
zIndex: 1,
}}>
Password reset link sent to your email.
</Box>
) : (
<Box css={{ position: "relative" }}>
<Container
size="3"
css={{
px: "$6",
py: "$7",
width: "100%",
"@bp3": {
py: "$8",
px: "$4",
},
}}>
<Flex
align="center"
justify="center"
css={{
flexGrow: 1,
flexDirection: "column",
}}>
<Heading size="3" as="h1" css={{ mb: "$5" }}>
Reset your password
</Heading>
<Login
id="forgot-password"
showEmail={true}
showPassword={false}
buttonText="Get reset link"
onSubmit={onSubmit}
errors={errors}
loading={loading}
/>
<Box>
Nevermind!
<Link href="/login" passHref>
<A>Take me back to log in</A>
</Link>
</Box>
</Flex>
</Container>
</Box>
)}
</Layout>
);
}
Example #10
Source File: reset-password.tsx From livepeer-com with MIT License | 5 votes |
ResetPasswordPage = () => {
useLoggedIn(false);
const [errors, setErrors] = useState([]);
const [loading, setLoading] = useState(false);
const { resetPassword } = useApi();
const router = useRouter();
const { email, resetToken } = router.query;
const onSubmit = async ({ password }) => {
setLoading(true);
setErrors([]);
const res = await resetPassword(email, resetToken, password);
// Don't need to worry about the success case, we'll redirect
if (res.errors) {
setLoading(false);
setErrors(res.errors);
}
};
return (
<Layout {...Content.metaData}>
<Guides backgroundColor="$neutral2" />
<Box css={{ position: "relative" }}>
<Container
size="3"
css={{
px: "$6",
py: "$7",
width: "100%",
"@bp3": {
py: "$8",
px: "$4",
},
}}>
<Flex
css={{
alignItems: "center",
justifyContent: "center",
flexGrow: 1,
flexDirection: "column",
py: "$5",
}}>
<Heading size="3" as="h1" css={{ mb: "$5" }}>
Reset your password
</Heading>
<Login
id="reset-password"
showEmail={false}
showPassword={true}
buttonText="Change password"
onSubmit={onSubmit}
errors={errors}
loading={loading}
/>
<Box>
Nevermind!
<Link href="/login" passHref>
<A>Take me back to log in</A>
</Link>
</Box>
</Flex>
</Container>
</Box>
</Layout>
);
}
Example #11
Source File: streamDetail.tsx From livepeer-com with MIT License | 4 votes |
StreamDetail = ({
breadcrumbs,
children,
stream,
streamHealth,
invalidateStream,
setSwitchTab,
activeTab = "Overview",
}) => {
useLoggedIn();
const { user, getIngest, getAdminStreams } = useApi();
const userIsAdmin = user && user.admin;
const router = useRouter();
const { query } = router;
const id = query.id;
const [_, setIngest] = useState([]);
const [notFound, setNotFound] = useState(false);
const [isCopied, setCopied] = useState(0);
const [keyRevealed, setKeyRevealed] = useState(false);
const [lastSession, setLastSession] = useState(null);
const [lastSessionLoading, setLastSessionLoading] = useState(false);
useEffect(() => {
if (user && user.admin && stream && !lastSessionLoading) {
setLastSessionLoading(true);
getAdminStreams({
sessionsonly: true,
limit: 1,
order: "createdAt-true",
filters: [{ id: "parentId", value: stream.id }],
userId: stream.userId,
})
.then((res) => {
const [streamsOrError] = res;
if (Array.isArray(streamsOrError) && streamsOrError.length > 0) {
setLastSession(streamsOrError[0]);
}
})
.catch((e) => console.log(e))
.finally(() => setLastSessionLoading(false));
}
}, [user, stream]);
useEffect(() => {
if (isCopied) {
const timeout = setTimeout(() => {
setCopied(0);
}, isCopied);
return () => clearTimeout(timeout);
}
}, [isCopied]);
useEffect(() => {
getIngest(true)
.then((ingest) => {
if (Array.isArray(ingest)) {
ingest.sort((a, b) => a.base.localeCompare(b.base));
}
setIngest(ingest);
})
.catch((err) => console.error(err)); // todo: surface this
}, [id]);
const healthState = useMemo(() => {
if (!stream?.isActive) return null;
const activeCond = streamHealth?.conditions.find(
(c) => c.type === "Active"
);
const healthyCond = streamHealth?.healthy;
const healthValid =
activeCond?.status &&
healthyCond?.status != null &&
healthyCond.lastProbeTime >= activeCond.lastTransitionTime;
return !healthValid
? StatusVariant.Pending
: healthyCond.status
? StatusVariant.Healthy
: StatusVariant.Unhealthy;
}, [stream?.isActive, streamHealth]);
if (!user) {
return <Layout />;
}
let { broadcasterHost, region } = stream || {};
if (!broadcasterHost && lastSession && lastSession.broadcasterHost) {
broadcasterHost = lastSession.broadcasterHost;
}
if (!region && lastSession && lastSession.region) {
region = lastSession.region;
}
const playbackId = (stream || {}).playbackId || "";
const domain = isStaging() ? "monster" : "com";
const globalIngestUrl = `rtmp://rtmp.livepeer.${domain}/live`;
const globalPlaybackUrl = `https://livepeercdn.${domain}/hls/${playbackId}/index.m3u8`;
const globalSrtIngestUrl = `srt://rtmp.livepeer.${domain}:2935?streamid=${
stream?.streamKey || ""
}`;
return (
<Layout id="streams" breadcrumbs={breadcrumbs}>
<Box css={{ px: "$6", py: "$7" }}>
{stream ? (
<>
<Flex>
<Box
css={{
minWidth: 424,
flex: "0 0 33%",
}}>
<Flex
justify="between"
align="end"
css={{
pb: "$3",
mb: "$5",
width: "100%",
}}>
<Heading size="2">
<Flex css={{ ai: "center" }}>
<Box
css={{
fontWeight: 600,
letterSpacing: "0",
mr: "$2",
}}>
{stream.name}
</Box>
{!healthState ? null : (
<StatusBadge
variant={healthState}
timestamp={streamHealth?.healthy?.lastProbeTime}
css={{ mt: "$1", letterSpacing: 0 }}
/>
)}
{stream.suspended && (
<Badge
size="2"
variant="red"
css={{
ml: "$1",
mt: "$1",
letterSpacing: 0,
}}>
<Box css={{ mr: 5 }}>
<PauseIcon />
</Box>
Suspended
</Badge>
)}
</Flex>
</Heading>
</Flex>
<Box>
<Box
css={{
maxWidth: "470px",
justifySelf: "flex-end",
width: "100%",
}}>
<Box
css={{
borderRadius: "$3",
overflow: "hidden",
position: "relative",
mb: "$7",
}}>
{stream.isActive ? (
<>
<Badge
size="2"
variant="green"
css={{
position: "absolute",
zIndex: 1,
left: 10,
top: 10,
letterSpacing: 0,
}}>
<Box css={{ mr: 5 }}>
<Status size="1" variant="green" />
</Box>
Active
</Badge>
<Player src={globalPlaybackUrl} />
</>
) : (
<Box
css={{
width: "100%",
height: 265,
borderRadius: "$2",
overflow: "hidden",
position: "relative",
bc: "#28282c",
}}>
<Badge
size="2"
css={{
backgroundColor: "$primary7",
position: "absolute",
zIndex: 1,
left: 10,
top: 10,
letterSpacing: 0,
}}>
<Box css={{ mr: 5 }}>
<Status
css={{ backgroundColor: "$primary9" }}
size="1"
/>
</Box>
Idle
</Badge>
</Box>
)}
</Box>
</Box>
<Box
css={{
borderBottom: "1px solid",
borderColor: "$neutral6",
pb: "$2",
mb: "$4",
width: "100%",
}}>
<Heading size="1" css={{ fontWeight: 600 }}>
Details
</Heading>
</Box>
<Flex
css={{
justifyContent: "flex-start",
alignItems: "baseline",
flexDirection: "column",
}}>
<Box
css={{
display: "grid",
alignItems: "center",
gridTemplateColumns: "10em auto",
width: "100%",
fontSize: "$2",
position: "relative",
}}>
<Cell css={{ color: "$primary11" }}>Stream name</Cell>
<Cell>{stream.name}</Cell>
<Cell css={{ color: "$primary11" }}>Stream ID</Cell>
<Cell>
<ClipBut text={stream.id} />
</Cell>
<Cell css={{ color: "$primary11" }}>Stream key</Cell>
<Cell>
{keyRevealed ? (
<Flex>
<ClipBut text={stream.streamKey} />
</Flex>
) : (
<Button
type="button"
variant="primary"
onClick={() => setKeyRevealed(true)}>
Reveal stream key
</Button>
)}
</Cell>
<Cell css={{ color: "$primary11" }}>RTMP ingest URL</Cell>
<Cell css={{ cursor: "pointer" }}>
<ShowURL url={globalIngestUrl} anchor={false} />
</Cell>
<Cell css={{ color: "$primary11" }}>SRT ingest URL</Cell>
<Cell css={{ cursor: "pointer" }}>
<ShowURL
url={globalSrtIngestUrl}
shortendUrl={globalSrtIngestUrl.replace(
globalSrtIngestUrl.slice(38),
"…"
)}
anchor={false}
/>
</Cell>
<Cell css={{ color: "$primary11" }}>Playback URL</Cell>
<Cell css={{ cursor: "pointer" }}>
<ShowURL
url={globalPlaybackUrl}
shortendUrl={globalPlaybackUrl.replace(
globalPlaybackUrl.slice(29, 45),
"…"
)}
anchor={false}
/>
<Tooltip
content={
<Box>
We changed our playback domain, but
cdn.livepeer.com is still working.
</Box>
}>
<Help />
</Tooltip>
</Cell>
<Cell css={{ color: "$primary11" }}>Record sessions</Cell>
<Cell>
<Flex css={{ position: "relative", top: "2px" }}>
<Box css={{ mr: "$2" }}>
<Record
stream={stream}
invalidate={invalidateStream}
/>
</Box>
<Tooltip
multiline
content={
<Box>
When enabled, transcoded streaming sessions will
be recorded and stored by Livepeer.com. Each
recorded session will have a recording .m3u8 URL
for playback and an MP4 download link. This
feature is currently free.
</Box>
}>
<Help />
</Tooltip>
</Flex>
</Cell>
<Cell css={{ color: "$primary11" }}>Created at</Cell>
<Cell>
<RelativeTime
id="cat"
prefix="createdat"
tm={stream.createdAt}
swap={true}
/>
</Cell>
<Cell css={{ color: "$primary11" }}>Last seen</Cell>
<Cell>
<RelativeTime
id="last"
prefix="lastSeen"
tm={stream.lastSeen}
swap={true}
/>
</Cell>
<Cell css={{ color: "$primary11" }}>Status</Cell>
<Cell>{stream.isActive ? "Active" : "Idle"}</Cell>
<Cell css={{ color: "$primary11" }}>Suspended</Cell>
<Cell>{stream.suspended ? "Suspended" : "Normal"}</Cell>
</Box>
</Flex>
</Box>
</Box>
<Box css={{ flexGrow: 1, ml: "$8" }}>
<Flex
justify="between"
css={{
borderBottom: "1px solid",
borderColor: "$neutral6",
mb: "$4",
width: "100%",
}}>
<Box css={{ display: "flex" }}>
<Box
as="div"
onClick={() => setSwitchTab("Overview")}
css={{
pb: "$2",
width: "100%",
cursor: "pointer",
textDecoration: "none",
borderBottom: "2px solid",
borderColor:
activeTab === "Overview" ? "$violet9" : "transparent",
mr: "$5",
"&:hover": {
textDecoration: "none",
},
}}>
Overview
</Box>
<Box
as="div"
onClick={() => setSwitchTab("Health")}
css={{
textDecoration: "none",
pb: "$2",
width: "100%",
cursor: "pointer",
borderBottom: "2px solid",
borderColor:
activeTab === "Health" ? "$violet9" : "transparent",
"&:hover": {
textDecoration: "none",
},
}}>
Health
</Box>
</Box>
<Box css={{ position: "relative", top: "-8px" }}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="primary"
size="2"
css={{ display: "flex", ai: "center", mr: "$1" }}>
Actions
<Box as={ChevronDownIcon} css={{ ml: "$1" }} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuGroup>
<Record
stream={stream}
invalidate={invalidateStream}
isSwitch={false}
/>
<Suspend
stream={stream}
invalidate={invalidateStream}
/>
<Delete
stream={stream}
invalidate={invalidateStream}
/>
{userIsAdmin && stream.isActive && (
<>
<DropdownMenuSeparator />
<DropdownMenuLabel>Admin only</DropdownMenuLabel>
<Terminate
stream={stream}
invalidate={invalidateStream}
/>
</>
)}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</Box>
</Flex>
<Box css={{ py: "$4" }}>{children}</Box>
</Box>
</Flex>
</>
) : notFound ? (
<Box>Not found</Box>
) : (
<Flex
css={{
height: "calc(100vh - 300px)",
justifyContent: "center",
alignItems: "center",
}}>
<Spinner />
</Flex>
)}
</Box>
</Layout>
);
}
Example #12
Source File: index.tsx From livepeer-com with MIT License | 4 votes |
Billing = () => {
useLoggedIn();
const { user, getUsage, getSubscription, getInvoices, getPaymentMethod } =
useApi();
const [usage, setUsage] = useState(null);
const [subscription, setSubscription] = useState(null);
const [invoices, setInvoices] = useState(null);
const fetcher = useCallback(async () => {
if (user?.stripeCustomerPaymentMethodId) {
const [_res, paymentMethod] = await getPaymentMethod(
user.stripeCustomerPaymentMethodId
);
return paymentMethod;
}
}, [user?.stripeCustomerPaymentMethodId]);
const queryKey = useMemo(() => {
return [user?.stripeCustomerPaymentMethodId];
}, [user?.stripeCustomerPaymentMethodId]);
const { data, isLoading } = useQuery([queryKey], () => fetcher());
const queryClient = useQueryClient();
const invalidateQuery = useCallback(() => {
return queryClient.invalidateQueries(queryKey);
}, [queryClient, queryKey]);
useEffect(() => {
const doGetInvoices = async (stripeCustomerId) => {
const [res, invoices] = await getInvoices(stripeCustomerId);
if (res.status == 200) {
setInvoices(invoices);
}
};
const doGetUsage = async (fromTime, toTime, userId) => {
const [res, usage] = await getUsage(fromTime, toTime, userId);
if (res.status == 200) {
setUsage(usage);
}
};
const getSubscriptionAndUsage = async (subscriptionId) => {
const [res, subscription] = await getSubscription(subscriptionId);
if (res.status == 200) {
setSubscription(subscription);
}
doGetUsage(
subscription?.current_period_start,
subscription?.current_period_end,
user.id
);
};
if (user) {
doGetInvoices(user.stripeCustomerId);
getSubscriptionAndUsage(user.stripeCustomerSubscriptionId);
}
}, [user]);
if (!user) {
return <Layout />;
}
return (
<Layout
id="billing"
breadcrumbs={[{ title: "Billing" }]}
{...Content.metaData}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$7" }}>
<Flex
justify="between"
align="end"
css={{
borderBottom: "1px solid",
borderColor: "$neutral6",
pb: "$4",
mb: "$5",
width: "100%",
}}>
<Heading size="2">
<Flex>
<Box
css={{
mr: "$3",
fontWeight: 600,
letterSpacing: "0",
}}>
Billing
</Box>
</Flex>
</Heading>
<Flex css={{ fontSize: "$3", color: "$primary9" }}>
Current billing period (
{subscription && (
<Flex>
{new Date(
subscription.current_period_start * 1000
).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})}{" "}
to{" "}
{new Date(
subscription.current_period_end * 1000
).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})}{" "}
</Flex>
)}
)
</Flex>
</Flex>
</Box>
<Box css={{ mb: "$8" }}>
<Flex
justify="between"
align="end"
css={{
borderBottom: "1px solid",
borderColor: "$neutral6",
pb: "$3",
mb: "$4",
width: "100%",
}}>
<Heading size="1">
<Flex align="center">
<Box
css={{
mr: "$3",
fontWeight: 600,
letterSpacing: "0",
}}>
Current Plan
</Box>
</Flex>
</Heading>
</Flex>
<Flex
justify="between"
align="center"
css={{ fontSize: "$3", color: "$hiContrast" }}>
<Text variant="gray">
You are currently on the
<Badge
size="1"
variant="primary"
css={{ mx: "$1", fontWeight: 700, letterSpacing: 0 }}>
{user?.stripeProductId
? products[user.stripeProductId]?.name
: products["prod_0"]?.name}
</Badge>
plan.
</Text>
<Link href="/dashboard/billing/plans" passHref>
<A
variant="primary"
css={{ display: "flex", alignItems: "center" }}>
View Plans & Upgrade <ArrowRightIcon />
</A>
</Link>
</Flex>
</Box>
<Box css={{ mb: "$9" }}>
<Flex
justify="between"
align="end"
css={{
borderBottom: "1px solid",
borderColor: "$neutral6",
pb: "$3",
mb: "$5",
width: "100%",
}}>
<Heading size="1">
<Flex align="center" justify="between">
<Box
css={{
mr: "$3",
fontWeight: 600,
letterSpacing: "0",
}}>
Payment Method
</Box>
</Flex>
</Heading>
<PaymentMethodDialog invalidateQuery={invalidateQuery} />
</Flex>
<Flex
css={{
".rccs__card__background": {
background:
"linear-gradient(to right, $colors$violet11, $colors$indigo11) !important",
},
".rccs__card--front": {
color: "white !important",
},
}}>
{user?.stripeCustomerPaymentMethodId ? (
<>
<PaymentMethod data={data} />
</>
) : (
"No payment method on file."
)}
</Flex>
</Box>
<Box css={{ mb: "$9" }}>
<Flex
justify="between"
align="end"
css={{
mb: "$4",
width: "100%",
}}>
<Heading size="1">
<Flex align="center">
<Box
css={{
mr: "$3",
fontWeight: 600,
letterSpacing: "0",
}}>
Upcoming Invoice
</Box>
</Flex>
</Heading>
</Flex>
{!products[user.stripeProductId].order ? (
<Text variant="gray">
The Personal plan is free of charge up to 1000 minutes per month
and limited to 10 concurrent viewers per account.
</Text>
) : (
subscription && (
<UpcomingInvoiceTable
subscription={subscription}
usage={usage}
prices={products[user.stripeProductId].usage}
/>
)
)}
</Box>
{invoices?.data.filter((invoice) => invoice.lines.total_count > 1)
.length > 0 && (
<Box css={{ mb: "$6" }}>
<Flex
justify="between"
align="end"
css={{
mb: "$4",
width: "100%",
}}>
<Heading size="1">
<Flex align="center">
<Box
css={{
mr: "$3",
fontWeight: 600,
letterSpacing: "0",
}}>
Past Invoices
</Box>
</Flex>
</Heading>
</Flex>
<PastInvoicesTable invoices={invoices} />
</Box>
)}
</Box>
</Layout>
);
}
Example #13
Source File: [id].tsx From livepeer-com with MIT License | 4 votes |
WebhookDetail = () => {
useLoggedIn();
const { user, getWebhook, deleteWebhook, updateWebhook } = useApi();
const [deleting, setDeleting] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const router = useRouter();
const dialogState = useToggleState();
const { id } = router.query;
const fetcher = useCallback(async () => {
const webhook = await getWebhook(id);
return webhook;
}, [id]);
const { data } = useQuery([id], () => fetcher());
const queryClient = useQueryClient();
const invalidateQuery = useCallback(() => {
return queryClient.invalidateQueries(id);
}, [queryClient, id]);
return !user ? null : (
<Layout
id="developers/webhooks"
breadcrumbs={[
{ title: "Developers" },
{ title: "Webhooks", href: "/dashboard/developers/webhooks" },
{ title: data?.name },
]}>
<Box css={{ p: "$6" }}>
<Box css={{ mb: "$8" }}>
{data && (
<Box
css={{
borderRadius: 6,
border: "1px solid $colors$primary7",
}}>
<Flex
css={{
p: "$3",
width: "100%",
borderBottom: "1px solid $colors$primary7",
gap: "$3",
fd: "row",
ai: "center",
jc: "space-between",
}}>
<Heading
size="2"
css={{
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
width: "100%",
ai: "flex-start",
}}>
{data.url}
</Heading>
<Flex css={{ ai: "flex-end", fg: "0", fs: "0", pl: "$3" }}>
<AlertDialog open={deleteDialogOpen}>
<Button
onClick={() => {
setDeleteDialogOpen(true);
}}
size="2"
css={{ mr: "$2", display: "flex", ai: "center" }}
variant="red">
<StyledCross />
Delete
</Button>
<AlertDialogContent
css={{ maxWidth: 450, px: "$5", pt: "$4", pb: "$4" }}>
<AlertDialogTitle asChild>
<Heading size="1">Delete Webhook</Heading>
</AlertDialogTitle>
<AlertDialogDescription asChild>
<Text
size="3"
variant="gray"
css={{ mt: "$2", lineHeight: "22px" }}>
Are you sure you want to delete this webhook?
</Text>
</AlertDialogDescription>
<Flex css={{ jc: "flex-end", gap: "$2", mt: "$5" }}>
<Button
onClick={() => setDeleteDialogOpen(false)}
size="2"
ghost>
Cancel
</Button>
<AlertDialogAction asChild>
<Button
size="2"
disabled={deleting}
onClick={async () => {
setDeleting(true);
await deleteWebhook(data.id);
await invalidateQuery();
setDeleting(false);
setDeleteDialogOpen(false);
router.push("/dashboard/developers/webhooks");
}}
variant="red">
{deleting && (
<Spinner
css={{
width: 16,
height: 16,
mr: "$2",
}}
/>
)}
Delete
</Button>
</AlertDialogAction>
</Flex>
</AlertDialogContent>
</AlertDialog>
<WebhookDialog
button={
<Button
size="2"
css={{ display: "flex", ai: "center" }}
onClick={() => dialogState.onToggle()}>
<StyledPencil />
Update details
</Button>
}
webhook={data}
action={Action.Update}
isOpen={dialogState.on}
onOpenChange={dialogState.onToggle}
onSubmit={async ({ events, name, url, sharedSecret }) => {
delete data.event; // remove deprecated field before updating
await updateWebhook(data.id, {
...data,
events: events ? events : data.events,
name: name ? name : data.name,
url: url ? url : data.url,
sharedSecret: sharedSecret ?? data.sharedSecret,
});
await invalidateQuery();
}}
/>
</Flex>
</Flex>
<Box
css={{
display: "grid",
gridTemplateColumns: "12em auto",
width: "100%",
fontSize: "$2",
position: "relative",
p: "$3",
borderBottomLeftRadius: 6,
borderBottomRightRadius: 6,
backgroundColor: "$panel",
}}>
<Cell variant="gray">URL</Cell>
<Cell>{data.url}</Cell>
<Cell variant="gray">Name</Cell>
<Cell>{data.name}</Cell>
<Cell variant="gray">Secret</Cell>
<Cell>{data.sharedSecret}</Cell>
<Cell variant="gray">Created</Cell>
<Cell>{format(data.createdAt, "MMMM dd, yyyy h:mm a")}</Cell>
<Cell variant="gray">Event types</Cell>
<Cell css={{ display: "flex", fw: "wrap" }}>
{data.events.map((e) => (
<Badge
variant="primary"
size="2"
css={{ fontWeight: 600, mr: "$1", mb: "$1" }}>
{e}
</Badge>
))}
</Cell>
<Cell variant="gray">Last trigger</Cell>
<Cell>
{data.status
? format(
data.status?.lastTriggeredAt,
"MMMM dd, yyyy h:mm:ss a"
)
: "Never"}
</Cell>
<Cell variant="gray">Last failure</Cell>
<Cell>
{!data.status
? "Never"
: data.status.lastFailure
? format(
data.status.lastFailure.timestamp,
"MMMM dd, yyyy h:mm:ss a"
)
: "Never"}
</Cell>
{data.status ? (
data.status.lastFailure?.statusCode ? (
<>
<Cell variant="gray">Error Status Code</Cell>
<Cell>{`${data.status.lastFailure.statusCode}
${
STATUS_CODES[data.status.lastFailure.statusCode]
}`}</Cell>
</>
) : data.status.lastFailure ? (
<>
<Cell variant="gray">Error message</Cell>
<Cell
css={{
fontFamily: "monospace",
}}>
{data.status.lastFailure.error ?? "unknown"}
</Cell>
</>
) : (
""
)
) : (
""
)}
{data.status?.lastFailure?.response ? (
<>
<Cell variant="gray">Error response</Cell>
<Cell
css={{
fontFamily: "monospace",
}}>
{data.status.lastFailure.response}
</Cell>
</>
) : (
""
)}
</Box>
</Box>
)}
</Box>
</Box>
</Layout>
);
}
Example #14
Source File: index.tsx From livepeer-com with MIT License | 4 votes |
export default function MintNFT() {
const { status, connect, account, chainId, ethereum } = useMetaMask();
const { user, token: jwt, endpoint } = useApi();
const videoNft = useMemo(
() =>
new minter.FullMinter({ auth: { jwt }, endpoint }, { ethereum, chainId }),
[ethereum, chainId, jwt, endpoint]
);
const isMinting = useToggleState();
const isUploading = useToggleState();
const initState = useMemo(() => {
if (typeof window === "undefined") {
return {};
}
const searchParams = new URLSearchParams(window.location.search);
return {
file: null as File,
contractAddress: searchParams.get("contractAddress"),
tokenUri: searchParams.get("tokenUri"),
recipient: searchParams.get("recipient"),
};
}, [typeof window !== "undefined" && window?.location?.search]);
const defaultContractAddress = useMemo<string>(
() => chains.getBuiltinChain(chainId)?.defaultContract,
[chainId]
);
const [state, setState] = useState(initState);
type State = typeof state;
const setStateProp = <T extends keyof State>(prop: T, value: State[T]) => {
setState({ ...state, [prop]: value });
};
const [logs, setLogs] = useState<JSX.Element[]>([]);
const addLog = useCallback(
(log: JSX.Element | string) =>
setLogs((prev) => [
...prev,
<>
[{new Date().toLocaleTimeString()}] {log}
</>,
]),
[setLogs]
);
const onClickMint = useCallback(async () => {
isMinting.onOn();
try {
await richMintNft(
videoNft.web3,
state.contractAddress ?? defaultContractAddress,
state.recipient ?? account,
state.tokenUri,
addLog,
chains.getBuiltinChain(chainId)
);
} finally {
isMinting.onOff();
}
}, [state, videoNft.web3, defaultContractAddress, account, addLog, chainId]);
const onClickSwitchNetwork = (chainId: string) => () => {
setLogs([]);
return richSwitchChain(
ethereum,
chains.getBuiltinChain(chainId).spec,
addLog
);
};
const onClickConnect = useCallback(() => {
setLogs([]);
return connect();
}, [setLogs, connect]);
if (!initState.tokenUri) {
useLoggedIn();
}
return (
<Layout {...Content.metaData} css={{ minHeight: "100vh" }}>
<Guides backgroundColor="$neutral2" />
<Box css={{ position: "relative", flex: 1 }}>
<Container
size="3"
css={{
px: "$6",
py: "$7",
width: "100%",
"@bp3": {
py: "$8",
px: "$4",
},
}}>
<Flex
css={{
alignItems: "center",
justifyContent: "center",
flexGrow: 1,
flexDirection: "column",
}}>
<AlertDialog open={true}>
<AlertDialogContent
css={{ maxWidth: 450, px: "$5", pt: "$4", pb: "$4" }}
onOpenAutoFocus={(e) => e.preventDefault()}>
<AlertDialogTitle asChild>
<Heading size="1">Mint a Video NFT</Heading>
</AlertDialogTitle>
<Box
css={{ mt: "$3" }}
as="form"
onSubmit={(e) => {
e.preventDefault();
return onClickMint();
}}>
<Flex direction="column" gap="2">
{state?.tokenUri || !user ? undefined : (
<>
<Label htmlFor="file">File {state?.tokenUri}</Label>
<Flex direction="row" gap="2">
<Text>{state.file?.name}</Text>
<Button
css={{ display: "flex", ai: "center" }}
type="button"
size="2"
variant="primary"
onClick={async () => {
setStateProp(
"file",
await videoNft.uploader.pickFile()
);
}}>
Pick a file
</Button>
</Flex>
</>
)}
<Label htmlFor="contractAddress">
Contract Address (optional)
</Label>
<Tooltip content="Defaults to Livepeer-owned Video NFT contract.">
<TextField
size="2"
type="text"
id="contractAddress"
value={
state.contractAddress === defaultContractAddress
? ""
: state.contractAddress
}
disabled={
isMinting.on ||
status !== "connected" ||
!chains.isChainBuiltin(chainId)
}
placeholder={
!defaultContractAddress
? "Unsupported network"
: `Livepeer Video NFT (${defaultContractAddress})`
}
onChange={(e) =>
setStateProp("contractAddress", e.target.value)
}
/>
</Tooltip>
<Label htmlFor="tokenUri">Token URI</Label>
<TextField
required={true}
autoFocus
size="2"
type="url"
pattern="^ipfs://.+"
id="tokenUri"
value={state.tokenUri}
disabled={true}
onChange={(e) => setStateProp("tokenUri", e.target.value)}
placeholder="ipfs://..."
/>
</Flex>
<AlertDialogDescription asChild>
<Text
size="3"
variant="gray"
css={{ mt: "$2", fontSize: "$2", mb: "$4" }}>
<Box
css={{
overflow: "scroll",
p: "$4",
height: 200,
borderRadius: 6,
}}>
{(() => {
switch (status) {
case "initializing":
return (
<div>
Synchronisation with MetaMask ongoing...
</div>
);
case "unavailable":
return (
<div>
MetaMask not available. Install it at{" "}
<a
href="https://metamask.io/download"
target="_blank">
metamask.io
</a>
</div>
);
case "notConnected":
return (
<div>Connect your MetaMask wallet below.</div>
);
case "connecting":
return <div>Connecting to MetaMask...</div>;
default:
return (
<div>Unknown MetaMask status: ${status}.</div>
);
case "connected":
if (!chains.isChainBuiltin(chainId)) {
return (
<div>
Only Polygon network is supported right now.
Click below to switch or add it to MetaMask.
</div>
);
}
return (
<>
<Box css={{ mb: "$2" }}>
Connected to {displayAddr(account)} on{" "}
{
chains.getBuiltinChain(chainId).spec
.chainName
}{" "}
(<code>{parseInt(chainId, 16)}</code>)
</Box>
{logs.map((log, idx) => (
<Box key={`log-${idx}`}>{log}</Box>
))}
</>
);
}
})()}
</Box>
</Text>
</AlertDialogDescription>
<Flex css={{ jc: "flex-end", gap: "$3", mt: "$4" }}>
{status === "notConnected" ? (
<Button
css={{ display: "flex", ai: "center" }}
type="button"
size="2"
disabled={status !== "notConnected"}
variant="primary"
onClick={onClickConnect}>
Connect to MetaMask
</Button>
) : status === "connected" &&
!chains.isChainBuiltin(chainId) ? (
<>
<Button
css={{ display: "flex", ai: "center" }}
type="button"
size="2"
variant="primary"
onClick={onClickSwitchNetwork("0x13881")}>
Polygon Testnet
</Button>
<Button
css={{ display: "flex", ai: "center" }}
type="button"
size="2"
variant="primary"
onClick={onClickSwitchNetwork("0x89")}>
Polygon Mainnet
</Button>
</>
) : user && jwt && state.file && !state.tokenUri ? (
<Button
css={{ display: "flex", ai: "center" }}
type="button"
size="2"
disabled={isUploading.on}
variant="primary"
onClick={async () => {
isUploading.onOn();
try {
const { file } = state;
addLog("Uploading file...");
let asset = await videoNft.api.createAsset(
file.name,
file
);
addLog("Normalizing for NFT...");
asset = await videoNft.api.nftNormalize(asset);
addLog("Exporting to IPFS...");
const { nftMetadataUrl } =
await videoNft.api.exportToIPFS(asset.id);
addLog("Done! NFT token URI: " + nftMetadataUrl);
setStateProp("tokenUri", nftMetadataUrl);
} catch (err) {
addLog("Error uploading file: " + err.message);
} finally {
isUploading.onOff();
}
}}>
{isUploading.on && (
<Spinner
css={{
color: "$hiContrast",
width: 16,
height: 16,
mr: "$2",
}}
/>
)}
{isUploading.on ? "Uploading..." : "Upload file"}
</Button>
) : (
<Button
css={{ display: "flex", ai: "center" }}
type="submit"
size="2"
disabled={isMinting.on || status !== "connected"}
variant="primary">
{isMinting.on && (
<Spinner
css={{
color: "$hiContrast",
width: 16,
height: 16,
mr: "$2",
}}
/>
)}
{isMinting.on ? "Minting..." : "Mint NFT"}
</Button>
)}
</Flex>
</Box>
</AlertDialogContent>
</AlertDialog>
</Flex>
</Container>
</Box>
</Layout>
);
}