Skip to content

Commit

Permalink
Added waves like posts renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
dkildar committed Jan 19, 2025
1 parent cc86543 commit aea81b7
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 15 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ecency/renderer",
"version": "1.0.0",
"version": "1.1.0",
"description": "React components for rendering Hive posts using Ecency extensions",
"keywords": [],
"files": [
Expand Down Expand Up @@ -28,6 +28,7 @@
"coverage": "vitest run --coverage"
},
"devDependencies": {
"@hiveio/dhive": "^1.3.2",
"@testing-library/dom": "^8.11.3",
"@testing-library/react": "^12.1.3",
"@types/node": "^22.10.5",
Expand Down Expand Up @@ -77,6 +78,7 @@
"dependencies": {
"@ecency/render-helper": "^2.2.36",
"clsx": "^2.1.1",
"medium-zoom": "^1.1.0"
"medium-zoom": "^1.1.0",
"react-twitter-embed": "^4.0.4"
}
}
8 changes: 8 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export function App() {
"\n" +
"▶️ [3Speak](https://3speak.tv/watch?v=theycallmedan/swqpoete)",
],
[
"Wave, Threads, LikeTu, testhreads posts",
"Hello world!\n\nhttps://ecency.com/@demo.com/re-ecencywaves-2024127t153020761z\n\nhttps://inleo.io/threads/view/vimukthi/re-leothreads-2tbpqwfov?referral=vimukthi\n\nhttps://ecency.com/waves/@shamis/wave-2025116t2377298z\n",
],
[
"Twitter widget",
"Hello world\n\nhttps://x.com/it_vicev/status/1798031021692055724",
],
];
return (
<div className="storybook">
Expand Down
18 changes: 18 additions & 0 deletions src/lib/api/hive.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Entry } from "@ecency/render-helper/lib/types";

const cache = new Map<string, Entry>();

export async function getCachedPost(username: string, permlink: string) {
if (cache.has(`${username}/${permlink}`)) {
return cache.get(`${username}/${permlink}`);
}

const response = (await (window as any).dHiveClient.call(
"condenser_api",
"get_content",
[username, permlink],
)) as Entry;

cache.set(`${username}/${permlink}`, response);
return response;
}
1 change: 1 addition & 0 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./hive.api";
4 changes: 4 additions & 0 deletions src/lib/components/ecency-renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
HivePostLinkExtension,
ImageZoomExtension,
TagLinkExtension,
WaveLikePostExtension,
YoutubeVideoExtension,
} from "./extensions";
import { ThreeSpeakVideoExtension } from "./extensions/three-speak-video-extension";
import { TwitterExtension } from "./extensions/twitter-extension";

interface Props {
value: string;
Expand Down Expand Up @@ -43,6 +45,8 @@ export function EcencyRenderer({
<TagLinkExtension containerRef={ref} />
<YoutubeVideoExtension containerRef={ref} />
<ThreeSpeakVideoExtension containerRef={ref} />
<WaveLikePostExtension containerRef={ref} />
<TwitterExtension containerRef={ref} />
</>
)}
</>
Expand Down
21 changes: 12 additions & 9 deletions src/lib/components/extensions/hive-post-link-extension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, { RefObject, useCallback, useEffect, useState } from "react";
import { hydrateRoot } from "react-dom/client";
import "./hive-post-link-extension.scss";
import { isWaveLikePost } from "../functions";

const simpleCache = new Map<
string,
Expand Down Expand Up @@ -105,15 +106,17 @@ export function HivePostLinkExtension({
".markdown-view:not(.markdown-view-pure) .markdown-post-link",
) ?? [],
);
elements.forEach((element) => {
const container = document.createElement("div");
container.classList.add("ecency-renderer-hive-post-extension");
hydrateRoot(
container,
<HivePostLinkRenderer link={element.getAttribute("href") ?? ""} />,
);
element.parentElement?.replaceChild(container, element);
});
elements
.filter((el) => !isWaveLikePost(el.getAttribute("href") ?? ""))
.forEach((element) => {
const container = document.createElement("div");
container.classList.add("ecency-renderer-hive-post-extension");
hydrateRoot(
container,
<HivePostLinkRenderer link={element.getAttribute("href") ?? ""} />,
);
element.parentElement?.replaceChild(container, element);
});
}, []);

