first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import {BackendResponse} from '@common/http/backend-response/backend-response';
import {useMutation} from '@tanstack/react-query';
import {apiClient, queryClient} from '@common/http/query-client';
import {showHttpErrorToast} from '@common/utils/http/show-http-error-toast';
import {Review} from '@app/titles/models/review';
import {Reviewable} from '@app/reviews/reviewable';
import {UseFormReturn} from 'react-hook-form';
import {onFormQueryError} from '@common/errors/on-form-query-error';
import {reviewsQueryKey} from '@app/reviews/requests/use-reviews';
interface Response extends BackendResponse {
review: Review;
}
export interface CreateReviewPayload {
score: number;
title?: string;
body?: string;
}
interface Payload extends CreateReviewPayload {
reviewable: Reviewable;
}
export function useCreateReview(form?: UseFormReturn<CreateReviewPayload>) {
return useMutation({
mutationFn: (payload: Payload) => createReview(payload),
onSuccess: async () => {
await queryClient.invalidateQueries({queryKey: reviewsQueryKey()});
},
onError: r => (form ? onFormQueryError(r, form) : showHttpErrorToast(r)),
});
}
function createReview(payload: Payload): Promise<Response> {
return apiClient
.post(`reviews`, {
reviewable_id: payload.reviewable.id,
reviewable_type: payload.reviewable.model_type,
score: payload.score,
title: payload.title,
body: payload.body,
})
.then(r => r.data);
}

View File

@@ -0,0 +1,36 @@
import {useQuery} from '@tanstack/react-query';
import {apiClient} from '@common/http/query-client';
import {BackendResponse} from '@common/http/backend-response/backend-response';
import {useAuth} from '@common/auth/use-auth';
import {Title} from '@app/titles/models/title';
import {Episode} from '@app/titles/models/episode';
interface Response extends BackendResponse {
ratings: {
episode: Record<number, {id: number; score: number}>;
title: Record<number, {id: number; score: number}>;
};
}
export function useCurrentUserRatings() {
const {user} = useAuth();
return useQuery({
queryKey: ['reviews', 'users', `${user?.id}`],
queryFn: () => fetchRatings(),
enabled: !!user,
});
}
export function useCurrentUserRatingFor(item: Title | Episode) {
const query = useCurrentUserRatings();
return {
isLoading: query.isLoading && query.fetchStatus !== 'idle',
rating: query.data?.ratings?.[item.model_type]?.[item.id],
};
}
function fetchRatings() {
return apiClient
.get<Response>(`users/me/ratings`)
.then(response => response.data);
}

View File

@@ -0,0 +1,27 @@
import {BackendResponse} from '@common/http/backend-response/backend-response';
import {useMutation} from '@tanstack/react-query';
import {apiClient, queryClient} from '@common/http/query-client';
import {showHttpErrorToast} from '@common/utils/http/show-http-error-toast';
import {reviewsQueryKey} from '@app/reviews/requests/use-reviews';
interface Response extends BackendResponse {
//
}
interface Payload {
reviewIds: number[];
}
export function useDeleteReviews() {
return useMutation({
mutationFn: (payload: Payload) => deleteReviews(payload),
onSuccess: async () => {
await queryClient.invalidateQueries({queryKey: reviewsQueryKey()});
},
onError: r => showHttpErrorToast(r),
});
}
function deleteReviews({reviewIds}: Payload): Promise<Response> {
return apiClient.delete(`reviews/${reviewIds.join(',')}`).then(r => r.data);
}

View File

@@ -0,0 +1,46 @@
import {useInfiniteData} from '@common/ui/infinite-scroll/use-infinite-data';
import {Reviewable} from '@app/reviews/reviewable';
import {Review} from '@app/titles/models/review';
import {useLocalStorage} from '@common/utils/hooks/local-storage';
import {useSearchParams} from 'react-router-dom';
export interface UseReviewAdditionalData {
current_user_review?: Review;
shared_review?: Review;
}
export function reviewsQueryKey(
reviewable?: Reviewable,
params?: Record<string, any>,
) {
const key: any[] = ['reviews'];
if (reviewable) {
key.push(`${reviewable.id}-${reviewable.model_type}`);
}
if (params) {
key.push(params);
}
return key;
}
export function useReviews(reviewable: Reviewable) {
const [searchParams] = useSearchParams();
const [sort] = useLocalStorage(
`reviewSort.${reviewable.model_type}`,
'created_at:desc',
);
const [defaultOrderBy, defaultOrderDir] = sort.split(':');
return useInfiniteData<Review, UseReviewAdditionalData>({
willSortOrFilter: true,
queryKey: reviewsQueryKey(reviewable, {sort}),
endpoint: 'reviewable/reviews',
defaultOrderBy,
defaultOrderDir: defaultOrderDir as 'asc' | 'desc',
queryParams: {
reviewable_type: reviewable.model_type,
reviewable_id: reviewable.id,
perPage: 5,
sharedReviewId: searchParams.get('reviewId'),
},
});
}

View File

@@ -0,0 +1,33 @@
import {BackendResponse} from '@common/http/backend-response/backend-response';
import {useMutation} from '@tanstack/react-query';
import {apiClient} from '@common/http/query-client';
import {showHttpErrorToast} from '@common/utils/http/show-http-error-toast';
import {Review} from '@app/titles/models/review';
import {toast} from '@common/ui/toast/toast';
import {message} from '@common/i18n/message';
interface Response extends BackendResponse {
review: Review;
}
interface Payload {
isHelpful: boolean;
}
export function useSubmitReviewFeedback(review: Review) {
return useMutation({
mutationFn: (payload: Payload) => submitFeedback(payload, review),
onSuccess: () => {
toast(message('Feedback submitted'));
},
onError: r => showHttpErrorToast(r),
});
}
function submitFeedback(payload: Payload, review: Review): Promise<Response> {
return apiClient
.post(`reviews/${review.id}/feedback`, {
is_helpful: payload.isHelpful,
})
.then(r => r.data);
}