Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 99 additions & 55 deletions packages/client/src/interface/channels/AgeGate.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { JSXElement, Match, Switch, createEffect } from "solid-js";
import { JSXElement, Match, Suspense, Switch } from "solid-js";

import { Trans } from "@lingui-solid/solid/macro";
import { useQuery } from "@tanstack/solid-query";
import { styled } from "styled-system/jsx";

import { useState } from "@revolt/state";
import { LAYOUT_SECTIONS } from "@revolt/state/stores/Layout";
import { Button, Checkbox, iconSize } from "@revolt/ui";
import { Button, Checkbox, CircularProgress, Text, iconSize } from "@revolt/ui";

import MdWarning from "@material-design-icons/svg/round/warning.svg?component-solid";

type GeoBlock = {
countryCode: string;
isAgeRestrictedGeo: boolean;
};

/**
* Age gate filter for any content
*/
Expand All @@ -21,53 +27,101 @@ export function AgeGate(props: {
}) {
const state = useState();

const confirmed = state.layout.getSectionState(LAYOUT_SECTIONS.MATURE, false);
const allowed = state.layout.getSectionState(
props.contentId + "-nsfw",
false,
);
const confirmed = () =>
state.layout.getSectionState(LAYOUT_SECTIONS.MATURE, false);
const allowed = () =>
state.layout.getSectionState(props.contentId + "-nsfw", false);

const geoQuery = useQuery(() => ({
queryKey: ["geoblock"],
queryFn: async (): Promise<GeoBlock> => {
const response = await fetch("https://geo.revolt.chat");
if (!response.ok) {
throw new Error("Failed to fetch geo data");
}
return response.json();
},
staleTime: 5 * 60 * 1000, // 5 minutes
throwOnError: true,
}));

return (
<Switch fallback={props.children}>
<Match when={props.enabled && (!confirmed || !allowed)}>
<Base>
<MdWarning {...iconSize("8em")} />
<Title>{props.contentName}</Title>
<SubText>
<Trans>This channel is marked as mature.</Trans>
</SubText>

<Confirmation>
<Checkbox
checked={state.layout.getSectionState(
LAYOUT_SECTIONS.MATURE,
false,
<Suspense fallback={<CircularProgress />}>
<Switch fallback={props.children}>
<Match
when={
props.enabled &&
(geoQuery.isLoading ||
geoQuery.error ||
(geoQuery.data && geoQuery.data.isAgeRestrictedGeo))
}
>
<Base>
<MdWarning {...iconSize("8em")} />
<Text class="headline" size="large">
{props.contentName}
</Text>

<Text class="body" size="large">
{geoQuery.data?.countryCode == "GB" ? (
<Trans>
This channel is not available in your region while we review
options on legal compliance.
</Trans>
) : (
<Trans>This content is not available in your region.</Trans>
)}
onChange={() =>
state.layout.toggleSectionState(LAYOUT_SECTIONS.MATURE, false)
}
/>
</Text>

<Trans>I confirm that I am at least 18 years old.</Trans>
</Confirmation>

<Actions>
<Button variant="secondary" onPress={() => history.back()}>
<Button variant="text" onPress={() => history.back()}>
<Trans>Back</Trans>
</Button>
<Button
variant="primary"
onPress={() =>
state.layout.getSectionState(LAYOUT_SECTIONS.MATURE) &&
state.layout.setSectionState(props.contentId + "-nsfw", true)
}
>
<Trans>Enter Channel</Trans>
</Button>
</Actions>
</Base>
</Match>
</Switch>
</Base>
</Match>
<Match when={props.enabled && (!confirmed() || !allowed())}>
<Base>
<MdWarning {...iconSize("8em")} />
<Text class="headline" size="large">
{props.contentName}
</Text>

<Text class="body" size="large">
<Trans>This channel is marked as mature.</Trans>
</Text>

<Confirmation>
<Checkbox
checked={state.layout.getSectionState(
LAYOUT_SECTIONS.MATURE,
false,
)}
onChange={() =>
state.layout.toggleSectionState(LAYOUT_SECTIONS.MATURE, false)
}
/>
<Text class="body" size="large">
<Trans>I confirm that I am at least 18 years old.</Trans>
</Text>
</Confirmation>

<Actions>
<Button variant="text" onPress={() => history.back()}>
<Trans>Back</Trans>
</Button>
<Button
variant="filled"
onPress={() =>
confirmed() &&
state.layout.setSectionState(props.contentId + "-nsfw", true)
}
>
<Trans>Enter Channel</Trans>
</Button>
</Actions>
</Base>
</Match>
</Switch>
</Suspense>
);
}

Expand All @@ -82,27 +136,17 @@ const Base = styled("div", {
padding: "var(--gap-lg)",
userSelect: "none",
overflowY: "auto",
color: "var(--md-sys-color-on-surface)",

"& svg": {
// TODO
fill: "orange",
},

gap: "var(--gap-sm)",
},
});

const Title = styled("h2", {
base: {
fontSize: "2em",
fontWeight: "black",
gap: "var(--gap-md)",
},
});

const SubText = styled("span", {
base: {},
});

const Confirmation = styled("label", {
base: {
display: "flex",
Expand Down