Refactor progress button

pull/3919/head
Kamran Ahmed 2 years ago
parent a3470cd844
commit f338bd5ecb
  1. 127
      src/components/TopicDetail/TopicDetail.tsx
  2. 138
      src/components/TopicDetail/TopicProgressButton.tsx

@ -1,8 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { useMemo, useRef, useState } from 'preact/hooks';
import CheckIcon from '../../icons/check.svg';
import CloseIcon from '../../icons/close.svg'; import CloseIcon from '../../icons/close.svg';
import ProgressIcon from '../../icons/progress.svg';
import ResetIcon from '../../icons/reset.svg';
import SpinnerIcon from '../../icons/spinner.svg'; import SpinnerIcon from '../../icons/spinner.svg';
import { useKeydown } from '../../hooks/use-keydown'; import { useKeydown } from '../../hooks/use-keydown';
@ -12,14 +9,13 @@ import { useToggleTopic } from '../../hooks/use-toggle-topic';
import { httpGet } from '../../lib/http'; import { httpGet } from '../../lib/http';
import { isLoggedIn } from '../../lib/jwt'; import { isLoggedIn } from '../../lib/jwt';
import { import {
getTopicStatus,
isTopicDone, isTopicDone,
renderTopicProgress, renderTopicProgress,
ResourceProgressType,
ResourceType, ResourceType,
updateResourceProgress as updateResourceProgressApi, updateResourceProgress as updateResourceProgressApi,
} from '../../lib/resource-progress'; } from '../../lib/resource-progress';
import { pageLoadingMessage, sponsorHidden } from '../../stores/page'; import { pageLoadingMessage, sponsorHidden } from '../../stores/page';
import { TopicProgressButton } from './TopicProgressButton';
export function TopicDetail() { export function TopicDetail() {
const [isActive, setIsActive] = useState(false); const [isActive, setIsActive] = useState(false);
@ -27,9 +23,6 @@ export function TopicDetail() {
const [error, setError] = useState(''); const [error, setError] = useState('');
const [topicHtml, setTopicHtml] = useState(''); const [topicHtml, setTopicHtml] = useState('');
const [progress, setProgress] = useState<ResourceProgressType>('pending');
const [isUpdatingProgress, setIsUpdatingProgress] = useState(true);
const isGuest = useMemo(() => !isLoggedIn(), []); const isGuest = useMemo(() => !isLoggedIn(), []);
const topicRef = useRef<HTMLDivElement>(null); const topicRef = useRef<HTMLDivElement>(null);
@ -52,45 +45,6 @@ export function TopicDetail() {
} }
}; };
const handleUpdateResourceProgress = (progress: ResourceProgressType) => {
setIsUpdatingProgress(true);
updateResourceProgressApi(
{
topicId,
resourceId,
resourceType,
},
progress
)
.then(() => {
setProgress(progress);
setIsActive(false);
renderTopicProgress(topicId, progress);
})
.catch((err) => {
alert(err.message);
console.error(err);
})
.finally(() => {
setIsUpdatingProgress(false);
});
};
// Load the topic status when the topic detail is active
useEffect(() => {
if (!topicId || !resourceId || !resourceType) {
return;
}
setIsUpdatingProgress(true);
getTopicStatus({ topicId, resourceId, resourceType })
.then((status) => {
setIsUpdatingProgress(false);
setProgress(status);
})
.catch(console.error);
}, [topicId, resourceId, resourceType]);
// Close the topic detail when user clicks outside the topic detail // Close the topic detail when user clicks outside the topic detail
useOutsideClick(topicRef, () => { useOutsideClick(topicRef, () => {
setIsActive(false); setIsActive(false);
@ -191,10 +145,6 @@ export function TopicDetail() {
resourceType === 'roadmap' ? 'roadmaps' : 'best-practices'; resourceType === 'roadmap' ? 'roadmaps' : 'best-practices';
const contributionUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/${contributionDir}/${resourceId}/content`; const contributionUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/${contributionDir}/${resourceId}/content`;
const allowMarkingDone = ['pending', 'learning'].includes(progress);
const allowMarkingLearning = ['pending'].includes(progress);
const allowMarkingPending = ['done', 'learning'].includes(progress);
return ( return (
<div> <div>
<div <div
@ -215,71 +165,14 @@ export function TopicDetail() {
<> <>
{/* Actions for the topic */} {/* Actions for the topic */}
<div className="mb-2"> <div className="mb-2">
{isGuest && ( <TopicProgressButton
<div className="flex items-center gap-2"> topicId={topicId}
<button resourceId={resourceId}
data-popup="login-popup" resourceType={resourceType}
className="inline-flex items-center rounded-md bg-green-600 p-1 px-2 text-sm text-white hover:bg-green-700" onClose={() => {
onClick={() => setIsActive(false)} setIsActive(false);
> }}
<img alt="Check" class="w-3" src={CheckIcon} /> />
<span className="ml-2">Mark as Done</span>
</button>
<button
data-popup="login-popup"
className="inline-flex items-center rounded-md bg-[#dad1fd] p-1 px-2 text-sm text-[#0E033B] hover:bg-[#C4B6FC]"
onClick={() => setIsActive(false)}
>
<img alt="Learning" class="w-4" src={ProgressIcon} />
<span className="ml-2">In Progress</span>
</button>
</div>
)}
{!isGuest && (
<div class="flex items-center gap-2 rounded-md">
{isUpdatingProgress && (
<button className="inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white p-1 px-2 text-sm text-black">
<img
alt="Check"
class="h-4 w-4 animate-spin"
src={SpinnerIcon}
/>
<span className="ml-2">Updating Status..</span>
</button>
)}
{!isUpdatingProgress && allowMarkingDone && (
<button
className="inline-flex items-center rounded-md border border-green-600 bg-green-600 p-1 px-2 text-sm text-white hover:bg-green-700"
onClick={() => handleUpdateResourceProgress('done')}
>
<img alt="Check" class="w-3" src={CheckIcon} />
<span className="ml-2">Mark as Done</span>
</button>
)}
{!isUpdatingProgress && allowMarkingLearning && (
<button
className="inline-flex items-center rounded-md bg-[#dad1fd] p-1 px-2 text-sm text-[#0E033B] hover:bg-[#C4B6FC]"
onClick={() => handleUpdateResourceProgress('learning')}
>
<img alt="Learning" className="w-4" src={ProgressIcon} />
<span className="ml-2">In Progress</span>
</button>
)}
{!isUpdatingProgress && allowMarkingPending && (
<button
className="inline-flex items-center rounded-md border border-red-600 bg-red-600 p-1 px-2 text-sm text-white hover:bg-red-700"
onClick={() => handleUpdateResourceProgress('pending')}
>
<img alt="Check" class="h-4" src={ResetIcon} />
<span className="ml-2">Mark as Pending</span>
</button>
)}
</div>
)}
<button <button
type="button" type="button"

@ -0,0 +1,138 @@
import { useEffect, useMemo, useState } from 'preact/hooks';
import CheckIcon from '../../icons/check.svg';
import ProgressIcon from '../../icons/progress.svg';
import ResetIcon from '../../icons/reset.svg';
import SpinnerIcon from '../../icons/spinner.svg';
import { isLoggedIn } from '../../lib/jwt';
import {
ResourceProgressType,
ResourceType,
getTopicStatus,
renderTopicProgress,
updateResourceProgress,
} from '../../lib/resource-progress';
type TopicProgressButtonProps = {
topicId: string;
resourceId: string;
resourceType: ResourceType;
onClose: () => void;
};
export function TopicProgressButton(props: TopicProgressButtonProps) {
const { topicId, resourceId, resourceType, onClose } = props;
const [isUpdatingProgress, setIsUpdatingProgress] = useState(true);
const [progress, setProgress] = useState<ResourceProgressType>('pending');
const isGuest = useMemo(() => !isLoggedIn(), []);
useEffect(() => {
if (!topicId || !resourceId || !resourceType) {
return;
}
setIsUpdatingProgress(true);
getTopicStatus({ topicId, resourceId, resourceType })
.then((status) => {
setIsUpdatingProgress(false);
setProgress(status);
})
.catch(console.error);
}, [topicId, resourceId, resourceType]);
const handleUpdateResourceProgress = (progress: ResourceProgressType) => {
setIsUpdatingProgress(true);
updateResourceProgress(
{
topicId,
resourceId,
resourceType,
},
progress
)
.then(() => {
setProgress(progress);
onClose();
renderTopicProgress(topicId, progress);
})
.catch((err) => {
alert(err.message);
console.error(err);
})
.finally(() => {
setIsUpdatingProgress(false);
});
};
const allowMarkingDone = ['pending', 'learning'].includes(progress);
const allowMarkingLearning = ['pending'].includes(progress);
const allowMarkingPending = ['done', 'learning'].includes(progress);
if (isUpdatingProgress) {
return (
<button className="inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white p-1 px-2 text-sm text-black">
<img alt="Check" class="h-4 w-4 animate-spin" src={SpinnerIcon} />
<span className="ml-2">Updating Status..</span>
</button>
);
}
if (isGuest) {
return (
<div className="flex items-center gap-2">
<button
data-popup="login-popup"
className="inline-flex items-center rounded-md bg-green-600 p-1 px-2 text-sm text-white hover:bg-green-700"
onClick={onClose}
>
<img alt="Check" class="w-3" src={CheckIcon} />
<span className="ml-2">Mark as Done</span>
</button>
<button
data-popup="login-popup"
className="inline-flex items-center rounded-md bg-[#dad1fd] p-1 px-2 text-sm text-[#0E033B] hover:bg-[#C4B6FC]"
onClick={onClose}
>
<img alt="Learning" class="w-4" src={ProgressIcon} />
<span className="ml-2">In Progress</span>
</button>
</div>
);
}
return (
<div class="flex items-center gap-2 rounded-md">
{allowMarkingDone && (
<button
className="inline-flex items-center rounded-md border border-green-600 bg-green-600 p-1 px-2 text-sm text-white hover:bg-green-700"
onClick={() => handleUpdateResourceProgress('done')}
>
<img alt="Check" class="w-3" src={CheckIcon} />
<span className="ml-1">Done</span>
</button>
)}
{allowMarkingLearning && (
<button
className="inline-flex items-center rounded-md bg-[#dad1fd] p-1 px-2 text-sm text-[#0E033B] hover:bg-[#C4B6FC]"
onClick={() => handleUpdateResourceProgress('learning')}
>
<img alt="Learning" className="w-4" src={ProgressIcon} />
<span className="ml-1">Doing</span>
</button>
)}
{allowMarkingPending && (
<button
className="inline-flex items-center rounded-md border border-red-600 bg-red-600 p-1 px-2 text-sm text-white hover:bg-red-700"
onClick={() => handleUpdateResourceProgress('pending')}
>
<img alt="Check" class="h-4" src={ResetIcon} />
<span className="ml-2">Pending</span>
</button>
)}
</div>
);
}
Loading…
Cancel
Save