-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/translateedit #27
Conversation
## ウォークスルー
この変更は、言語選択機能の追加、翻訳と投票機能の強化、および新しいカスタムフックの導入を含みます。これにより、ユーザーは多言語対応のインターフェースで翻訳を管理し、評価できるようになります。
## 変更点
| ファイル | 要約 |
| --- | --- |
| web/app/components/Header.tsx | `Header`コンポーネントに言語選択機能を追加 |
| web/app/root.tsx | ユーザーの言語設定をセッションに保存し、HTMLのlang属性とHeaderコンポーネントに反映 |
| web/app/components/ui/textarea.tsx | 新しい`Textarea`コンポーネントを追加 |
| web/app/routes/_index/components/URLTranslationForm.tsx | `targetLanguage`の選択機能を削除 |
| web/app/routes/_index/components/translatedList.tsx | `TranslatedList`コンポーネントを更新し、言語情報を表示 |
| web/app/routes/reader.$encodedUrl/components/TranslatedContent.tsx | `TranslatedContent`コンポーネントに新しいコールバック関数と`userId`を追加 |
| web/app/routes/reader.$encodedUrl/components/Translation.tsx | 翻訳の表示と投票機能を持つ`Translation`コンポーネントを追加 |
| web/app/routes/reader.$encodedUrl/components/VoteButtons.tsx | `VoteButtons`コンポーネントを追加 |
| web/app/routes/reader.$encodedUrl/hooks/useClickOutside.ts | `useClickOutside`カスタムフックを追加 |
| web/app/routes/reader.$encodedUrl/route.tsx | `loader`関数と`action`関数を追加し、投票と翻訳追加のハンドラを実装 | Uplevel your code reviews with CodeRabbit ProCodeRabbit ProIf you like this project, please support us by purchasing the Pro version. The Pro version has advanced context, superior noise reduction and several proprietary improvements compared to the open source version. Moreover, CodeRabbit Pro is free for open source projects. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Files selected (21)
- web/app/components/Header.tsx (1)
- web/app/components/ui/textarea.tsx (1)
- web/app/root.tsx (1)
- web/app/routes/_index/components/URLTranslationForm.tsx (4)
- web/app/routes/_index/components/translatedList.tsx (1)
- web/app/routes/reader.$encodedUrl/components/TranslatedContent.tsx (1)
- web/app/routes/reader.$encodedUrl/components/Translation.tsx (1)
- web/app/routes/reader.$encodedUrl/components/VoteButtons.tsx (1)
- web/app/routes/reader.$encodedUrl/hooks/useClickOutside.ts (1)
- web/app/routes/reader.$encodedUrl/route.tsx (2)
- web/app/routes/reader.$encodedUrl/types.ts (1)
- web/app/routes/reader.$encodedUrl/utils.ts (1)
- web/app/routes/reader.$encodedUrl/utils/actions.ts (1)
- web/app/routes/reader.$encodedUrl/utils/create.ts (1)
- web/package.json (1)
- web/prisma/migrations/20240715072354_/migration.sql (1)
- web/prisma/migrations/20240715073559_/migration.sql (1)
- web/prisma/migrations/20240715074033_/migration.sql (1)
- web/prisma/migrations/20240715083506_/migration.sql (1)
- web/prisma/migrations/20240715093411_/migration.sql (1)
- web/prisma/schema.prisma (1)
Files skipped from review due to trivial changes (3)
- web/package.json
- web/app/routes/reader.$encodedUrl/components/Translation.tsx (diff too large)
- web/app/routes/reader.$encodedUrl/route.tsx (diff too large)
Review comments generated (43)
- Review: 13
- LGTM: 30
Tips
Chat with CodeRabbit Bot (@coderabbitai
)
- Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
- Invite the bot into a review comment chain by tagging
@coderabbitai
in a reply.
Code suggestions
- The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
- You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.
Pausing incremental reviews
- Add
@coderabbitai: ignore
anywhere in the PR description to pause further reviews from the bot.
@@ -0,0 +1,24 @@ | |||
import * as React from "react"; | |||
|
|||
import { cn } from "~/utils"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
select: { | ||
number: true, | ||
translateTexts: { | ||
where: { language: "ja" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
select: { | ||
number: true, | ||
translateTexts: { | ||
where: { language: "ja" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const useClickOutside = (callback: () => void) => { | ||
const ref = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
const handleClickOutside = (event: MouseEvent) => { | ||
if (ref.current && !ref.current.contains(event.target as Node)) { | ||
callback(); | ||
} | ||
}; | ||
|
||
document.addEventListener("mousedown", handleClickOutside); | ||
return () => { | ||
document.removeEventListener("mousedown", handleClickOutside); | ||
}; | ||
}, [callback]); | ||
|
||
return ref; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
このカスタムフックは、クリックイベントを監視して指定されたコールバックを実行するためのものですが、useRef
の型が固定されているため、他の要素にも対応できるように汎用性を持たせると良いでしょう。また、mousedown
イベントだけでなく、touchstart
イベントも考慮するとモバイルデバイスでも動作します。
-export const useClickOutside = (callback: () => void) => {
- const ref = useRef<HTMLDivElement>(null);
+export const useClickOutside = <T extends HTMLElement>(callback: () => void) => {
+ const ref = useRef<T>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
};
document.addEventListener("mousedown", handleClickOutside);
+ document.addEventListener("touchstart", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
+ document.removeEventListener("touchstart", handleClickOutside);
};
}, [callback]);
return ref;
};
const userVoteStatus = translation.userVote | ||
? translation.userVote.isUpvote | ||
? "upvoted" | ||
: "downvoted" | ||
: "not_voted"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
userVoteStatus
の計算は冗長です。三項演算子を使って簡潔に書くことができます。
- const userVoteStatus = translation.userVote
- ? translation.userVote.isUpvote
- ? "upvoted"
- : "downvoted"
- : "not_voted";
+ const userVoteStatus = translation.userVote ? (translation.userVote.isUpvote ? "upvoted" : "downvoted") : "not_voted";
/* | ||
Warnings: | ||
|
||
- You are about to drop the column `point` on the `translate_texts` table. All the data in the column will be lost. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この警告は重要です。point
列を削除すると、すべてのデータが失われます。これが意図された変更であることを確認してください。また、必要に応じてバックアップを取るか、データ移行戦略を検討してください。
- - You are about to drop the column `point` on the `translate_texts` table. All the data in the column will be lost.
+ - You are about to drop the column `point` on the `translate_texts` table. All the data in the column will be lost. Ensure this is intentional and consider backing up or migrating data if necessary.
/* | ||
Warnings: | ||
|
||
- Added the required column `updated_at` to the `votes` table without a default value. This is not possible if the table is not empty. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await prisma.$transaction(async (prisma) => { | ||
const existingVote = await prisma.vote.findUnique({ | ||
where: { | ||
translateTextId_userId: { translateTextId, userId }, | ||
}, | ||
}); | ||
|
||
if (existingVote) { | ||
if (existingVote.isUpvote === isUpvote) { | ||
await prisma.vote.delete({ where: { id: existingVote.id } }); | ||
await prisma.translateText.update({ | ||
where: { id: translateTextId }, | ||
data: { point: { increment: isUpvote ? -1 : 1 } }, | ||
}); | ||
} else { | ||
await prisma.vote.update({ | ||
where: { id: existingVote.id }, | ||
data: { isUpvote }, | ||
}); | ||
await prisma.translateText.update({ | ||
where: { id: translateTextId }, | ||
data: { point: { increment: isUpvote ? 2 : -2 } }, | ||
}); | ||
} | ||
} else { | ||
await prisma.vote.create({ | ||
data: { userId, translateTextId, isUpvote }, | ||
}); | ||
await prisma.translateText.update({ | ||
where: { id: translateTextId }, | ||
data: { point: { increment: isUpvote ? 1 : -1 } }, | ||
}); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prisma.$transaction
の使用方法に問題があります。現在の実装では、トランザクション内で非同期関数を直接呼び出していますが、これは推奨されていません。代わりに、トランザクションブロック内でprisma
オブジェクトを使用する必要があります。
- await prisma.$transaction(async (prisma) => {
+ await prisma.$transaction(async (tx) => {
const existingVote = await tx.vote.findUnique({
where: {
translateTextId_userId: { translateTextId, userId },
},
});
if (existingVote) {
if (existingVote.isUpvote === isUpvote) {
await tx.vote.delete({ where: { id: existingVote.id } });
await tx.translateText.update({
where: { id: translateTextId },
data: { point: { increment: isUpvote ? -1 : 1 } },
});
} else {
await tx.vote.update({
where: { id: existingVote.id },
data: { isUpvote },
});
await tx.translateText.update({
where: { id: translateTextId },
data: { point: { increment: isUpvote ? 2 : -2 } },
});
}
} else {
await tx.vote.create({
data: { userId, translateTextId, isUpvote },
});
await tx.translateText.update({
where: { id: translateTextId },
data: { point: { increment: isUpvote ? 1 : -1 } },
});
}
});
const sourceText = await prisma.sourceText.findUnique({ | ||
where: { id: sourceTextId }, | ||
include: { page: true }, | ||
}); | ||
|
||
if (sourceText) { | ||
await prisma.translateText.create({ | ||
data: { | ||
language: "ja", | ||
text, | ||
sourceTextId, | ||
pageId: sourceText.page.id, | ||
userId, | ||
}, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sourceText
が見つからなかった場合のエラーハンドリングが不足しています。sourceText
がnull
の場合に適切なレスポンスを返すように修正してください。
if (sourceText) {
await prisma.translateText.create({
data: {
language: "ja",
text,
sourceTextId,
pageId: sourceText.page.id,
userId,
},
});
+ } else {
+ return json({ success: false, message: "Source text not found" }, { status: 404 });
}
-- AlterTable | ||
ALTER TABLE "translate_texts" ADD COLUMN "point" INTEGER NOT NULL DEFAULT 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary by CodeRabbit