Skip to content

Commit

Permalink
feat: allow copying link to questions and answers
Browse files Browse the repository at this point in the history
closes #104
  • Loading branch information
drodil committed Jan 8, 2024
1 parent 5e0248f commit 93b21f2
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class QetaCollatorFactory implements DocumentCollatorFactory {
yield {
title: `Answer for ${question.title}`,
text: answer.content,
location: `/qeta/questions/${question.id}`,
location: `/qeta/questions/${question.id}#answer_${answer.id}`,
docType: 'qeta',
author: answer.author,
score: answer.score,
Expand Down
10 changes: 8 additions & 2 deletions plugins/qeta/src/components/QuestionPage/AnswerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DeleteModal } from '../DeleteModal/DeleteModal';
import { AnswerForm } from './AnswerForm';
import { AuthorBox } from './AuthorBox';
import { CommentSection } from '../CommentSection/CommentSection';
import { LinkButton } from './LinkButton';

export const AnswerCard = (props: {
answer: AnswerResponse;
Expand All @@ -22,6 +23,8 @@ export const AnswerCard = (props: {
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
const handleDeleteModalOpen = () => setDeleteModalOpen(true);
const handleDeleteModalClose = () => setDeleteModalOpen(false);
const highlightedAnswer =
window.location.hash.slice(1) === `answer_${answer.id}`;

const onAnswerEdit = (a: AnswerResponse) => {
setEditMode(false);
Expand All @@ -37,12 +40,15 @@ export const AnswerCard = (props: {
return (
<>
<Card
id={`a${answer.id}`}
className={`qetaAnswerCard ${styles.questionCard}`}
id={`answer_${answer.id}`}
className={`qetaAnswerCard ${styles.questionCard} ${
highlightedAnswer ? styles.highlight : ''
}`}
>
<CardContent>
<div className={styles.questionCardVote}>
<VoteButtons entity={answerEntity} question={question} />
<LinkButton entity={answerEntity} />
</div>
<div className={styles.questionCardContent}>
{editMode ? (
Expand Down
33 changes: 33 additions & 0 deletions plugins/qeta/src/components/QuestionPage/LinkButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IconButton, Tooltip } from '@material-ui/core';
import Link from '@material-ui/icons/Link';
import React from 'react';
import { AnswerResponse, QuestionResponse } from '../../api';

export const LinkButton = (props: {
entity: QuestionResponse | AnswerResponse;
}) => {
const isQuestion = 'title' in props.entity;
const copyToClipboard = () => {
const url = new URL(window.location.href);
if (!isQuestion) {
url.hash = `#answer_${props.entity.id}`;
}
window.navigator.clipboard.writeText(url.toString());
};

return (
<Tooltip
title={`Copy link to this ${
isQuestion ? 'question' : 'answer'
} to clipboard`}
>
<IconButton
aria-label="copy link to clipboard"
size="small"
onClick={copyToClipboard}
>
<Link />
</IconButton>
</Tooltip>
);
};
14 changes: 13 additions & 1 deletion plugins/qeta/src/components/QuestionPage/QuestionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnswerResponse, QuestionResponse } from '../../api';
import { Box, Card, CardContent, Grid, Typography } from '@material-ui/core';
import { Link, MarkdownContent } from '@backstage/core-components';
import React from 'react';
import React, { useEffect } from 'react';
import { VoteButtons } from './VoteButtons';
import { useStyles } from '../../utils/hooks';
import { DeleteModal } from '../DeleteModal/DeleteModal';
Expand All @@ -11,6 +11,7 @@ import { TagsAndEntities } from './TagsAndEntities';
import { CommentSection } from '../CommentSection/CommentSection';
import { useRouteRef } from '@backstage/core-plugin-api';
import { editQuestionRouteRef } from '../../routes';
import { LinkButton } from './LinkButton';

export const QuestionCard = (props: { question: QuestionResponse }) => {
const { question } = props;
Expand All @@ -24,6 +25,16 @@ export const QuestionCard = (props: { question: QuestionResponse }) => {
setQuestionEntity(q);
};

const highlightedAnswer = window.location.hash.slice(1) ?? undefined;
useEffect(() => {
if (highlightedAnswer) {
const element = document.querySelector(`#${highlightedAnswer}`);
if (element) {
element.scrollIntoView();
}
}
}, [highlightedAnswer]);

return (
<>
<Card
Expand All @@ -34,6 +45,7 @@ export const QuestionCard = (props: { question: QuestionResponse }) => {
<div className={styles.questionCardVote}>
<VoteButtons entity={questionEntity} />
<FavoriteButton entity={questionEntity} />
<LinkButton entity={questionEntity} />
</div>
<div className={styles.questionCardContent}>
<Typography variant="body1" gutterBottom>
Expand Down
4 changes: 2 additions & 2 deletions plugins/qeta/src/components/QuestionPage/QuestionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ export const QuestionPage = () => {
</Box>
{allQuestions.sort(sortAnswers).map(a => {
return (
<>
<React.Fragment key={a.id}>
<Divider className={styles.questionDivider} />
<Box key={a.id} sx={{ mb: 1 }}>
<AnswerCard answer={a} question={question} />
</Box>
</>
</React.Fragment>
);
})}
<Divider className={styles.questionDivider} />
Expand Down
11 changes: 11 additions & 0 deletions plugins/qeta/src/utils/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ export const useStyles = makeStyles(theme => {
overflow: 'hidden',
whiteSpace: 'nowrap',
},
highlight: {
animation: '$highlight 2s',
},
'@keyframes highlight': {
'0%': {
boxShadow: `0px 0px 0px 3px ${theme.palette.secondary.light}`,
},
'100%': {
boxShadow: 'none',
},
},
};
});

Expand Down

0 comments on commit 93b21f2

Please sign in to comment.