Skip to content

Commit

Permalink
Merge pull request #15 from frak-id/feat/referral
Browse files Browse the repository at this point in the history
Feat/referral
  • Loading branch information
srod authored May 16, 2024
2 parents f401b8d + 8deee63 commit 8e0cd6a
Show file tree
Hide file tree
Showing 40 changed files with 1,914 additions and 76 deletions.
7 changes: 7 additions & 0 deletions .changeset/afraid-worms-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@frak-labs/nexus-example": patch
"@frak-labs/nexus-wallet": patch
"@frak-labs/nexus-sdk": patch
---

Add referral workflow
Binary file modified bun.lockb
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.popup {
align-items: center;
color: #383f4e;
display: flex;
justify-content: center;
left: 0;
overflow: hidden;
position: fixed;
scroll-behavior: smooth;
bottom: 0;
width: 100%;
z-index: 10000001;
font-size: 16px;
line-height: 24px;
}

.popup__content {
background-color: #fff;
display: flex;
flex-direction: column;
max-height: calc(100% - 80px);
max-width: 978px;
overflow: visible;
margin: 10px;
padding: 10px;
position: relative;
width: 100%;
border-radius: 10px;
border: 1px solid #e5e5e5;
}

.popup__explanation {
color: #2a303b;
font-size: 18px;
line-height: 24px;
margin-bottom: 20px;
font-weight: 600;
text-align: center;
}
37 changes: 37 additions & 0 deletions packages/example/src/module/article/component/Popup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import css from "!!raw-loader!./index.module.css";
import { frakWalletSdkConfig } from "@/context/frak-wallet/config";
import { Button } from "@/module/common/component/Button";
import type { Article } from "@/type/Article";

export const cssRaw = css;

function buildRedirectUrl(redirectUrl: string) {
const outputUrl = new URL(frakWalletSdkConfig.walletUrl);
outputUrl.pathname = "/register";
outputUrl.searchParams.set("redirectUrl", encodeURIComponent(redirectUrl));
return outputUrl.toString();
}

