From d6a28a312a8d3cd560fe9b382bcfa97ca19498d0 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Fri, 2 Jun 2023 21:23:26 +0100 Subject: [PATCH] Add contribution functionality --- .../TopicDetail/ContributionForm.tsx | 226 ++++++++++++++++++ src/components/TopicDetail/TopicDetail.tsx | 69 ++++-- 2 files changed, 275 insertions(+), 20 deletions(-) create mode 100644 src/components/TopicDetail/ContributionForm.tsx diff --git a/src/components/TopicDetail/ContributionForm.tsx b/src/components/TopicDetail/ContributionForm.tsx new file mode 100644 index 000000000..5696e7938 --- /dev/null +++ b/src/components/TopicDetail/ContributionForm.tsx @@ -0,0 +1,226 @@ +import { useEffect, useRef, useState } from 'preact/hooks'; +import { httpPost } from '../../lib/http'; + +type ContributionInputProps = { + id: number; + title: string; + link: string; + isLast: boolean; + totalCount: number; + onAdd: () => void; + onRemove: () => void; + onChange: (link: { id: number; title: string; link: string }) => void; +}; + +function ContributionInput(props: ContributionInputProps) { + const { + isLast, + totalCount, + onAdd, + onRemove, + onChange, + id, + title: defaultTitle, + link: defaultLink, + } = props; + const titleRef = useRef(null); + const [focused, setFocused] = useState(''); + const [title, setTitle] = useState(defaultTitle); + const [link, setLink] = useState(defaultLink); + + useEffect(() => { + if (!titleRef?.current) { + return; + } + + titleRef.current.focus(); + }, []); + + useEffect(() => { + onChange({ id, title, link }); + }, [title, link]); + + const canAddMore = isLast && totalCount < 5; + + return ( +
+

+ Resource Title +

+ setFocused('title')} + onBlur={() => setFocused('')} + onChange={(e) => setTitle((e.target as any).value)} + /> +

+ Resource Link +

+ setFocused('link')} + onBlur={() => setFocused('')} + onChange={(e) => setLink((e.target as any).value)} + /> + +
+ {totalCount !== 1 && ( + + )} + + {canAddMore && ( + + )} +
+
+ ); +} + +type ContributionFormProps = { + resourceType: string; + resourceId: string; + topicId: string; + onClose: (message?: string) => void; +}; + +export function ContributionForm(props: ContributionFormProps) { + const { onClose, resourceType, resourceId, topicId } = props; + const [isSubmitting, setIsSubmitting] = useState(false); + const [links, setLinks] = useState< + { id: number; title: string; link: string }[] + >([ + { + id: new Date().getTime(), + title: '', + link: '', + }, + ]); + + async function onSubmit(e: any) { + e.preventDefault(); + setIsSubmitting(true); + + const { response, error } = await httpPost( + `${import.meta.env.PUBLIC_API_URL}/v1-contribute-link`, + { + resourceType, + resourceId, + topicId, + links, + } + ); + + setIsSubmitting(false); + + if (!response || error) { + alert(error?.message || 'Something went wrong. Please try again.'); + return; + } + + onClose('Thanks for your contribution! We will review it shortly.'); + } + + return ( +
+
+

Guidelines

+
    +
  • Content should only be in English
  • +
  • Do not add things you have not evaluated personally.
  • +
  • It should strictly be relevant to the topic.
  • +
  • It should not be paid or behind a signup.
  • +
  • + Quality over quantity. Smaller set of quality links is preferred. +
  • +
+
+ +
+ {links.map((link, counter) => ( + { + setLinks( + links.map((l) => { + if (l.id === link.id) { + return newLink; + } + + return l; + }) + ); + }} + onRemove={() => { + setLinks(links.filter((l) => l.id !== link.id)); + }} + onAdd={() => { + setLinks([ + ...links, + { + id: new Date().getTime(), + title: '', + link: '', + }, + ]); + }} + /> + ))} + +
+ + +
+ +
+ ); +} diff --git a/src/components/TopicDetail/TopicDetail.tsx b/src/components/TopicDetail/TopicDetail.tsx index 450815fb5..0abfe5a42 100644 --- a/src/components/TopicDetail/TopicDetail.tsx +++ b/src/components/TopicDetail/TopicDetail.tsx @@ -16,10 +16,13 @@ import { } from '../../lib/resource-progress'; import { pageProgressMessage, sponsorHidden } from '../../stores/page'; import { TopicProgressButton } from './TopicProgressButton'; +import { ContributionForm } from './ContributionForm'; export function TopicDetail() { + const [contributionAlertMessage, setContributionAlertMessage] = useState(''); const [isActive, setIsActive] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [isContributing, setIsContributing] = useState(false); const [error, setError] = useState(''); const [topicHtml, setTopicHtml] = useState(''); @@ -45,14 +48,15 @@ export function TopicDetail() { } }; - // Close the topic detail when user clicks outside the topic detail useOutsideClick(topicRef, () => { setIsActive(false); + setIsContributing(false); }); useKeydown('Escape', () => { setIsActive(false); + setIsContributing(false); }); // Toggle topic is available even if the component UI is not active @@ -99,6 +103,7 @@ export function TopicDetail() { setIsActive(true); sponsorHidden.set(true); + setContributionAlertMessage(''); setTopicId(topicId); setResourceType(resourceType); setResourceId(resourceId); @@ -142,10 +147,6 @@ export function TopicDetail() { return null; } - const contributionDir = - resourceType === 'roadmap' ? 'roadmaps' : 'best-practices'; - const contributionUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/${contributionDir}/${resourceId}/content`; - return (
)} - {!isLoading && !error && ( + {!isLoading && isContributing && ( + { + if (message) { + setContributionAlertMessage(message); + } + + setIsContributing(false); + }} + /> + )} + + {!isContributing && !isLoading && !error && ( <> {/* Actions for the topic */}
@@ -173,6 +189,7 @@ export function TopicDetail() { onShowLoginPopup={showLoginPopup} onClose={() => { setIsActive(false); + setIsContributing(false); }} /> @@ -180,7 +197,10 @@ export function TopicDetail() { type="button" id="close-topic" className="absolute right-2.5 top-2.5 inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900" - onClick={() => setIsActive(false)} + onClick={() => { + setIsActive(false); + setIsContributing(false); + }} > Close @@ -193,20 +213,29 @@ export function TopicDetail() { dangerouslySetInnerHTML={{ __html: topicHtml }} >
-

- Contribute links to learning resources about this topic{' '} - +

+ Contribute links to learning resources about this topic{' '} +

+ +
)}