/* * Copyright (C) Contributors to the Suwayomi project * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import { useTranslation } from 'react-i18next'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; import Divider from '@mui/material/Divider'; import FormGroup from '@mui/material/FormGroup'; import IconButton from '@mui/material/IconButton'; import Link from '@mui/material/Link'; import ListItemButton from '@mui/material/ListItemButton'; import MenuItem from '@mui/material/MenuItem'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import PopupState, { bindDialog, bindMenu, bindTrigger } from 'material-ui-popup-state'; import { useMemo, useState } from 'react'; import Badge from '@mui/material/Badge'; import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { Trackers } from '@/features/tracker/services/Trackers.ts'; import { ListPreference } from '@/features/source/configuration/components/ListPreference.tsx'; import { NumberSetting } from '@/base/components/settings/NumberSetting.tsx'; import { DateSetting } from '@/base/components/settings/DateSetting.tsx'; import { makeToast } from '@/base/utils/Toast.ts'; import { Menu } from '@/base/components/menu/Menu.tsx'; import { CARD_STYLING, UNSET_DATE } from '@/features/tracker/Tracker.constants.ts'; import { TypographyMaxLines } from '@/base/components/texts/TypographyMaxLines.tsx'; import { SelectSetting, SelectSettingValue } from '@/base/components/settings/SelectSetting.tsx'; import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx'; import { TrackRecordType } from '@/lib/graphql/generated/graphql.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { TTrackerBind, TTrackRecordBind } from '@/features/tracker/Tracker.types.ts'; import { AvatarSpinner } from '@/base/components/AvatarSpinner.tsx'; const TrackerActiveLink = ({ children, url }: { children: React.ReactNode; url: string }) => ( {children} ); type TTrackerActive = Pick; const TrackerActiveRemoveBind = ({ trackerRecordId, tracker, onClick, onClose, }: { trackerRecordId: TrackRecordType['id']; tracker: TTrackerActive; onClick: () => void; onClose: () => void; }) => { const { t } = useTranslation(); const [removeRemoteTracking, setRemoveRemoteTracking] = useState(false); const removeBind = () => { onClose(); requestManager .unbindTracker(trackerRecordId, removeRemoteTracking) .response.then(() => makeToast(t('manga.action.track.remove.label.success'), 'success')) .catch((e) => makeToast(t('manga.action.track.remove.label.error'), 'error', getErrorMessage(e))); }; return ( {(popupState) => ( <> { onClick(); popupState.open(); }} > {t('global.button.remove')} { onClose(); popupState.close(); }} > {t('manga.action.track.remove.dialog.label.title', { tracker: tracker.name })} {t('manga.action.track.remove.dialog.label.description')} {tracker.supportsTrackDeletion && ( setRemoveRemoteTracking(checked)} /> )} )} ); }; const TrackerUpdatePrivateStatus = ({ trackRecordId, isPrivate, closeMenu, supportsPrivateTracking, }: { trackRecordId: TrackRecordType['id']; isPrivate: boolean; closeMenu: () => void; supportsPrivateTracking: TTrackerActive['supportsPrivateTracking']; }) => { const { t } = useTranslation(); if (!supportsPrivateTracking) { return null; } return ( { requestManager .updateTrackerBind(trackRecordId, { private: !isPrivate }) .response.catch((e) => makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)), ); closeMenu(); }} > {t(isPrivate ? 'tracking.action.button.track_publicly' : 'tracking.action.button.track_privately')} ); }; type TTrackRecordActive = Pick; const TrackerActiveHeader = ({ trackRecord, tracker, openSearch, }: { trackRecord: TTrackRecordActive; tracker: TTrackerActive; openSearch: () => void; }) => { const { t } = useTranslation(); return ( ) : null } > {trackRecord.title} {(popupState) => ( <> {(onClose, setHideMenu) => [ onClose()}> {t('global.label.open_in_browser')} , setHideMenu(true)} onClose={onClose} />, , ]} )} ); }; const TrackerActiveCardInfoRow = ({ children }: { children: React.ReactNode }) => ( {children} ); const isUnsetScore = (score: string | number): boolean => !Math.trunc(Number(score)); export const TrackerActiveCard = ({ trackRecord, tracker, onClick, }: { trackRecord: TTrackRecordBind; tracker: TTrackerBind; onClick: () => void; }) => { const { t } = useTranslation(); const isScoreUnset = isUnsetScore(trackRecord.displayScore); const currentScore = isScoreUnset ? tracker.scores[0] : trackRecord.displayScore; const selectSettingValues = useMemo( () => tracker.scores.map( (score) => [score, { text: isUnsetScore(score) ? '-' : score }] satisfies SelectSettingValue< TTrackerBind['scores'][number] >, ), [tracker.scores], ); const updateTrackerBind = (patch: Parameters[1]) => { requestManager .updateTrackerBind(trackRecord.id, patch) .response.catch((e) => makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)), ); }; return ( status.name)} key="status" type="ListPreference" entryValues={tracker.statuses.map((status) => `${status.value}`)} ListPreferenceCurrentValue={`${trackRecord.status}`} updateValue={(_, status) => updateTrackerBind({ status: Number(status) })} summary="%s" /> updateTrackerBind({ lastChapterRead })} /> settingName={t('tracking.track_record.label.score')} value={currentScore} values={selectSettingValues} handleChange={(score) => updateTrackerBind({ scoreString: score })} /> updateTrackerBind({ startDate: startDate ?? UNSET_DATE }) } /> updateTrackerBind({ finishDate: finishDate ?? UNSET_DATE }) } /> ); };