Skip to content

Commit

Permalink
support new handle format
Browse files Browse the repository at this point in the history
  • Loading branch information
Kris Urbas committed Nov 1, 2023
1 parent ed09cc2 commit 14d4a8e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 45 deletions.
18 changes: 14 additions & 4 deletions e2e/fixtures/profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,21 @@ import { test as base, expect } from "@playwright/test";
import { ProfilePage } from "./ProfilePage";

export const test = base.extend<{
anyProfile: ProfilePage;
v1Profile: ProfilePage;
v1ProfileWithSuffix: ProfilePage;
v2Profile: ProfilePage;
}>({
anyProfile: async ({ page }, use) => {
const publication = new ProfilePage(page, "lensprotocol");
await use(publication);
v1Profile: async ({ page }, use) => {
const profile = new ProfilePage(page, "lensprotocol");
await use(profile);
},
v1ProfileWithSuffix: async ({ page }, use) => {
const profile = new ProfilePage(page, "lensprotocol.lens");
await use(profile);
},
v2Profile: async ({ page }, use) => {
const profile = new ProfilePage(page, "lens/lensprotocol");
await use(profile);
},
});

Expand Down
74 changes: 37 additions & 37 deletions e2e/profiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,44 @@ test.use(devices["Desktop Chrome"]);

test.describe("Given a Profile link", async () => {
test.describe("When opening it", async () => {
test("Then it should show relevant app options", async ({ anyProfile }) => {
await anyProfile.open();
test("Then it should show relevant app options", async ({ v1Profile }) => {
await v1Profile.open();

await expect(anyProfile.options).toHaveText([
await expect(v1Profile.options).toHaveText([
"Buttrfly",
"Collectz",
"Hey",
"LensFrens",
"Lensta",
"Riff",
"Soclly",
"Tape"
"Tape",
]);
});
});
});

test.describe("Given a Publication link posted on a social media website/app", async () => {
test.describe("When checking Open Graph meta tags", async () => {
test("Then it should render the expected base-line meta tags", async ({ anyProfile }) => {
await anyProfile.open();
test("Then it should render the expected base-line meta tags", async ({ v1Profile }) => {
await v1Profile.open();

expect(await anyProfile.extractOpenGraphProperties()).toMatchObject({
"og:title": `@${anyProfile.handle} profile`,
expect(await v1Profile.extractOpenGraphProperties()).toMatchObject({
"og:title": `@${v1Profile.handle} profile`,
"og:description": "The Social Layer for Web3 🌿",
"og:url": expect.stringContaining(`/u/${anyProfile.handle}`),
"og:url": expect.stringContaining(`/u/${v1Profile.handle}`),
"og:site_name": "Lens Share",
"og:type": "profile",
});
});

test("Then it should include the expected Twitter Card meta tags", async ({ anyProfile }) => {
await anyProfile.open();
test("Then it should include the expected Twitter Card meta tags", async ({ v1Profile }) => {
await v1Profile.open();

expect(await anyProfile.extractTwitterMetaTags()).toEqual({
expect(await v1Profile.extractTwitterMetaTags()).toEqual({
"twitter:card": "summary_large_image",
"twitter:site": "LensProtocol",
"twitter:title": `@${anyProfile.handle} profile`,
"twitter:title": `@${v1Profile.handle} profile`,
"twitter:description": "The Social Layer for Web3 🌿",
"twitter:image": expect.any(String),
"twitter:image:type": "image/png",
Expand All @@ -53,26 +53,26 @@ test.describe("Given a Publication link posted on a social media website/app", a

test.describe("When the link includes the `by` attribution param", async () => {
test("Then it should mention the originating app in page `title` and Open Graph `site_name` tag", async ({
anyProfile,
v1Profile,
}) => {
await anyProfile.openAsSharedBy("Hey");
await v1Profile.openAsSharedBy("Hey");

expect(await anyProfile.getTitle()).toContain("Hey");
expect(await anyProfile.extractOpenGraphProperties()).toMatchObject({
expect(await v1Profile.getTitle()).toContain("Hey");
expect(await v1Profile.extractOpenGraphProperties()).toMatchObject({
"og:site_name": "Hey",
});
});

test("Then it should mention the originating app in Twitter Card `site` if a Twitter handle is provided in the app manifest", async ({
anyProfile,
v1Profile,
}) => {
await anyProfile.openAsSharedBy("Hey");
await v1Profile.openAsSharedBy("Hey");

expect(await anyProfile.getTitle()).toContain("Hey");
expect(await anyProfile.extractOpenGraphProperties()).toMatchObject({
expect(await v1Profile.getTitle()).toContain("Hey");
expect(await v1Profile.extractOpenGraphProperties()).toMatchObject({
"og:site_name": "Hey",
});
expect(await anyProfile.extractTwitterMetaTags()).toMatchObject({
expect(await v1Profile.extractTwitterMetaTags()).toMatchObject({
"twitter:site": "heydotxyz",
});
});
Expand All @@ -81,10 +81,10 @@ test.describe("Given a Publication link posted on a social media website/app", a

test.describe("Given a Profile link with `by` attribution param", async () => {
test.describe("When opening it", async () => {
test("Then it should show the specified app first", async ({ anyProfile }) => {
await anyProfile.openAsSharedBy("Hey");
test("Then it should show the specified app first", async ({ v1Profile }) => {
await v1Profile.openAsSharedBy("Hey");

await expect(anyProfile.options).toHaveText([
await expect(v1Profile.options).toHaveText([
"Hey",
"Buttrfly",
"Collectz",
Expand All @@ -99,33 +99,33 @@ test.describe("Given a Profile link with `by` attribution param", async () => {

test.describe("When opening it on a platform not supported by the specified app", async () => {
test("Then it should show a message an attribution message before offering other options", async ({
anyProfile,
v1Profile,
}) => {
await anyProfile.openAsSharedBy("orb");
await v1Profile.openAsSharedBy("orb");

await expect(anyProfile.context).toHaveText("Shared via Orb mobile app.");
await expect(v1Profile.context).toHaveText("Shared via Orb mobile app.");
});
});
});

test.describe("Given an opened Profile link", async () => {
test.describe("When submitting an app choice", async () => {
test("Then it should open the publication with the selected app", async ({ anyProfile }) => {
await anyProfile.open();
const url = await anyProfile.justOnce("Hey");
test("Then it should open the publication with the selected app", async ({ v1Profile }) => {
await v1Profile.open();
const url = await v1Profile.justOnce("Hey");

await expect(url).toMatch(`https://hey.xyz/u/${anyProfile.handle}`);
await expect(url).toMatch(`https://hey.xyz/u/${v1Profile.handle}`);
});
});

test.describe("When submitting an app choice with 'Remember' checkbox selected", async () => {
test("Then it should use the same app for all future publications", async ({ anyProfile }) => {
await anyProfile.open();
await anyProfile.remember("Hey");
test("Then it should use the same app for all future publications", async ({ v1Profile }) => {
await v1Profile.open();
await v1Profile.remember("Hey");

const response = await anyProfile.open();
const response = await v1Profile.open();

await expect(response?.url()).toMatch(`https://hey.xyz/u/${anyProfile.handle}`);
await expect(response?.url()).toMatch(`https://hey.xyz/u/${v1Profile.handle}`);
});
});
});
3 changes: 2 additions & 1 deletion src/app/u/[handle]/opengraph-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ImageResponse } from "next/server";
import { client } from "@/app/client";
import { gintoNordMediumData } from "@/app/fonts";
import { formatProfileHandle } from "@/formatters";
import { mapV1HandleToV2 } from "@/utils/handle";
import { resolveMediaUrl } from "@/utils/media";

export type ImageProps = {
Expand Down Expand Up @@ -56,7 +57,7 @@ function resolveProfileImage(profile: ProfileFragment) {
}

export default async function Image({ params }: ImageProps) {
const profile = await client.profile.fetch({ forHandle: params.handle });
const profile = await client.profile.fetch({ forHandle: mapV1HandleToV2(params.handle) });

if (!profile) notFound();

Expand Down
8 changes: 5 additions & 3 deletions src/app/u/[handle]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { twitterHandle } from "@/config";
import { AppManifest, findApp, findFavoriteApp, findProfileApps } from "@/data";
import { formatProfileHandle } from "@/formatters";
import { resolvePlatformType } from "@/utils/device";
import { mapV1HandleToV2 } from "@/utils/handle";
import { resolveAttribution } from "@/utils/request";

import { openWith } from "./actions";
Expand All @@ -25,12 +26,13 @@ export type ProfilePageProps = {

export default async function ProfilePage({ params, searchParams }: ProfilePageProps) {
const platform = resolvePlatformType();
const profile = await client.profile.fetch({ forHandle: params.handle });
const handle = mapV1HandleToV2(params.handle);
const profile = await client.profile.fetch({ forHandle: handle });

const favoriteApp = await findFavoriteApp({ platform });

if (favoriteApp) {
redirectTo(favoriteApp, params.handle);
redirectTo(favoriteApp, handle);
}

if (!profile) notFound();
Expand Down Expand Up @@ -72,7 +74,7 @@ export async function generateMetadata(
{ params, searchParams }: ProfilePageProps,
parent: ResolvingMetadata
) {
const profile = await client.profile.fetch({ forHandle: params.handle });
const profile = await client.profile.fetch({ forHandle: mapV1HandleToV2(params.handle) });

if (!profile) notFound();

Expand Down
19 changes: 19 additions & 0 deletions src/utils/handle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const V1_SUFFIX = ".lens";
const V2_NAMESPACE = "lens";

function isV1Handle(handle: string): boolean {
return handle.endsWith(".lens");
}

/**
* v1 handle format: `handle.lens`
* v2 handle format: `namespace/handle`
*/
export function mapV1HandleToV2(handle: string): string {
if (isV1Handle(handle)) {
const localName = handle.slice(0, -V1_SUFFIX.length);
return `${V2_NAMESPACE}/${localName}`;
}

return handle;
}

0 comments on commit 14d4a8e

Please sign in to comment.