export function Popup({ article }: { article: Article }) {
return (
<div className={"popup"}>
<div className={"popup__content"}>
<p className={"popup__explanation"}>
A Nexus user shared this link with you, create a Nexus
account to instantly get 50rFRK
</p>
<Button
variant={article.provider}
size={"small"}
onClick={() => {
window.location.href = buildRedirectUrl(
`${window.location.origin}/article?id=${article.id}`
);
}}
>
Connect or create a Nexus Wallet
</Button>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function InjectBannerComponent({
const articleIframeDocument = articleIframe?.contentWindow?.document;

useEffect(() => {
const containerName = "frak-paywall";
const containerName = "frak-banner";
let containerRoot =
articleIframeDocument?.getElementById(containerName);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Popup } from "@/module/article/component/Popup";
import { cssRaw as cssRawButton } from "@/module/common/component/Button";
import type { Article } from "@/type/Article";
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { cssRaw } from "../Popup";

export function InjectPopupComponent({
article,
}: {
article: Article;
}) {
const [containerRoot, setContainerRoot] = useState<Element | undefined>();

// Get the article's iframe
const articleIframeName = "frak-article-iframe";
const articleIframe = document.querySelector(
`#${articleIframeName}`
) as HTMLIFrameElement;
const articleIframeDocument = articleIframe?.contentWindow?.document;

useEffect(() => {
const containerName = "frak-popup";
let containerRoot =
articleIframeDocument?.getElementById(containerName);

// If the container does not exist, create it
if (!containerRoot) {
const appRoot = document.createElement("div");
appRoot.id = containerName;
articleIframeDocument?.body?.insertAdjacentElement(
"beforeend",
appRoot
);
containerRoot =
articleIframeDocument?.getElementById(containerName);

if (containerRoot) {
containerRoot.style.width = "100%";
setContainerRoot(containerRoot);
}
}
}, [articleIframeDocument]);

if (
!(
articleIframe &&
articleIframeDocument &&
articleIframe.contentDocument
)
) {
console.log(`iframe ${articleIframeName} not found`);
return null;
}

return (
containerRoot && (
<>
{createPortal(<Popup article={article} />, containerRoot)}
{/* Inject the styles into the iframe */}
{createPortal(
<style>
{cssRaw.toString()}
{cssRawButton.toString()}
</style>,
articleIframe.contentDocument.head
)}
</>
)
);
}
10 changes: 10 additions & 0 deletions packages/example/src/module/article/component/Read/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { InjectBannerComponent } from "@/module/article/component/Read/InjectBannerComponent";
import { InjectPopupComponent } from "@/module/article/component/Read/InjectPopupComponent";
import { InjectUnlockComponent } from "@/module/article/component/Read/InjectUnlockComponent";
import { Skeleton } from "@/module/common/component/Skeleton";
import type { Article } from "@/type/Article";
import {
useArticleUnlockOptions,
useArticleUnlockStatus,
useNexusReferral,
useWalletStatus,
} from "@frak-labs/nexus-sdk/react";
import { useEffect, useState } from "react";
Expand All @@ -24,6 +26,11 @@ export function ReadArticle({
// The iframe reference
const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);

// The nexus referral
const { data: referral } = useNexusReferral({
contentId: article.contentId as Hex,
});

// The unlock options for the article
const { data: unlockOptions } = useArticleUnlockOptions({
articleId: article.id as Hex,
Expand Down Expand Up @@ -71,6 +78,9 @@ export function ReadArticle({
walletStatus?.key === "not-connected" && (
<InjectBannerComponent article={article} />
)}
{injecting > 0 && referral?.key === "referred-history" && (
<InjectPopupComponent article={article} />
)}
{articleUnlockStatus &&
articleUnlockStatus?.key !== "waiting-response" ? (
<iframe
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/core/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export {
getStartArticleUnlockUrl,
decodeStartUnlockReturn,
} from "./startUnlock";
export { setUserReferred } from "./setUserReferred";
31 changes: 31 additions & 0 deletions packages/sdk/src/core/actions/setUserReferred.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Address, Hex } from "viem";
import type { NexusClient, SetUserReferredReturnType } from "../types";

/**
* Type used to get the user referred options
*/
export type SetUserReferredParams = {
contentId: Hex;
walletAddress: Address;
};

/**
* Function used to watch a current user referred
* @param client
* @param contentId
* @param walletAddress
* @param callback
*/
export function setUserReferred(
client: NexusClient,
{ contentId, walletAddress }: SetUserReferredParams,
callback: (status: SetUserReferredReturnType) => void
) {
return client.listenerRequest(
{
method: "frak_listenToSetUserReferred",
params: [contentId, walletAddress],
},
callback
);
}
2 changes: 2 additions & 0 deletions packages/sdk/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export type {
RedirectRpcSchema,
StartArticleUnlockParams,
StartArticleUnlockReturnType,
SetUserReferredParams,
SetUserReferredReturnType,
// Client
NexusClient,
// Transport
Expand Down
4 changes: 4 additions & 0 deletions packages/sdk/src/core/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export type {
StartArticleUnlockParams,
StartArticleUnlockReturnType,
} from "./rpc/startUnlock";
export type {
SetUserReferredParams,
SetUserReferredReturnType,
} from "./rpc/setUserReferred";
export type { IFrameRpcSchema, RedirectRpcSchema } from "./rpc";
// Client related
export type { NexusClient } from "./client";
Expand Down
11 changes: 10 additions & 1 deletion packages/sdk/src/core/types/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Hex } from "viem";
import type { Address, Hex } from "viem";
import type { SetUserReferredReturnType } from "./rpc/setUserReferred";
import type {
StartArticleUnlockParams,
StartArticleUnlockReturnType,
Expand Down Expand Up @@ -35,6 +36,14 @@ export type IFrameRpcSchema = [
Parameters: [contentId: Hex, articleId: Hex];
ReturnType: ArticleUnlockStatusReturnType;
},
/**
* Method used to set the referred user
*/
{
Method: "frak_listenToSetUserReferred";
Parameters: [contentId: Hex, walletAddress: Address];
ReturnType: SetUserReferredReturnType;
},
];

/**
Expand Down
28 changes: 28 additions & 0 deletions packages/sdk/src/core/types/rpc/setUserReferred.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Hex } from "viem";

/**
* Parameters of the referred request
*/
export type SetUserReferredParams = Readonly<{
contentId: Hex;
}>;

/**
* Return type of the referred request
*/
export type SetUserReferredReturnType =
| UserIsSameWallet
| UserReferredSuccessful
| UserReferredHistory;

type UserIsSameWallet = {
key: "same-wallet";
};

type UserReferredSuccessful = {
key: "referred-successful";
};

type UserReferredHistory = {
key: "referred-history";
};
26 changes: 20 additions & 6 deletions packages/sdk/src/core/utils/compression/iframeRpcKeyProvider.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { KeyProvider } from "../../types/compression";
import type { IFrameRpcSchema } from "../../types/rpc";
import type { UnlockOptionsReturnType } from "../../types/rpc/unlockOption";
import type { ArticleUnlockStatusReturnType } from "../../types/rpc/unlockStatus";
import type { WalletStatusReturnType } from "../../types/rpc/walletStatus";
import type {
ArticleUnlockStatusReturnType,
ExtractedParametersFromRpc,
ExtractedReturnTypeFromRpc,
} from "../../types/transport";
IFrameRpcSchema,
KeyProvider,
SetUserReferredReturnType,
UnlockOptionsReturnType,
WalletStatusReturnType,
} from "../../types";

/**
* Get the right request key provider for the given args
Expand All @@ -30,6 +31,11 @@ export const iFrameRequestKeyProvider: KeyProvider<
return ["article-unlock-status", args.params[0], args.params[1]];
}

// Referred user key
if (args.method === "frak_listenToSetUserReferred") {
return ["user-referred", args.params[0], args.params[1]];
}

// Not found
throw new Error(`No key provider found for the arguments ${args}`);
};
Expand Down Expand Up @@ -87,5 +93,13 @@ export function getIFrameResponseKeyProvider<
]) as RpcResponseKeyProvider<TParameters>;
}

// Referred user key
if (param.method === "frak_listenToSetUserReferred") {
return ((response: SetUserReferredReturnType) => [
"user-referred",
response.key,
]) as RpcResponseKeyProvider<TParameters>;
}

throw new Error(`No key provider found for the request ${param}`);
}
1 change: 1 addition & 0 deletions packages/sdk/src/react/hook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { useWalletStatus } from "./useWalletStatus";
export type { WalletStatusQueryReturnType } from "./useWalletStatus";
export { useArticleUnlockStatus } from "./useArticleUnlockStatus";
export type { ArticleUnlockStatusQueryReturnType } from "./useArticleUnlockStatus";
export { useNexusReferral } from "./useNexusReferral";
11 changes: 11 additions & 0 deletions packages/sdk/src/react/hook/useMounted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useEffect, useState } from "react";

export function useMounted() {
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

return mounted;
}
Loading

0 comments on commit 8e0cd6a

Please sign in to comment.