@adland/react 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @adland/react
2
2
 
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [90c2ad6]
8
+ - @adland/data@0.10.0
9
+
10
+ ## 0.7.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 0225dd5: export new types & display metadata in ad react component
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [0225dd5]
19
+ - @adland/data@0.9.0
20
+
3
21
  ## 0.6.2
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adland/react",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -28,7 +28,7 @@
28
28
  "@types/react-dom": "^18.3.1",
29
29
  "lucide-react": "0.561.0",
30
30
  "tsup": "^8.0.1",
31
- "@adland/data": "0.8.0"
31
+ "@adland/data": "0.10.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@typescript-eslint/eslint-plugin": "^7.13.1",
@@ -1,10 +1,13 @@
1
1
  import { useEffect, useRef, useCallback } from "react";
2
2
  import { sendTrackRequest } from "../utils/sdk";
3
3
  import sdk from "@farcaster/miniapp-sdk";
4
- import { adCardIcon } from "../utils/constants";
5
4
  import { Loader, SquareDashed } from "lucide-react";
6
5
  import { type AdData } from "@adland/data";
7
6
  import { useFetch } from "../hooks/useFetch";
7
+ import BasicAdBody from "./BasicAdBody";
8
+ import LinkAdContent from "./content/LinkAdContent";
9
+ import MiniappAdContent from "./content/MiniappAdContent";
10
+ import CastAdContent from "./content/CastAdContent";
8
11
 
9
12
  type Network = "testnet" | "mainnet";
10
13
 
