refactor(slugTitle): Switch to use the slug title returned by the server
This commit is contained in:
@@ -54,6 +54,7 @@ export interface TagInfo extends TagBase {
|
||||
}
|
||||
export interface QuestionParams {
|
||||
title: string;
|
||||
url_title?: string;
|
||||
content: string;
|
||||
html: string;
|
||||
tags: Tag[];
|
||||
@@ -396,6 +397,7 @@ export interface SearchParams {
|
||||
export interface SearchResItem {
|
||||
object_type: string;
|
||||
object: {
|
||||
url_title?: string;
|
||||
id: string;
|
||||
question_id?: string;
|
||||
title: string;
|
||||
@@ -455,6 +457,7 @@ export interface TimelineItem {
|
||||
|
||||
export interface TimelineObject {
|
||||
title: string;
|
||||
url_title?: string;
|
||||
object_type: string;
|
||||
question_id: string;
|
||||
answer_id: string;
|
||||
@@ -470,6 +473,7 @@ export interface TimelineRes {
|
||||
export interface ReviewItem {
|
||||
type: 'question' | 'answer' | 'tag';
|
||||
info: {
|
||||
url_title?: string;
|
||||
object_id: string;
|
||||
title: string;
|
||||
content: string;
|
||||
|
||||
@@ -30,7 +30,7 @@ const HotQuestions: FC = () => {
|
||||
<ListGroupItem
|
||||
key={li.id}
|
||||
as={Link}
|
||||
to={pathFactory.questionLanding(li.id, li.title)}
|
||||
to={pathFactory.questionLanding(li.id, li.url_title)}
|
||||
action>
|
||||
<div className="link-dark">{li.title}</div>
|
||||
{li.answer_count > 0 ? (
|
||||
|
||||
@@ -19,6 +19,7 @@ interface IProps {
|
||||
qid: string;
|
||||
aid?: string;
|
||||
title: string;
|
||||
slugTitle: string;
|
||||
hasAnswer?: boolean;
|
||||
isAccepted: boolean;
|
||||
callback: (type: string) => void;
|
||||
@@ -29,6 +30,7 @@ const Index: FC<IProps> = ({
|
||||
qid,
|
||||
aid = '',
|
||||
title,
|
||||
slugTitle,
|
||||
isAccepted = false,
|
||||
hasAnswer = false,
|
||||
memberActions = [],
|
||||
@@ -159,7 +161,13 @@ const Index: FC<IProps> = ({
|
||||
|
||||
return (
|
||||
<div className="d-flex align-items-center">
|
||||
<Share type={type} qid={qid} aid={aid} title={title} />
|
||||
<Share
|
||||
type={type}
|
||||
qid={qid}
|
||||
aid={aid}
|
||||
title={title}
|
||||
slugTitle={slugTitle}
|
||||
/>
|
||||
{memberActions?.map((item) => {
|
||||
if (item.action === 'edit') {
|
||||
return (
|
||||
|
||||
@@ -123,7 +123,7 @@ const QuestionList: FC<Props> = ({ source }) => {
|
||||
<ListGroup.Item key={li.id} className="border-bottom py-3 px-0">
|
||||
<h5 className="text-wrap text-break">
|
||||
<NavLink
|
||||
to={pathFactory.questionLanding(li.id, li.title)}
|
||||
to={pathFactory.questionLanding(li.id, li.url_title)}
|
||||
className="link-dark">
|
||||
{li.title}
|
||||
{li.status === 2 ? ` [${t('closed')}]` : ''}
|
||||
|
||||
@@ -13,21 +13,25 @@ interface IProps {
|
||||
qid: any;
|
||||
aid?: any;
|
||||
title: string;
|
||||
slugTitle: string;
|
||||
}
|
||||
|
||||
const Index: FC<IProps> = ({ type, qid, aid, title }) => {
|
||||
const Index: FC<IProps> = ({ type, qid, aid, title, slugTitle = '' }) => {
|
||||
const user = loggedUserInfoStore((state) => state.user);
|
||||
const [show, setShow] = useState(false);
|
||||
const [showTip, setShowTip] = useState(false);
|
||||
const [canSystemShare, setSystemShareState] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
// FIXME: pathFactory
|
||||
let baseUrl =
|
||||
type === 'question'
|
||||
? `${window.location.origin}${pathFactory.questionLanding(qid, title)}`
|
||||
? `${window.location.origin}${pathFactory.questionLanding(
|
||||
qid,
|
||||
slugTitle,
|
||||
)}`
|
||||
: `${window.location.origin}${pathFactory.answerLanding({
|
||||
questionId: qid,
|
||||
questionTitle: title,
|
||||
slugTitle,
|
||||
answerId: aid,
|
||||
})}`;
|
||||
if (user.id) {
|
||||
|
||||
@@ -130,7 +130,7 @@ const Answers: FC = () => {
|
||||
<a
|
||||
href={pathFactory.answerLanding({
|
||||
questionId: li.question_id,
|
||||
questionTitle: li.question_info.title,
|
||||
slugTitle: li.question_info.url_title,
|
||||
answerId: li.id,
|
||||
})}
|
||||
target="_blank"
|
||||
|
||||
@@ -102,7 +102,7 @@ const Flags: FC = () => {
|
||||
<a
|
||||
href={pathFactory.questionLanding(
|
||||
li.question_id,
|
||||
li.title,
|
||||
li.url_title,
|
||||
)}
|
||||
target="_blank"
|
||||
className="text-wrap text-break mt-2"
|
||||
|
||||
@@ -137,7 +137,7 @@ const Questions: FC = () => {
|
||||
<tr key={li.id}>
|
||||
<td>
|
||||
<a
|
||||
href={pathFactory.questionLanding(li.id, li.title)}
|
||||
href={pathFactory.questionLanding(li.id, li.url_title)}
|
||||
target="_blank"
|
||||
className="text-break text-wrap"
|
||||
rel="noreferrer">
|
||||
|
||||
@@ -29,7 +29,7 @@ const SearchQuestion = ({ similarQuestions }) => {
|
||||
as="a"
|
||||
className="link-dark"
|
||||
key={item.id}
|
||||
href={pathFactory.questionLanding(item.id, item.title)}
|
||||
href={pathFactory.questionLanding(item.id, item.url_title)}
|
||||
target="_blank">
|
||||
<span className="text-wrap text-break">
|
||||
{item.title}
|
||||
|
||||
@@ -237,7 +237,7 @@ const Ask = () => {
|
||||
edit_summary: formData.edit_summary.value,
|
||||
})
|
||||
.then((res) => {
|
||||
navigate(pathFactory.questionLanding(qid, params.title), {
|
||||
navigate(pathFactory.questionLanding(qid, params.url_title), {
|
||||
state: { isReview: res?.wait_for_review },
|
||||
});
|
||||
})
|
||||
@@ -264,7 +264,7 @@ const Ask = () => {
|
||||
html: editorRef2.current.getHtml(),
|
||||
})
|
||||
.then(() => {
|
||||
navigate(pathFactory.questionLanding(id, params.title));
|
||||
navigate(pathFactory.questionLanding(id, params.url_title));
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.isError) {
|
||||
@@ -273,7 +273,7 @@ const Ask = () => {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
navigate(pathFactory.questionLanding(id, params.title));
|
||||
navigate(pathFactory.questionLanding(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ interface Props {
|
||||
/** is author */
|
||||
isAuthor: boolean;
|
||||
questionTitle: string;
|
||||
slugTitle: string;
|
||||
isLogged: boolean;
|
||||
callback: (type: string) => void;
|
||||
}
|
||||
@@ -32,6 +33,7 @@ const Index: FC<Props> = ({
|
||||
isAuthor,
|
||||
isLogged,
|
||||
questionTitle = '',
|
||||
slugTitle,
|
||||
callback,
|
||||
}) => {
|
||||
const { t } = useTranslation('translation', {
|
||||
@@ -114,6 +116,7 @@ const Index: FC<Props> = ({
|
||||
type="answer"
|
||||
isAccepted={data.adopted === 2}
|
||||
title={questionTitle}
|
||||
slugTitle={slugTitle}
|
||||
callback={callback}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
@@ -65,7 +65,7 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer, isLogged }) => {
|
||||
<Link
|
||||
className="link-dark"
|
||||
reloadDocument
|
||||
to={pathFactory.questionLanding(data.id, data.title)}>
|
||||
to={pathFactory.questionLanding(data.id, data.url_title)}>
|
||||
{data.title}
|
||||
{data.status === 2
|
||||
? ` [${t('closed', { keyPrefix: 'question' })}]`
|
||||
@@ -129,6 +129,7 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer, isLogged }) => {
|
||||
type="question"
|
||||
memberActions={data?.member_actions}
|
||||
title={data.title}
|
||||
slugTitle={data.url_title}
|
||||
hasAnswer={hasAnswer}
|
||||
isAccepted={Boolean(data?.accepted_answer_id)}
|
||||
callback={initPage}
|
||||
|
||||
@@ -32,7 +32,7 @@ const Index: FC<Props> = ({ id }) => {
|
||||
action
|
||||
key={item.id}
|
||||
as={Link}
|
||||
to={pathFactory.questionLanding(item.id, item.title)}>
|
||||
to={pathFactory.questionLanding(item.id, item.url_title)}>
|
||||
<div className="link-dark">{item.title}</div>
|
||||
{item.answer_count > 0 && (
|
||||
<div
|
||||
|
||||
@@ -178,6 +178,7 @@ const Index = () => {
|
||||
key={item?.id}
|
||||
data={item}
|
||||
questionTitle={question?.title || ''}
|
||||
slugTitle={question?.url_title}
|
||||
isAuthor={isAuthor}
|
||||
callback={initPage}
|
||||
isLogged={isLogged}
|
||||
|
||||
@@ -115,7 +115,7 @@ const Index = () => {
|
||||
navigate(
|
||||
pathFactory.answerLanding({
|
||||
questionId: qid,
|
||||
questionTitle: data?.question?.title,
|
||||
slugTitle: data?.question?.url_title,
|
||||
answerId: aid,
|
||||
}),
|
||||
{
|
||||
@@ -147,7 +147,7 @@ const Index = () => {
|
||||
<Row className="justify-content-center">
|
||||
<Col xxl={7} lg={8} sm={12} className="mb-4 mb-md-0">
|
||||
<a
|
||||
href={pathFactory.questionLanding(qid, data?.question.title)}
|
||||
href={pathFactory.questionLanding(qid, data?.question.url_title)}
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<h5 className="mb-3">{data?.question.title}</h5>
|
||||
|
||||
@@ -95,7 +95,7 @@ const Index: FC = () => {
|
||||
const editor = unreviewed_info?.user_info;
|
||||
const editTime = unreviewed_info?.create_at;
|
||||
if (type === 'question') {
|
||||
itemLink = pathFactory.questionLanding(info?.object_id, info?.title);
|
||||
itemLink = pathFactory.questionLanding(info?.object_id, info?.url_title);
|
||||
itemTitle = info?.title;
|
||||
editBadge = t('question_edit');
|
||||
editSummary ||= t('edit_question');
|
||||
@@ -103,7 +103,7 @@ const Index: FC = () => {
|
||||
itemLink = pathFactory.answerLanding({
|
||||
// @ts-ignore
|
||||
questionId: unreviewed_info.content.question_id,
|
||||
questionTitle: info?.title,
|
||||
slugTitle: info?.url_title,
|
||||
answerId: unreviewed_info.object_id,
|
||||
});
|
||||
itemTitle = info?.title;
|
||||
|
||||
@@ -15,11 +15,14 @@ const Index: FC<Props> = ({ data }) => {
|
||||
if (!data?.object_type) {
|
||||
return null;
|
||||
}
|
||||
let itemUrl = pathFactory.questionLanding(data.object.id, data.object.title);
|
||||
let itemUrl = pathFactory.questionLanding(
|
||||
data.object.id,
|
||||
data.object.url_title,
|
||||
);
|
||||
if (data.object_type === 'answer' && data.object.question_id) {
|
||||
itemUrl = pathFactory.answerLanding({
|
||||
questionId: data.object.question_id,
|
||||
questionTitle: data.object.title,
|
||||
slugTitle: data.object.url_title,
|
||||
answerId: data.object.id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ const Index: FC = () => {
|
||||
if (timelineData?.object_info.object_type === 'question') {
|
||||
linkUrl = pathFactory.questionLanding(
|
||||
timelineData?.object_info.question_id,
|
||||
timelineData?.object_info.title,
|
||||
timelineData?.object_info.url_title,
|
||||
);
|
||||
pageTitle = `${t('title_for_question')} ${timelineData?.object_info.title}`;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ const Index: FC = () => {
|
||||
if (timelineData?.object_info.object_type === 'answer') {
|
||||
linkUrl = pathFactory.answerLanding({
|
||||
questionId: timelineData?.object_info.question_id,
|
||||
questionTitle: timelineData?.object_info.title,
|
||||
slugTitle: timelineData?.object_info.url_title,
|
||||
answerId: timelineData?.object_info.answer_id,
|
||||
});
|
||||
pageTitle = `${t('title_for_answer', {
|
||||
|
||||
@@ -23,7 +23,7 @@ const Index: FC<Props> = ({ visible, data }) => {
|
||||
<a
|
||||
href={pathFactory.answerLanding({
|
||||
questionId: item.question_id,
|
||||
questionTitle: item.question_info?.title,
|
||||
slugTitle: item.question_info?.url_title,
|
||||
answerId: item.answer_id,
|
||||
})}
|
||||
className="text-break">
|
||||
|
||||
@@ -22,10 +22,10 @@ const Index: FC<Props> = ({ visible, data }) => {
|
||||
className="text-break"
|
||||
href={
|
||||
item.object_type === 'question'
|
||||
? pathFactory.questionLanding(item.object_id, item.title)
|
||||
? pathFactory.questionLanding(item.object_id, item.url_title)
|
||||
: pathFactory.answerLanding({
|
||||
questionId: item.question_id,
|
||||
questionTitle: item.title,
|
||||
slugTitle: item.url_title,
|
||||
answerId: item.object_id,
|
||||
})
|
||||
}>
|
||||
|
||||
@@ -29,7 +29,7 @@ const Index: FC<Props> = ({ visible, tabName, data }) => {
|
||||
className="text-break"
|
||||
href={pathFactory.questionLanding(
|
||||
tabName === 'questions' ? item.question_id : item.id,
|
||||
item.title,
|
||||
item.url_title,
|
||||
)}>
|
||||
{item.title}
|
||||
{tabName === 'questions' && item.status === 'closed'
|
||||
|
||||
@@ -33,10 +33,13 @@ const Index: FC<Props> = ({ visible, data }) => {
|
||||
className="text-break"
|
||||
href={
|
||||
item.object_type === 'question'
|
||||
? pathFactory.questionLanding(item.object_id, item.title)
|
||||
? pathFactory.questionLanding(
|
||||
item.object_id,
|
||||
item.url_title,
|
||||
)
|
||||
: pathFactory.answerLanding({
|
||||
questionId: item.question_id,
|
||||
questionTitle: item.title,
|
||||
slugTitle: item.url_title,
|
||||
answerId: item.object_id,
|
||||
})
|
||||
}>
|
||||
|
||||
@@ -23,10 +23,13 @@ const Index: FC<Props> = ({ data, type }) => {
|
||||
type === 'answer'
|
||||
? pathFactory.answerLanding({
|
||||
questionId: item.question_id,
|
||||
questionTitle: item.question_info?.title,
|
||||
slugTitle: item.question_info?.url_title,
|
||||
answerId: item.answer_id,
|
||||
})
|
||||
: pathFactory.questionLanding(item.question_id, item.title)
|
||||
: pathFactory.questionLanding(
|
||||
item.question_id,
|
||||
item.url_title,
|
||||
)
|
||||
}>
|
||||
{type === 'answer' ? item.question_info.title : item.title}
|
||||
</a>
|
||||
|
||||
@@ -29,10 +29,13 @@ const Index: FC<Props> = ({ visible, data }) => {
|
||||
className="text-break"
|
||||
href={
|
||||
item.object_type === 'question'
|
||||
? pathFactory.questionLanding(item.question_id, item.title)
|
||||
? pathFactory.questionLanding(
|
||||
item.question_id,
|
||||
item.url_title,
|
||||
)
|
||||
: pathFactory.answerLanding({
|
||||
questionId: item.question_id,
|
||||
questionTitle: item.title,
|
||||
slugTitle: item.url_title,
|
||||
answerId: item.answer_id,
|
||||
})
|
||||
}>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import urlcat from 'urlcat';
|
||||
|
||||
import Pattern from '@/common/pattern';
|
||||
import { seoSettingStore } from '@/stores';
|
||||
|
||||
const tagLanding = (slugName: string) => {
|
||||
@@ -20,30 +19,25 @@ const tagInfo = (slugName: string) => {
|
||||
const tagEdit = (tagId: string) => {
|
||||
return urlcat('/tags/:tagId/edit', { tagId });
|
||||
};
|
||||
const questionLanding = (questionId: string, title: string = '') => {
|
||||
const questionLanding = (questionId: string, slugTitle: string = '') => {
|
||||
const { seo } = seoSettingStore.getState();
|
||||
if (seo.permalink === 1) {
|
||||
title = title.toLowerCase();
|
||||
title = title.trim().replace(/\s+/g, '-');
|
||||
title = title.replace(Pattern.emoji, '');
|
||||
if (title) {
|
||||
return urlcat('/questions/:questionId/:slugPermalink', {
|
||||
questionId,
|
||||
slugPermalink: title,
|
||||
});
|
||||
}
|
||||
if (seo.permalink === 1 && slugTitle) {
|
||||
return urlcat('/questions/:questionId/:slugPermalink', {
|
||||
questionId,
|
||||
slugPermalink: slugTitle,
|
||||
});
|
||||
}
|
||||
|
||||
return urlcat('/questions/:questionId', { questionId });
|
||||
};
|
||||
const answerLanding = (params: {
|
||||
questionId: string;
|
||||
questionTitle?: string;
|
||||
slugTitle?: string;
|
||||
answerId: string;
|
||||
}) => {
|
||||
const questionLandingUrl = questionLanding(
|
||||
params.questionId,
|
||||
params.questionTitle,
|
||||
params.slugTitle,
|
||||
);
|
||||
return urlcat(`${questionLandingUrl}/:answerId`, {
|
||||
answerId: params.answerId,
|
||||
|
||||
Reference in New Issue
Block a user