Skip to content

Commit

Permalink
feat: add ability to bind manga metadata to another anilist id
Browse files Browse the repository at this point in the history
  • Loading branch information
oae committed Oct 27, 2022
1 parent 350b5f3 commit a3a9252
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 5 deletions.
100 changes: 97 additions & 3 deletions src/components/addManga/steps/reviewStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import { Badge, createStyles, Divider, Grid, Group, Image, LoadingOverlay, Text, Title, Tooltip } from '@mantine/core';
import {
ActionIcon,
Badge,
createStyles,
Divider,
Grid,
Group,
Image,
LoadingOverlay,
Popover,
Text,
TextInput,
Title,
Tooltip,
} from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { getHotkeyHandler } from '@mantine/hooks';
import { IconCheck, IconEdit, IconGitMerge } from '@tabler/icons';
import { useState } from 'react';
import { trpc } from '../../../utils/trpc';
import type { FormType } from '../form';

Expand All @@ -11,6 +28,9 @@ const useStyles = createStyles((_theme) => ({
}));

export function ReviewStep({ form }: { form: UseFormReturnType<FormType> }) {
const [anilistId, setAnilistId] = useState<string>();
const [opened, setOpened] = useState(false);
const bindMutation = trpc.manga.bind.useMutation();
const query = trpc.manga.detail.useQuery(
{
source: form.values.source,
Expand All @@ -26,9 +46,23 @@ export function ReviewStep({ form }: { form: UseFormReturnType<FormType> }) {

const manga = query.data;

const handleBind = async () => {
setOpened(false);
if (!anilistId || !manga?.Name) {
return;
}
await bindMutation.mutateAsync({
anilistId,
title: manga.Name,
});

query.refetch();
setAnilistId('');
};

return (
<>
<LoadingOverlay visible={query.isLoading} />
<LoadingOverlay visible={query.isLoading || bindMutation.isLoading} />

{manga && (
<Grid>
Expand All @@ -55,7 +89,67 @@ export function ReviewStep({ form }: { form: UseFormReturnType<FormType> }) {
/>
</Grid.Col>
<Grid.Col span={8}>
<Divider mb="xs" labelPosition="center" label={<Title order={3}>{manga.Name}</Title>} />
<Divider
mb="xs"
labelPosition="center"
label={
<>
<Title order={3}>{manga.Name}</Title>
<Popover
width={300}
trapFocus
position="bottom"
withArrow
shadow="md"
opened={opened}
onChange={setOpened}
>
<Popover.Target>
<Tooltip inline label="Fix the wrong match">
<ActionIcon
ml={4}
color="blue"
variant="transparent"
size="lg"
radius="xl"
onClick={() => setOpened((o) => !o)}
>
<IconEdit size={18} />
</ActionIcon>
</Tooltip>
</Popover.Target>
<Popover.Dropdown
sx={(theme) => ({
background: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white,
})}
>
<TextInput
data-autofocus
mb="xl"
size="md"
radius="xl"
value={anilistId}
onChange={(event) => setAnilistId(event.currentTarget.value)}
onKeyDown={getHotkeyHandler([['Enter', handleBind]])}
icon={<IconGitMerge size={18} stroke={1.5} />}
rightSection={
<ActionIcon size={32} radius="xl" color="blue" variant="filled" onClick={handleBind}>
<IconCheck size={18} stroke={1.5} />
</ActionIcon>
}
rightSectionWidth={42}
label={
<Text size="sm" mb="xs">
Please enter a new AniList id for {manga.Name}
</Text>
}
placeholder="AniList Id"
/>
</Popover.Dropdown>
</Popover>
</>
}
/>
{manga.Metadata.Synonyms && (
<Group spacing="xs">
{manga.Metadata.Synonyms.map((synonym) => (
Expand Down
20 changes: 19 additions & 1 deletion src/server/trpc/router/manga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { z } from 'zod';
import { isCronValid, sanitizer } from '../../../utils';
import { checkChaptersQueue, removeJob, schedule } from '../../queue/checkChapters';
import { downloadQueue } from '../../queue/download';
import { getAvailableSources, getMangaDetail, Manga, removeManga, search } from '../../utils/mangal';
import {
bindTitleToAnilistId,
getAvailableSources,
getMangaDetail,
Manga,
removeManga,
search,
} from '../../utils/mangal';
import { t } from '../trpc';

export const mangaRouter = t.router({
Expand All @@ -14,6 +21,17 @@ export const mangaRouter = t.router({
sources: t.procedure.query(async () => {
return getAvailableSources();
}),
bind: t.procedure
.input(
z.object({
title: z.string().trim().min(1),
anilistId: z.string().trim().min(1),
}),
)
.mutation(async ({ input }) => {
const { title, anilistId } = input;
await bindTitleToAnilistId(title, anilistId);
}),
detail: t.procedure
.input(
z.object({
Expand Down
11 changes: 10 additions & 1 deletion src/server/utils/mangal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ export const getAvailableSources = async () => {
return [];
};

export const bindTitleToAnilistId = async (title: string, anilistId: string) => {
try {
const { command } = await execa('mangal', ['inline', 'anilist', 'set', '--name', title, '--id', anilistId]);
logger.info(`Bind manga to anilist id with following command: ${command}`);
} catch (err) {
logger.error(`Failed to bind manga to anilist id. err: ${err}`);
}
};

export const getMangaPath = (libraryPath: string, title: string) => path.resolve(libraryPath, sanitizer(title));

export const search = async (source: string, keyword: string): Promise<IOutput> => {
Expand All @@ -71,7 +80,7 @@ export const search = async (source: string, keyword: string): Promise<IOutput>
logger.info(`Search manga with following command: ${command}`);
return JSON.parse(stdout);
} catch (err) {
logger.error(`Failed to get available sources: err: ${err}`);
logger.error(`Failed to search manga. err: ${err}`);
}

return {
Expand Down

0 comments on commit a3a9252

Please sign in to comment.