mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2026-04-29 21:12:37 +08:00
[questions][feat] add useProtectedCallback hook (#472)
This commit is contained in:
@@ -5,6 +5,7 @@ import { Fragment, useRef, useState } from 'react';
|
||||
import { Menu, Transition } from '@headlessui/react';
|
||||
import { CheckIcon, HeartIcon } from '@heroicons/react/20/solid';
|
||||
|
||||
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
|
||||
import { trpc } from '~/utils/trpc';
|
||||
|
||||
export type AddToListDropdownProps = {
|
||||
@@ -85,14 +86,16 @@ export default function AddToListDropdown({
|
||||
});
|
||||
};
|
||||
|
||||
const handleMenuButtonClick = useProtectedCallback(() => {
|
||||
addClickOutsideListener();
|
||||
setMenuOpened(!menuOpened);
|
||||
});
|
||||
|
||||
const CustomMenuButton = ({ children }: PropsWithChildren<unknown>) => (
|
||||
<button
|
||||
className="focus:ring-primary-500 inline-flex w-full justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-100"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
addClickOutsideListener();
|
||||
setMenuOpened(!menuOpened);
|
||||
}}>
|
||||
onClick={handleMenuButtonClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { TextInput } from '@tih/ui';
|
||||
|
||||
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
|
||||
|
||||
import ContributeQuestionDialog from './ContributeQuestionDialog';
|
||||
import type { ContributeQuestionFormProps } from './forms/ContributeQuestionForm';
|
||||
|
||||
@@ -23,9 +25,9 @@ export default function ContributeQuestionCard({
|
||||
setShowDraftDialog(false);
|
||||
};
|
||||
|
||||
const handleOpenContribute = () => {
|
||||
const handleOpenContribute = useProtectedCallback(() => {
|
||||
setShowDraftDialog(true);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { Vote } from '@prisma/client';
|
||||
import type { ButtonSize } from '@tih/ui';
|
||||
import { Button } from '@tih/ui';
|
||||
|
||||
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
|
||||
|
||||
export type BackendVote = {
|
||||
id: string;
|
||||
vote: Vote;
|
||||
@@ -31,6 +33,15 @@ export default function VotingButtons({
|
||||
vote?.vote === 'UPVOTE' ? 'secondary' : 'tertiary';
|
||||
const downvoteButtonVariant =
|
||||
vote?.vote === 'DOWNVOTE' ? 'secondary' : 'tertiary';
|
||||
|
||||
const handleUpvoteClick = useProtectedCallback(() => {
|
||||
onUpvote();
|
||||
});
|
||||
|
||||
const handleDownvoteClick = useProtectedCallback(() => {
|
||||
onDownvote();
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<Button
|
||||
@@ -42,7 +53,7 @@ export default function VotingButtons({
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onUpvote();
|
||||
handleUpvoteClick();
|
||||
}}
|
||||
/>
|
||||
<p>{upvoteCount}</p>
|
||||
@@ -55,7 +66,7 @@ export default function VotingButtons({
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onDownvote();
|
||||
handleDownvoteClick();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import type { QuestionsQuestionType } from '@prisma/client';
|
||||
import { Button } from '@tih/ui';
|
||||
|
||||
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
|
||||
import { useQuestionVote } from '~/utils/questions/useVote';
|
||||
|
||||
import AddToListDropdown from '../../AddToListDropdown';
|
||||
@@ -168,6 +169,10 @@ export default function BaseQuestionCard({
|
||||
return countryCount;
|
||||
}, [countries]);
|
||||
|
||||
const handleCreateEncounterClick = useProtectedCallback(() => {
|
||||
setShowReceivedForm(true);
|
||||
});
|
||||
|
||||
const cardContent = (
|
||||
<>
|
||||
{showVoteButtons && (
|
||||
@@ -244,10 +249,7 @@ export default function BaseQuestionCard({
|
||||
label={createEncounterButtonText}
|
||||
size="sm"
|
||||
variant="tertiary"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
setShowReceivedForm(true);
|
||||
}}
|
||||
onClick={handleCreateEncounterClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createContext, useState } from 'react';
|
||||
|
||||
import ProtectedDialog from './ProtectedDialog';
|
||||
|
||||
export type ProtectedContextData = {
|
||||
showDialog: () => void;
|
||||
};
|
||||
|
||||
export const ProtectedContext = createContext<ProtectedContextData>({
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
showDialog: () => {},
|
||||
});
|
||||
|
||||
export type ProtectedContextProviderProps = PropsWithChildren<
|
||||
Record<string, unknown>
|
||||
>;
|
||||
|
||||
export default function ProtectedContextProvider({
|
||||
children,
|
||||
}: ProtectedContextProviderProps) {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<ProtectedContext.Provider
|
||||
value={{
|
||||
showDialog: () => {
|
||||
setShow(true);
|
||||
},
|
||||
}}>
|
||||
{children}
|
||||
<ProtectedDialog
|
||||
show={show}
|
||||
onClose={() => {
|
||||
setShow(false);
|
||||
}}
|
||||
/>
|
||||
</ProtectedContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { Button, Dialog } from '@tih/ui';
|
||||
|
||||
export type ProtectedDialogProps = {
|
||||
onClose: () => void;
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
export default function ProtectedDialog({
|
||||
show,
|
||||
onClose,
|
||||
}: ProtectedDialogProps) {
|
||||
const handlePrimaryClick = () => {
|
||||
signIn();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isShown={show}
|
||||
primaryButton={
|
||||
<Button
|
||||
label="Sign in"
|
||||
variant="primary"
|
||||
onClick={handlePrimaryClick}
|
||||
/>
|
||||
}
|
||||
secondaryButton={
|
||||
<Button label="Cancel" variant="tertiary" onClick={onClose} />
|
||||
}
|
||||
title="Sign in to continue"
|
||||
onClose={onClose}>
|
||||
<p>This action requires you to be signed in.</p>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user