return <></>;
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./hive-post-link-extension";
export * from "./author-link-extension";
export * from "./tag-link-extension";
export * from "./youtube-video-extension";
export * from "./wave-like-post-extension";
40 changes: 40 additions & 0 deletions src/lib/components/extensions/twitter-extension.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use client";

import React, { RefObject, useEffect } from "react";
import { hydrateRoot } from "react-dom/client";
import { TwitterTweetEmbed } from "react-twitter-embed";

export function TwitterExtension({
containerRef,
}: {
containerRef: RefObject<HTMLElement | null>;
}) {
useEffect(() => {
const elements = Array.from(
containerRef.current?.querySelectorAll<HTMLElement>(
".markdown-view:not(.markdown-view-pure) .markdown-external-link",
) ?? [],
);
elements
.filter(
(el) =>
el.getAttribute("href")?.startsWith("https://x.com") ||
el.getAttribute("href")?.startsWith("https://twitter.com"),
)
.forEach((element) => {
const container = document.createElement("div");
const [_, __, ___, tweetId] = URL.parse(
element.getAttribute("href")!,
)!.pathname.split("/");

container.classList.add("ecency-renderer-twitter-extension-frame");
element.classList.add("ecency-renderer-twitter-extension");

hydrateRoot(container, <TwitterTweetEmbed tweetId={tweetId} />);
element.innerHTML = "";
element.appendChild(container);
});
}, []);

return <></>;
}
82 changes: 82 additions & 0 deletions src/lib/components/extensions/wave-like-post-extension.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.ecency-renderer-wave-like-post-extension {
&-renderer {
border-radius: 1rem;
padding: 1rem;
border: 1px solid var(--border-color, #dee2e6);
display: flex;
flex-direction: column;
gap: 1rem;
position: relative;
text-decoration: none;
color: inherit;
margin: 1rem auto;
max-width: 600px;

&::after {
content: none !important;
}

&:hover {
background-color: rgba(#357ce6, 0.03);
}

&--logo {
position: absolute;
top: 1rem;
right: 1rem;
width: 2rem;
height: 2rem;

&::after {
content: none !important;
}

.ecency-logo-circle {
fill: rgba(#8d8d8d, 0.5);
}

.ecency-logo-sign {
fill: #fff;
}
}

&--author {
display: flex;
align-items: center;
gap: 0.5rem;

&-avatar {
width: 48px;
height: 48px;
border-radius: 24px;
}

&-content {
display: flex;
flex-direction: column;

&-host {
opacity: 0.5;
font-size: 0.875rem;
}

&-link {
color: #357ce6;
font-size: 1rem;
text-decoration: none;
font-weight: 500;

&:hover {
text-decoration: underline;
}
}

a {
&::after {
content: none !important;
}
}
}
}
}
}
106 changes: 106 additions & 0 deletions src/lib/components/extensions/wave-like-post-extension.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"use client";

import React, { RefObject, useEffect, useMemo, useState } from "react";
import { hydrateRoot } from "react-dom/client";
import { isWaveLikePost } from "../functions";
import { getCachedPost } from "../../api";
import { Entry } from "@ecency/render-helper/lib/types";
import "./wave-like-post-extension.scss";
import { EcencyRenderer } from "../ecency-renderer";
import { Logo } from "../icons";

export function WaveLikePostRenderer({ link }: { link: string }) {
const [post, setPost] = useState<Entry & { title: string }>();

const host = useMemo(() => {
if (
post?.permlink?.startsWith("re-ecencywaves") ||
post?.permlink?.startsWith("wave-")
) {
return "ecency.waves";
}

if (post?.permlink?.startsWith("re-leothreads")) {
return "threads";
}

return "";
}, [post]);

useEffect(() => {
const [_, __, ___, username, permlink] = new URL(
`https://ecency.com/${link}`,
).pathname.split("/");
getCachedPost(username.replace("@", ""), permlink)
.then((resp) => {
setPost(resp as any);
})
.catch((e) => console.error(e));
}, []);

return post ? (
<a
href={`/waves/${post.author}/${post.permlink}`}
target="_blank"
className="ecency-renderer-wave-like-post-extension-renderer"
>
<div className="ecency-renderer-wave-like-post-extension-renderer--author">
<img
src={`https://images.ecency.com/u/${post.author}/avatar/small`}
alt={post.author}
className="ecency-renderer-wave-like-post-extension-renderer--author-avatar"
/>
<div className="ecency-renderer-wave-like-post-extension-renderer--author-content">
<a
className="ecency-renderer-wave-like-post-extension-renderer--author-content-link"
href={`/@${post.author}/posts`}
target="_blank"
>
@{post.author}
</a>
<div className="ecency-renderer-wave-like-post-extension-renderer--author-content-host">
#{host}
</div>
</div>
<a
href="https://ecency.com"
target="_blank"
className="ecency-renderer-wave-like-post-extension-renderer--logo"
dangerouslySetInnerHTML={{ __html: Logo }}
/>
</div>
<div className="ecency-renderer-wave-like-post-extension-renderer--body">
<EcencyRenderer value={post.body} />
</div>
</a>
) : (
<></>
);
}

export function WaveLikePostExtension({
containerRef,
}: {
containerRef: RefObject<HTMLElement | null>;
}) {
useEffect(() => {
const elements = Array.from(
containerRef.current?.querySelectorAll<HTMLElement>(
".markdown-view:not(.markdown-view-pure) .markdown-post-link",
) ?? [],
);
elements
.filter((el) => isWaveLikePost(el.getAttribute("href") ?? ""))
.forEach((element) => {
const container = document.createElement("div");
container.classList.add("ecency-renderer-wave-like-extension");
hydrateRoot(
container,
<WaveLikePostRenderer link={element.getAttribute("href") ?? ""} />,
);
element.parentElement?.replaceChild(container, element);
});
}, []);

return <></>;
}
1 change: 1 addition & 0 deletions src/lib/components/functions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./is-wave-like-post";
9 changes: 9 additions & 0 deletions src/lib/components/functions/is-wave-like-post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function isWaveLikePost(link: string) {
const [_, __, ___, permlink] = link.split("/");
return (
permlink.includes("re-ecencywaves") ||
permlink.includes("re-leothreads") ||
permlink.startsWith("wave-") ||
permlink.startsWith("re-liketu-moments")
);
}
1 change: 1 addition & 0 deletions src/lib/components/icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const Logo = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 148.85 148.85"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><circle class="ecency-logo-circle" cx="74.43" cy="74.43" r="74.43"/><path class="ecency-logo-sign" d="M88.27,105.71c-9,.08-30.35.27-35.13-.29-3.88-.46-11-3-11.11-12.81C42,87,41.66,64,42.46,59,44.13,48.4,47,41.77,59.05,36.33c10.26-4.44,32.17-.78,34.54,16.93.45,3.37,1.25,3.74,2.49,4,19.61,4.13,24,26.26,14.6,38.32C104.73,103.26,98.31,104.76,88.27,105.71ZM84.71,59.25c.68-11.52-11-19.82-22.82-13.66-8.42,4.39-9.15,10.76-9.68,18-.67,9.2-.25,15.91-.09,25.13.07,4.13,1.27,6.64,5.7,7,1.14.1,17,0,25.22.06,10.74.06,24.06-4.89,21.93-18a12.68,12.68,0,0,0-10.8-10.22,2.12,2.12,0,0,0-2.21,1C85,83,69.66,82.31,63.41,74.46c-5.61-7.06-2.7-18.73,4.68-21.2,2.78-.94,5.11-.11,6.25,1.86,1.84,3.18.11,6.06-2.49,7.65s-2.45,3.92-1.36,5.46c2.56,3.59,7.6,2.88,10.79-.28C83.87,65.4,84.52,62.47,84.71,59.25Z"/></g></g></svg>`;
22 changes: 22 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@ import React from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import { App } from "./App";
import { Client } from "@hiveio/dhive";

const SERVERS = [
"https://api.hive.blog",
"https://api.openhive.network",
"https://hapi.ecency.com",
"https://rpc.mahdiyari.info",
"https://api.deathwing.me",
"https://api.syncad.com",
"https://anyx.io",
"https://api.c0ff33a.uk",
"https://hive-api.3speak.tv",
"https://techcoderx.com",
"https://hived.emre.sh",
"https://api.hive.blue",
];

(window as any).dHiveClient = new Client(SERVERS, {
timeout: 2000,
failoverThreshold: 2,
consoleOnFailover: true,
});

const container = document.getElementById("root");
if (container) {
Expand Down
Loading

0 comments on commit aea81b7

Please sign in to comment.