Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BAT gating for Web3 Brave Talk #1079

Merged
merged 22 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
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
55 changes: 55 additions & 0 deletions src/components/web3/NonExpandablePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { css } from "@emotion/react";
import React, { ReactNode, useState } from "react";
import { baseText } from "./styles";
import PlusImage from "../../images/plus.svg";
import MinusImage from "../../images/minus.svg";
import SpinnerImage from "../../images/spinner.svg";

interface Props {
header: string;
subhead: string;
loading?: boolean;
children?: ReactNode;
}

export const NonExapandablePanel: React.FC<Props> = ({
header,
subhead,
loading = false,
children,
Copy link
Contributor

@johnhalbert johnhalbert Jul 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

children looks like it's not used - if that's what was intended we should remove it.

}) => {
return (
<div
css={{
background: "rgba(255, 255, 255, 0.24)",
backdropFilter: "blur(8px)",
marginTop: "11px",
padding: "24px 19px",
textAlign: "left",
}}
>
<div
css={{
display: "flex",
alignItems: "center",
}}
>
<div css={{ flex: 1 }}>
<div
css={css(baseText, {
fontWeight: 500,
fontSize: "22px",
lineHeight: "32px",
})}
>
{header}
</div>
<div>{subhead}</div>
</div>
{loading && (
<img width={25} height={26} src={SpinnerImage} alt="spinner" />
)}
</div>
</div>
);
};
12 changes: 11 additions & 1 deletion src/components/web3/OptionalSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from "react";
import { useTranslation } from "react-i18next";
import { POAP, NFTcollection, NFT } from "./core";
import { ExapandablePanel } from "./ExpandablePanel";
import { NonExapandablePanel } from "./NonExpandablePanel";
import { SelectableImageList } from "./SelectableImageList";
import { SelectablePoapList } from "./SelectablePoapList";
import { SelectableNFTCollectionList } from "./SelectableNFTCollectionList";
import { PermissionTypeSelector } from "./PermissionTypeSelector";
import noNftImage from "../../images/no-nft-image.png";
import { Web3PermissionType } from "./api";

interface Props {
startCall: boolean;
Expand All @@ -16,7 +18,7 @@ interface Props {
nft: string | null;
setNft: (nft: string) => void;
permissionType: string;
setPermissionType: (permissionType: string) => void;
setPermissionType: (permissionType: Web3PermissionType) => void;
participantPoaps: POAP[];
setParticipantPoaps: (participantPoaps: POAP[]) => void;
moderatorPoaps: POAP[];
Expand Down Expand Up @@ -157,6 +159,14 @@ export const OptionalSettings: React.FC<Props> = ({
</ExapandablePanel>
</React.Fragment>
)}
{startCall && permissionType === "balance" && (
tackley marked this conversation as resolved.
Show resolved Hide resolved
<React.Fragment>
<NonExapandablePanel
header={t("bat_gating_panel_header")}
subhead={t("bat_gating_panel_subheader")}
/>
</React.Fragment>
)}
</div>
);
};
9 changes: 8 additions & 1 deletion src/components/web3/PermissionTypeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from "react";
import { css } from "@emotion/react";
import { useTranslation } from "react-i18next";
import { Web3PermissionType } from "./api";

type Props = {
permissionType: string;
setPermissionType: (permissionType: string) => void;
setPermissionType: (permissionType: Web3PermissionType) => void;
};

