mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
fix(web): leave app invite consent to the client
Co-authored-by: Jitter <d14dfe033ef0f809866f9f984de04821b0d900d7652fd85a54776ee40ca3a68f@buzz.block.builderlab.xyz> Signed-off-by: Matt Toohey <contact@matttoohey.com>
This commit is contained in:
@@ -48,7 +48,6 @@ export function InvitePage({ code }: { code: string }) {
|
||||
const [document, setDocument] = React.useState<PolicyDocument | null>(null);
|
||||
const [ageConfirmed, setAgeConfirmed] = React.useState(false);
|
||||
const [agreementConfirmed, setAgreementConfirmed] = React.useState(false);
|
||||
const [opening, setOpening] = React.useState(false);
|
||||
const [joiningBrowser, setJoiningBrowser] = React.useState(false);
|
||||
const [browserJoinError, setBrowserJoinError] = React.useState<string | null>(
|
||||
null,
|
||||
@@ -104,18 +103,6 @@ export function InvitePage({ code }: { code: string }) {
|
||||
return ((await response.json()) as { receipt: string }).receipt;
|
||||
};
|
||||
|
||||
const openInvite = async () => {
|
||||
setOpening(true);
|
||||
try {
|
||||
const receipt = await acceptPolicy();
|
||||
const query = new URLSearchParams({ relay, code });
|
||||
if (receipt) query.set("policy_receipt", receipt);
|
||||
window.location.href = `buzz://join?${query.toString()}`;
|
||||
} finally {
|
||||
setOpening(false);
|
||||
}
|
||||
};
|
||||
|
||||
const joinInBrowser = async () => {
|
||||
setBrowserJoinError(null);
|
||||
setJoiningBrowser(true);
|
||||
@@ -135,7 +122,6 @@ export function InvitePage({ code }: { code: string }) {
|
||||
const browserSigningAvailable = hasNip07Provider();
|
||||
const disabled =
|
||||
policy === undefined ||
|
||||
opening ||
|
||||
joiningBrowser ||
|
||||
Boolean(policy?.age_attestation_required && !ageConfirmed) ||
|
||||
Boolean(
|
||||
@@ -144,7 +130,8 @@ export function InvitePage({ code }: { code: string }) {
|
||||
!agreementConfirmed,
|
||||
);
|
||||
const hasPolicyRequirements = Boolean(
|
||||
policy &&
|
||||
browserSigningAvailable &&
|
||||
policy &&
|
||||
(policy.age_attestation_required ||
|
||||
policy.terms_markdown ||
|
||||
policy.privacy_markdown),
|
||||
@@ -235,34 +222,18 @@ export function InvitePage({ code }: { code: string }) {
|
||||
{joiningBrowser ? "Joining…" : "Join in browser"}
|
||||
</Button>
|
||||
) : null}
|
||||
{policy === null ? (
|
||||
<Button
|
||||
asChild
|
||||
className={`h-10 w-full ${
|
||||
browserSigningAvailable
|
||||
? "border border-black bg-white text-black hover:bg-black/5"
|
||||
: "bg-black text-white hover:bg-black/90 focus-visible:ring-black"
|
||||
}`}
|
||||
>
|
||||
<a
|
||||
href={`buzz://join?relay=${encodeURIComponent(relay)}&code=${encodeURIComponent(code)}`}
|
||||
>
|
||||
Accept invite in Buzz
|
||||
</a>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`h-10 w-full disabled:cursor-not-allowed disabled:bg-black/30 disabled:text-white/70 ${
|
||||
browserSigningAvailable
|
||||
? "border border-black bg-white text-black hover:bg-black/5"
|
||||
: "bg-black text-white hover:bg-black/90 focus-visible:ring-black"
|
||||
}`}
|
||||
disabled={disabled}
|
||||
onClick={openInvite}
|
||||
>
|
||||
Accept invite in Buzz
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
asChild
|
||||
className={`h-10 w-full ${
|
||||
browserSigningAvailable
|
||||
? "border border-black bg-white text-black hover:bg-black/5"
|
||||
: "bg-black text-white hover:bg-black/90 focus-visible:ring-black"
|
||||
}`}
|
||||
>
|
||||
<a href={`buzz://join?${new URLSearchParams({ relay, code })}`}>
|
||||
Open invite in Buzz
|
||||
</a>
|
||||
</Button>
|
||||
{browserJoinError ? (
|
||||
<p className="text-sm text-red-700" role="alert">
|
||||
{browserJoinError}
|
||||
|
||||
@@ -18,7 +18,7 @@ test("home page shows repositories section", async ({ page }) => {
|
||||
await expect(page.getByText("Repositories")).toBeVisible();
|
||||
});
|
||||
|
||||
test("invite requires age and legal consent before opening Buzz", async ({
|
||||
test("browser joining requires consent but app handoff does not", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.route("**/api/join-policy", async (route) => {
|
||||
@@ -71,6 +71,14 @@ test("invite requires age and legal consent before opening Buzz", async ({
|
||||
]),
|
||||
});
|
||||
});
|
||||
await page.addInitScript(() => {
|
||||
Object.assign(window, {
|
||||
nostr: {
|
||||
getPublicKey: async () => "ab".repeat(32),
|
||||
signEvent: async (event: unknown) => event,
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.goto("/invite/demo-code");
|
||||
|
||||
await expect(
|
||||
@@ -87,9 +95,10 @@ test("invite requires age and legal consent before opening Buzz", async ({
|
||||
const agreementConfirmation = page.getByLabel(
|
||||
"I agree to the Buzz Terms of Service and Privacy Policy.",
|
||||
);
|
||||
const acceptInvite = page.getByRole("button", {
|
||||
name: "Accept invite in Buzz",
|
||||
});
|
||||
const acceptInvite = page.getByRole("button", { name: "Join in browser" });
|
||||
await expect(
|
||||
page.getByRole("link", { name: "Open invite in Buzz" }),
|
||||
).toHaveAttribute("href", /^buzz:\/\/join\?relay=.*&code=demo-code$/);
|
||||
|
||||
await expect(ageConfirmation).toBeVisible();
|
||||
await expect(agreementConfirmation).toBeVisible();
|
||||
@@ -128,6 +137,39 @@ test("invite requires age and legal consent before opening Buzz", async ({
|
||||
expect(consentBox?.width).toBe(acceptButtonBox?.width);
|
||||
});
|
||||
|
||||
for (const policyStatus of [200, 503]) {
|
||||
test(`app invite handoff needs no browser consent (policy HTTP ${policyStatus})`, async ({
|
||||
page,
|
||||
}) => {
|
||||
let acceptanceRequests = 0;
|
||||
await page.route("**/api/join-policy", (route) =>
|
||||
route.fulfill({
|
||||
status: policyStatus,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
policy: {
|
||||
terms_markdown: "Terms",
|
||||
age_attestation_required: true,
|
||||
version: "v1",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
await page.route("**/api/invites/accept-policy", (route) => {
|
||||
acceptanceRequests++;
|
||||
return route.fulfill({ status: 500 });
|
||||
});
|
||||
const loaded = page.waitForResponse("**/api/join-policy");
|
||||
await page.goto("/invite/demo-code");
|
||||
await loaded;
|
||||
await expect(
|
||||
page.getByRole("link", { name: "Open invite in Buzz" }),
|
||||
).toHaveAttribute("href", /^buzz:\/\/join\?relay=.*&code=demo-code$/);
|
||||
await expect(page.getByRole("checkbox")).toHaveCount(0);
|
||||
expect(acceptanceRequests).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
test("invite can enroll a NIP-07 identity for browser access", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
Reference in New Issue
Block a user