@@ -167,13 +170,11 @@ export function Ad({
167
170
  if (adData) {
168
171
  return (
169
172
  <>
170
- {adData.type === "link" && <LinkAdContent data={adData.data} />}
173
+ {adData.type === "link" && <LinkAdContent data={adData} />}
171
174
 
172
- {adData.type === "cast" && <CastAdContent data={adData.data} />}
175
+ {adData.type === "cast" && <CastAdContent data={adData} />}
173
176
 
174
- {adData.type === "miniapp" && (
175
- <MiniAppAdContent data={adData.data} />
176
- )}
177
+ {adData.type === "miniapp" && <MiniappAdContent data={adData} />}
177
178
  </>
178
179
  );
179
180
  } else {
@@ -188,23 +189,6 @@ export function Ad({
188
189
  );
189
190
  }
190
191
 
191
- const BasicAdBody = ({
192
- children,
193
- ...rest
194
- }: { children: React.ReactNode } & React.HTMLAttributes<HTMLDivElement>) => {
195
- return (
196
- <div
197
- className="border p-2 md:p-4 flex hover:cursor-pointer hover:bg-primary/10 flex-row rounded-md justify-between items-center gap-2 relative border-b-2 border-b-primary"
198
- {...rest}
199
- >
200
- {children}
201
- <div className="inline-flex items-center border p-1 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80">
202
- AD
203
- </div>
204
- </div>
205
- );
206
- };
207
-
208
192
  function ErrorAdContent({ error }: { error: unknown }) {
209
193
  return (
210
194
  <BasicAdBody>
@@ -243,58 +227,3 @@ function EmtpyAdContent({ data }: { data: { url: string } }) {
243
227
  </BasicAdBody>
244
228
  );
245
229
  }
246
-
247
- // Link Ad Component
248
- function LinkAdContent({ data }: { data: { url: string } }) {
249
- const Icon = adCardIcon["link"];
250
- return (
251
- <BasicAdBody
252
- onClick={() => {
253
- sdk.actions.openUrl(data.url);
254
- }}
255
- >
256
- <div className="flex flex-row items-center gap-2">
257
- <Icon className="w-5 h-5 md:w-7 md:h-7" />
258
- <p className="font-bold text-primary">LINK</p>
259
- </div>
260
- </BasicAdBody>
261
- );
262
- }
263
-
264
- // Cast Ad Component
265
- function CastAdContent({ data }: { data: { hash: string } }) {
266
- const Icon = adCardIcon["cast"];
267
- return (
268
- <BasicAdBody
269
- onClick={() => {
270
- sdk.actions.viewCast({
271
- hash: data.hash,
272
- });
273
- }}
274
- >
275
- <div className="flex flex-row items-center gap-2">
276
- <Icon className="w-5 h-5 md:w-7 md:h-7" />
277
- <p className="font-bold text-primary">CAST</p>
278
- </div>
279
- </BasicAdBody>
280
- );
281
- }
282
-
283
- // MiniApp Ad Component
284
- function MiniAppAdContent({ data }: { data: { url: string } }) {
285
- const Icon = adCardIcon["miniapp"];
286
- return (
287
- <BasicAdBody
288
- onClick={() => {
289
- sdk.actions.openMiniApp({
290
- url: data.url,
291
- });
292
- }}
293
- >
294
- <div className="flex flex-row items-center gap-2">
295
- <Icon className="w-5 h-5 md:w-7 md:h-7" />
296
- <p className="font-bold text-primary">MINIAPP</p>
297
- </div>
298
- </BasicAdBody>
299
- );
300
- }
@@ -0,0 +1,27 @@
1
+ const BasicAdBody = ({
2
+ name,
3
+ children,
4
+ ...rest
5
+ }: {
6
+ children: React.ReactNode;
7
+ name?: string;
8
+ } & React.HTMLAttributes<HTMLDivElement>) => {
9
+ return (
10
+ <div
11
+ className="border p-2 md:p-4 flex hover:cursor-pointer hover:bg-primary/10 flex-row rounded-md justify-between items-center gap-2 relative border-b-2 border-b-primary"
12
+ {...rest}
13
+ >
14
+ {children}
15
+ <div className="flex flex-row items-center border-2 border-primary">
16
+ {name && (
17
+ <div className="text-xs font-semibold text-primary px-1">{name}</div>
18
+ )}
19
+ <div className="inline-flex items-center border p-1 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80">
20
+ AD
21
+ </div>
22
+ </div>
23
+ </div>
24
+ );
25
+ };
26
+
27
+ export default BasicAdBody;
@@ -0,0 +1,56 @@
1
+ import { adCardIcon } from "../../utils/constants";
2
+ import { formatRelativeTime } from "../../utils/relativeTime";
3
+ import sdk from "@farcaster/miniapp-sdk";
4
+ import BasicAdBody from "../BasicAdBody";
5
+ import { CastAd } from "@adland/data";
6
+
7
+ // Cast Ad Component
8
+ const CastAdContent = ({ data }: { data: CastAd }) => {
9
+ const { data: castData, metadata: castMetadata } = data;
10
+ const Icon = adCardIcon["cast"];
11
+ return (
12
+ <BasicAdBody
13
+ name={"CAST"}
14
+ onClick={() => {
15
+ sdk.actions.viewCast({
16
+ hash: castData.hash,
17
+ });
18
+ }}
19
+ >
20
+ <div className="flex flex-row items-center gap-2">
21
+ {castMetadata?.pfpUrl ? (
22
+ <img
23
+ src={castMetadata.pfpUrl}
24
+ alt="Cast pfp"
25
+ className="w-10 h-10 rounded-md"
26
+ />
27
+ ) : (
28
+ <Icon className="w-5 h-5 md:w-7 md:h-7" />
29
+ )}
30
+ <div className="flex flex-col gap-0">
31
+ {castMetadata?.username ? (
32
+ <div className="flex flex-row items-center gap-1">
33
+ <p className="font-bold text-primary">{castMetadata.username}</p>
34
+ {castMetadata.timestamp && (
35
+ <p className="text-xs text-muted-foreground/80">
36
+ {formatRelativeTime(
37
+ new Date(castMetadata.timestamp).getTime(),
38
+ )}
39
+ </p>
40
+ )}
41
+ </div>
42
+ ) : (
43
+ <p className="font-bold text-primary">CAST</p>
44
+ )}
45
+ {castMetadata?.text ? (
46
+ <p className="text-black">{castMetadata.text}</p>
47
+ ) : (
48
+ <p className="text-muted-foreground/80">cast</p>
49
+ )}
50
+ </div>
51
+ </div>
52
+ </BasicAdBody>
53
+ );
54
+ };
55
+
56
+ export default CastAdContent;
@@ -0,0 +1,49 @@
1
+ import sdk from "@farcaster/miniapp-sdk";
2
+ import BasicAdBody from "../BasicAdBody";
3
+ import { adCardIcon } from "../../utils/constants";
4
+ import { LinkAd } from "@adland/data";
5
+
6
+ // Link Ad Component
7
+ const LinkAdContent = ({ data }: { data: LinkAd }) => {
8
+ const Icon = adCardIcon["link"];
9
+ const { data: linkData, metadata: linkMetadata } = data;
10
+
11
+ return (
12
+ <BasicAdBody
13
+ name={"LINK"}
14
+ onClick={() => {
15
+ sdk.actions.openUrl(linkData.url);
16
+ }}
17
+ >
18
+ <div className="flex flex-row items-center gap-2 flex-1 min-w-0">
19
+ {linkMetadata?.image && (
20
+ <img
21
+ src={linkMetadata.image}
22
+ alt={linkMetadata.title || "Link preview"}
23
+ className="w-10 h-10 md:w-12 md:h-12 rounded object-cover flex-shrink-0"
24
+ />
25
+ )}
26
+ <div className="flex flex-col gap-1 min-w-0 flex-1">
27
+ {!Boolean(linkMetadata) && (
28
+ <div className="flex flex-row items-center gap-2">
29
+ <Icon className="w-5 h-5 md:w-7 md:h-7 flex-shrink-0" />
30
+ <p className="font-bold text-primary">LINK</p>
31
+ </div>
32
+ )}
33
+ {linkMetadata?.title && (
34
+ <p className="text-sm text-muted-foreground truncate">
35
+ {linkMetadata.title}
36
+ </p>
37
+ )}
38
+ {linkMetadata?.description && (
39
+ <p className="text-xs text-muted-foreground/80 line-clamp-1">
40
+ {linkMetadata.description}
41
+ </p>
42
+ )}
43
+ </div>
44
+ </div>
45
+ </BasicAdBody>
46
+ );
47
+ };
48
+
49
+ export default LinkAdContent;
@@ -0,0 +1,48 @@
1
+ import { MiniAppAd } from "@adland/data";
2
+ import sdk from "@farcaster/miniapp-sdk";
3
+ import BasicAdBody from "../BasicAdBody";
4
+ import { adCardIcon } from "../../utils/constants";
5
+
6
+ const MiniappAdContent = ({ data }: { data: MiniAppAd }) => {
7
+ const Icon = adCardIcon["miniapp"];
8
+ const { data: miniappData, metadata: miniappMetadata } = data;
9
+ console.log({ miniappMetadata });
10
+ return (
11
+ <BasicAdBody
12
+ name={"MINIAPP"}
13
+ onClick={() => {
14
+ sdk.actions.openMiniApp({
15
+ url: miniappData.url,
16
+ });
17
+ }}
18
+ >
19
+ <div className="flex flex-row items-center gap-2">
20
+ {miniappMetadata?.icon ? (
21
+ <img
22
+ src={miniappMetadata.icon}
23
+ alt="Miniapp icon"
24
+ className="w-10 h-10 rounded-md"
25
+ />
26
+ ) : (
27
+ <Icon className="w-5 h-5 md:w-7 md:h-7" />
28
+ )}
29
+ <div className="flex flex-col gap-0">
30
+ {miniappMetadata?.title ? (
31
+ <p className="font-bold text-primary">{miniappMetadata.title}</p>
32
+ ) : (
33
+ <p className="font-bold text-primary">MINIAPP</p>
34
+ )}
35
+ {miniappMetadata?.description ? (
36
+ <p className="text-xs text-muted-foreground/80">
37
+ {miniappMetadata.description}
38
+ </p>
39
+ ) : (
40
+ <p className="text-xs text-muted-foreground/80">miniapp</p>
41
+ )}
42
+ </div>
43
+ </div>
44
+ </BasicAdBody>
45
+ );
46
+ };
47
+
48
+ export default MiniappAdContent;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Formats a timestamp as a relative time string (e.g., "2 days ago", "55 minutes ago")
3
+ * @param timestamp - Unix timestamp in seconds or milliseconds
4
+ * @returns Formatted relative time string
5
+ */
6
+ export function formatRelativeTime(timestamp: number): string {
7
+ // Convert to milliseconds if timestamp is in seconds (less than year 2000 in ms)
8
+ const timestampMs = timestamp < 946684800000 ? timestamp * 1000 : timestamp;
9
+ const now = Date.now();
10
+ const diffMs = now - timestampMs;
11
+ const diffSeconds = Math.floor(diffMs / 1000);
12
+ const diffMinutes = Math.floor(diffSeconds / 60);
13
+ const diffHours = Math.floor(diffMinutes / 60);
14
+ const diffDays = Math.floor(diffHours / 24);
15
+ const diffWeeks = Math.floor(diffDays / 7);
16
+ const diffMonths = Math.floor(diffDays / 30);
17
+ const diffYears = Math.floor(diffDays / 365);
18
+
19
+ if (diffSeconds < 60) {
20
+ return diffSeconds <= 1 ? "just now" : `${diffSeconds} seconds ago`;
21
+ }
22
+
23
+ if (diffMinutes < 60) {
24
+ return diffMinutes === 1 ? "1 minute ago" : `${diffMinutes} minutes ago`;
25
+ }
26
+
27
+ if (diffHours < 24) {
28
+ return diffHours === 1 ? "1 hour ago" : `${diffHours} hours ago`;
29
+ }
30
+
31
+ if (diffDays < 7) {
32
+ return diffDays === 1 ? "1 day ago" : `${diffDays} days ago`;
33
+ }
34
+
35
+ if (diffWeeks < 4) {
36
+ return diffWeeks === 1 ? "1 week ago" : `${diffWeeks} weeks ago`;
37
+ }
38
+
39
+ if (diffMonths < 12) {
40
+ return diffMonths === 1 ? "1 month ago" : `${diffMonths} months ago`;
41
+ }
42
+
43
+ return diffYears === 1 ? "1 year ago" : `${diffYears} years ago`;
44
+ }