const styles = {
Expand Down Expand Up @@ -53,6 +54,12 @@ export const PermissionTypeSelector: React.FC<Props> = ({
>
{t("nft_collection_permission_type")}
</button>
<button
onClick={() => setPermissionType("balance")}
css={[styles.base, permissionType === "balance" && styles.selected]}
>
{"BAT Gating"}
</button>
</div>
);
};
14 changes: 14 additions & 0 deletions src/components/web3/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ declare let window: any;
// making a cross domain call - see https://github.com/brave/devops/issues/5445.
const SIMPLEHASH_PROXY_ROOT_URL = "/api/v1/simplehash";

export type Web3PermissionType = "POAP" | "NFT-collection" | "balance";

export interface Web3Authentication {
method: string;
proof: {
Expand All @@ -22,6 +24,18 @@ export interface Web3Authorization {
method: string;
POAPs: Web3AuthList;
Collections: Web3AuthList;
Balances: Web3BalancesList;
}

export interface Web3BalancesList {
participants: Web3BalancesRequireList;
moderators: Web3BalancesRequireList;
}

export interface Web3BalancesRequireList {
network: "ETH"; // "ETH" | "SOL" in the future probably
token?: string;
minimum: string; // in wei, e.g. 10e-18
}

export interface Web3AuthList {
Expand Down
28 changes: 20 additions & 8 deletions src/hooks/use-web3-call-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ import {
Web3RequestBody,
Web3Authentication,
web3Prove,
Web3PermissionType,
} from "../components/web3/api";
import { POAP, NFTcollection } from "../components/web3/core";
import { generateRoomName } from "../lib";
import { fetchJWT } from "../rooms";

interface Web3CallState {
web3Address?: string;
permissionType: string;
permissionType: Web3PermissionType;
nft: string | null;
participantPoaps: POAP[];
moderatorPoaps: POAP[];
participantNFTCollections: NFTcollection[];
moderatorNFTCollections: NFTcollection[];
setWeb3Address: (web3Address: string, event: string) => void;
setPermissionType: (permissionType: string) => void;
setPermissionType: (permissionType: Web3PermissionType) => void;
setNft: (nft: string) => void;
setParticipantPoaps: (participanPoaps: POAP[]) => void;
setModeratorPoaps: (moderatorPoaps: POAP[]) => void;
Expand All @@ -38,7 +39,8 @@ export function useWeb3CallState(
setFeedbackMessage: (message: TranslationKeys) => void
): Web3CallState {
const [web3Address, _setWeb3Address] = useState<string>();
const [permissionType, setPermissionType] = useState<string>("POAP");
const [permissionType, setPermissionType] =
useState<Web3PermissionType>("NFT-collection");
const [nft, setNft] = useState<string | null>(null);
const [participantPoaps, setParticipantPoaps] = useState<POAP[]>([]);
const [moderatorPoaps, setModeratorPoaps] = useState<POAP[]>([]);
Expand Down Expand Up @@ -105,12 +107,10 @@ export function useWeb3CallState(
} catch (e: any) {
console.error(e);

if (
e.message.includes(
"You must have an appropriate token to join this call"
)
) {
if (e.message.includes("no-token")) {
setFeedbackMessage("invalid_token_error");
} else if (e.message.includes("no-currency")) {
setFeedbackMessage("not_enough_currency_error");
} else {
setFeedbackMessage("not_participant_error");
}
Expand Down Expand Up @@ -151,6 +151,18 @@ export function useWeb3CallState(
deny: [],
},
},
Balances: {
participants: {
network: "ETH" as const,
token: "BAT",
minimum: "1",
},
moderators: {
network: "ETH" as const,
token: "BAT",
minimum: "1",
},
},
},
avatarURL: nft,
};
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"avatar_fetch_error": "Failed to fetch avatar NFTs",
"avatar_nft_subhead": "Currently supports Ethereum ERC-721 NFTs",
"bat_gating_panel_header": "Non-Zero BAT gating will be applied.",
"bat_gating_panel_subheader": "Users will need to have a non-zero balance of BAT in their wallet to join this call.",
"call_permission_type": "Call permission type:",
"check_subscription": "Checking subscription status...",
"create_link": "Create link",
Expand All @@ -27,6 +29,7 @@
"my_account_link": "My Account",
"nft_collection_permission_type": "NFT Collection",
"not_participant_error": "Not listed as participant",
"not_enough_currency_error": "Access failure: Non-zero amount of BAT in your wallet is required to join this call.",
"notice_text": "Encrypted video calls. Right in your browser.",
"participant_nft_collection_panel_header": "Require an NFT Collection",
"participant_nft_collection_panel_subhead": "Select an NFT Collection that participants must verify to join",
Expand Down
10 changes: 6 additions & 4 deletions src/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@ const roomsRequest = async ({
failureMessages: failureMessages,
});
}

const respText = await response.text();
const message =
failureMessages[status] ||
`Request failed: ${status} ${response.statusText}`;
status == 401
? respText
: failureMessages[status] ||
`Request failed: ${status} ${response.statusText}`;

console.warn(`!!! body: ${await response.text()}`);
console.warn(`!!! body: ${respText}`);
throw new Error(message);
}

Expand Down