Fix: Format-check fail and translations. Feat: added animations to upvote and downvote buttons

This commit is contained in:
Moyasee
2025-10-03 02:55:41 +03:00
parent 8d5b169166
commit fab02c4d16
5 changed files with 66 additions and 21 deletions

View File

@@ -213,7 +213,6 @@
"leave_a_review": "Leave a Review",
"write_review_placeholder": "Share your thoughts about this game...",
"sort_newest": "Newest",
"sort_by": "Sort by",
"no_reviews_yet": "No reviews yet",
"be_first_to_review": "Be the first to share your thoughts about this game!",
"sort_oldest": "Oldest",
@@ -226,7 +225,6 @@
"loading_reviews": "Loading reviews...",
"loading_more_reviews": "Loading more reviews...",
"load_more_reviews": "Load More Reviews",
"youve_played_for_hours": "You've played for {{hours}} hours",
"would_you_recommend_this_game": "Would you like to leave a review to this game?",
"yes": "Yes",
"maybe_later": "Maybe Later",
@@ -330,8 +328,8 @@
"delete_review": "Delete review",
"delete_review_modal_title": "Are you sure you want to delete your review?",
"delete_review_modal_description": "This action cannot be undone.",
"delete_review_button": "Delete",
"delete_review_karma_warning": "You will lose any karma points earned from this review."
"delete_review_modal_delete_button": "Delete",
"delete_review_modal_cancel_button": "Cancel"
},
"activation": {
"title": "Activate Hydra",

View File

@@ -4,6 +4,7 @@ import { ThumbsUp, ThumbsDown } from "lucide-react";
import { useNavigate } from "react-router-dom";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { motion } from "framer-motion";
import type { GameReview } from "@types";
import { HeroPanel } from "./hero";
@@ -131,10 +132,10 @@ export function GameDetailsContent() {
},
handlePaste: (view, event) => {
// Strip formatting from pasted content to prevent overflow issues
const text = event.clipboardData?.getData('text/plain') || '';
const text = event.clipboardData?.getData("text/plain") || "";
const currentText = view.state.doc.textContent;
const remainingChars = MAX_REVIEW_CHARS - currentText.length;
if (text && remainingChars > 0) {
event.preventDefault();
const truncatedText = text.slice(0, remainingChars);
@@ -147,7 +148,7 @@ export function GameDetailsContent() {
onUpdate: ({ editor }) => {
const text = editor.getText();
setReviewCharCount(text.length);
// Prevent typing beyond character limit
if (text.length > MAX_REVIEW_CHARS) {
const truncatedContent = text.slice(0, MAX_REVIEW_CHARS);
@@ -293,7 +294,12 @@ export function GameDetailsContent() {
console.log("reviewScore:", reviewScore);
console.log("submittingReview:", submittingReview);
if (!objectId || !reviewHtml.trim() || submittingReview || reviewCharCount > MAX_REVIEW_CHARS) {
if (
!objectId ||
!reviewHtml.trim() ||
submittingReview ||
reviewCharCount > MAX_REVIEW_CHARS
) {
console.log("Early return - validation failed");
return;
}
@@ -584,7 +590,13 @@ export function GameDetailsContent() {
</button>
</div>
<div className="game-details__review-char-counter">
<span className={reviewCharCount > MAX_REVIEW_CHARS ? "over-limit" : ""}>
<span
className={
reviewCharCount > MAX_REVIEW_CHARS
? "over-limit"
: ""
}
>
{reviewCharCount}/{MAX_REVIEW_CHARS}
</span>
</div>
@@ -619,12 +631,14 @@ export function GameDetailsContent() {
<option value={10}>10/10</option>
</select>
</div>
<button
className="game-details__review-submit-button"
onClick={handleSubmitReview}
disabled={
!editor?.getHTML().trim() || submittingReview || reviewCharCount > MAX_REVIEW_CHARS
!editor?.getHTML().trim() ||
submittingReview ||
reviewCharCount > MAX_REVIEW_CHARS
}
>
{submittingReview
@@ -739,24 +753,56 @@ export function GameDetailsContent() {
/>
<div className="game-details__review-actions">
<div className="game-details__review-votes">
<button
<motion.button
className={`game-details__vote-button game-details__vote-button--upvote ${review.hasUpvoted ? "game-details__vote-button--active" : ""}`}
onClick={() =>
handleVoteReview(review.id, "upvote")
}
whileTap={{
scale: 0.9,
transition: { duration: 0.1 },
}}
whileHover={{
scale: 1.05,
transition: { duration: 0.2 },
}}
animate={
review.hasUpvoted
? {
scale: [1, 1.2, 1],
transition: { duration: 0.3 },
}
: {}
}
>
<ThumbsUp size={16} />
<span>{review.upvotes || 0}</span>
</button>
<button
</motion.button>
<motion.button
className={`game-details__vote-button game-details__vote-button--downvote ${review.hasDownvoted ? "game-details__vote-button--active" : ""}`}
onClick={() =>
handleVoteReview(review.id, "downvote")
}
whileTap={{
scale: 0.9,
transition: { duration: 0.1 },
}}
whileHover={{
scale: 1.05,
transition: { duration: 0.2 },
}}
animate={
review.hasDownvoted
? {
scale: [1, 1.2, 1],
transition: { duration: 0.3 },
}
: {}
}
>
<ThumbsDown size={16} />
<span>{review.downvotes || 0}</span>
</button>
</motion.button>
</div>
{userDetails?.id === review.user?.id && (
<button

View File

@@ -659,7 +659,7 @@ $hero-height: 300px;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
border-color: globals.$brand-teal;
border-color: rgba(255, 255, 255, 0.1);
}
}
@@ -945,20 +945,20 @@ $hero-height: 300px;
&__review-input {
min-height: 120px;
padding: 12px;
.ProseMirror {
outline: none;
color: #ffffff;
font-size: 14px;
line-height: 1.5;
&:focus {
outline: none;
}
p {
margin: 0 0 8px 0;
&:last-child {
margin-bottom: 0;
}

View File

@@ -15,5 +15,6 @@
&__actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
}

View File

@@ -29,11 +29,11 @@ export function DeleteReviewModal({
>
<div className="delete-review-modal__actions">
<Button onClick={onClose} theme="outline">
{t("cancel")}
{t("delete_review_modal_cancel_button")}
</Button>
<Button onClick={handleDeleteReview} theme="danger">
{t("delete_review_button")}
{t("delete_review_modal_delete_button")}
</Button>
</div>
</Modal>