Allow embedding of roadmaps

pull/3460/head
Kamran Ahmed 1 year ago
parent 9d05c64f50
commit b35a169315
  1. 26
      src/components/CustomRoadmap/CustomRoadmap.tsx
  2. 15
      src/components/CustomRoadmap/FlowRoadmapRenderer.tsx
  3. 27
      src/components/TopicDetail/TopicDetail.tsx
  4. 29
      src/pages/r/embed.astro

@ -53,14 +53,20 @@ export type GetRoadmapResponse = RoadmapDocument & {
export function hideRoadmapLoader() { export function hideRoadmapLoader() {
const loaderEl = document.querySelector( const loaderEl = document.querySelector(
'[data-roadmap-loader]' '[data-roadmap-loader]',
) as HTMLElement; ) as HTMLElement;
if (loaderEl) { if (loaderEl) {
loaderEl.remove(); loaderEl.remove();
} }
} }
export function CustomRoadmap() { type CustomRoadmapProps = {
isEmbed?: boolean;
};
export function CustomRoadmap(props: CustomRoadmapProps) {
const { isEmbed = false } = props;
const { id, secret } = getUrlParams() as { id: string; secret: string }; const { id, secret } = getUrlParams() as { id: string; secret: string };
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@ -71,14 +77,15 @@ export function CustomRoadmap() {
setIsLoading(true); setIsLoading(true);
const roadmapUrl = new URL( const roadmapUrl = new URL(
`${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}` `${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${id}`,
); );
if (secret) { if (secret) {
roadmapUrl.searchParams.set('secret', secret); roadmapUrl.searchParams.set('secret', secret);
} }
const { response, error } = await httpGet<GetRoadmapResponse>( const { response, error } = await httpGet<GetRoadmapResponse>(
roadmapUrl.toString() roadmapUrl.toString(),
); );
if (error || !response) { if (error || !response) {
@ -95,7 +102,10 @@ export function CustomRoadmap() {
} }
async function trackVisit() { async function trackVisit() {
if (!isLoggedIn()) return; if (!isLoggedIn() || isEmbed) {
return;
}
await httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-visit`, { await httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-visit`, {
resourceId: id, resourceId: id,
resourceType: 'roadmap', resourceType: 'roadmap',
@ -119,9 +129,9 @@ export function CustomRoadmap() {
return ( return (
<> <>
<RoadmapHeader /> {!isEmbed && <RoadmapHeader />}
<FlowRoadmapRenderer roadmap={roadmap!} /> <FlowRoadmapRenderer isEmbed={isEmbed} roadmap={roadmap!} />
<TopicDetail canSubmitContribution={false} /> <TopicDetail isEmbed={isEmbed} canSubmitContribution={false} />
</> </>
); );
} }

@ -1,26 +1,27 @@
import { ReadonlyEditor } from '../../../editor/readonly-editor'; import { ReadonlyEditor } from '../../../editor/readonly-editor';
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal'; import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
import { import {
refreshProgressCounters,
renderResourceProgress, renderResourceProgress,
updateResourceProgress,
type ResourceProgressType,
renderTopicProgress, renderTopicProgress,
refreshProgressCounters, type ResourceProgressType,
updateResourceProgress,
} from '../../lib/resource-progress'; } from '../../lib/resource-progress';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import type { Node } from 'reactflow'; import type { Node } from 'reactflow';
import { useCallback, type MouseEvent, useMemo, useState, useRef } from 'react'; import { type MouseEvent, useCallback, useRef, useState } from 'react';
import { EmptyRoadmap } from './EmptyRoadmap'; import { EmptyRoadmap } from './EmptyRoadmap';
import { cn } from '../../lib/classname'; import { cn } from '../../lib/classname';
import { totalRoadmapNodes } from '../../stores/roadmap.ts'; import { totalRoadmapNodes } from '../../stores/roadmap.ts';
type FlowRoadmapRendererProps = { type FlowRoadmapRendererProps = {
isEmbed?: boolean;
roadmap: RoadmapDocument; roadmap: RoadmapDocument;
}; };
export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) { export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
const { roadmap } = props; const { roadmap, isEmbed = false } = props;
const roadmapId = String(roadmap._id!); const roadmapId = String(roadmap._id!);
const [hideRenderer, setHideRenderer] = useState(false); const [hideRenderer, setHideRenderer] = useState(false);
@ -32,6 +33,10 @@ export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
topicId: string, topicId: string,
newStatus: ResourceProgressType, newStatus: ResourceProgressType,
) { ) {
if (isEmbed) {
return;
}
pageProgressMessage.set('Updating progress'); pageProgressMessage.set('Updating progress');
updateResourceProgress( updateResourceProgress(
{ {

@ -29,6 +29,7 @@ import { Spinner } from '../ReactIcons/Spinner';
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx'; import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
type TopicDetailProps = { type TopicDetailProps = {
isEmbed?: boolean;
canSubmitContribution: boolean; canSubmitContribution: boolean;
}; };
@ -42,7 +43,7 @@ const linkTypes: Record<AllowedLinkTypes, string> = {
}; };
export function TopicDetail(props: TopicDetailProps) { export function TopicDetail(props: TopicDetailProps) {
const { canSubmitContribution } = props; const { canSubmitContribution, isEmbed = false } = props;
const [hasEnoughLinks, setHasEnoughLinks] = useState(false); const [hasEnoughLinks, setHasEnoughLinks] = useState(false);
const [contributionUrl, setContributionUrl] = useState(''); const [contributionUrl, setContributionUrl] = useState('');
@ -163,9 +164,9 @@ export function TopicDetail(props: TopicDetailProps) {
); );
const links = topicDom.querySelectorAll('a'); const links = topicDom.querySelectorAll('a');
const contributionUrl = const urlElem: HTMLElement =
topicDom.querySelector('[data-github-url]')?.dataset?.githubUrl || topicDom.querySelector('[data-github-url]')!;
''; const contributionUrl = urlElem?.dataset?.githubUrl || '';
setContributionUrl(contributionUrl); setContributionUrl(contributionUrl);
setHasEnoughLinks(links.length >= 3); setHasEnoughLinks(links.length >= 3);
@ -218,14 +219,16 @@ export function TopicDetail(props: TopicDetailProps) {
<> <>
{/* Actions for the topic */} {/* Actions for the topic */}
<div className="mb-2"> <div className="mb-2">
<TopicProgressButton {!isEmbed && (
topicId={topicId} <TopicProgressButton
resourceId={resourceId} topicId={topicId}
resourceType={resourceType} resourceId={resourceId}
onClose={() => { resourceType={resourceType}
setIsActive(false); onClose={() => {
}} setIsActive(false);
/> }}
/>
)}
<button <button
type="button" type="button"

@ -0,0 +1,29 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import { CustomRoadmap } from '../../components/CustomRoadmap/CustomRoadmap';
import { SkeletonRoadmapHeader } from '../../components/CustomRoadmap/SkeletonRoadmapHeader';
import Loader from '../../components/Loader.astro';
import ProgressHelpPopup from '../../components/ProgressHelpPopup.astro';
import SkeletonLayout from '../../layouts/SkeletonLayout.astro';
---
<SkeletonLayout title='Roadmaps' noIndex={true}>
<div class='relative flex min-h-[550px] flex-col'>
<div data-roadmap-loader class='flex w-full grow flex-col'>
<div class='flex grow items-center justify-center'>
<Loader />
</div>
</div>
<CustomRoadmap isEmbed={true} client:only='react' />
<div class='fixed bottom-5 right-4'>
<a
target='_blank'
class='rounded-md bg-gray-600 p-2 text-white hover:bg-black'
href='https://roadmap.sh'
>
roadmap.sh
</a>
</div>
</div>
</SkeletonLayout>
Loading…
Cancel
Save