Implement tracker login and logout
This commit is contained in:
@@ -39,6 +39,8 @@ import { BrowseSettings } from '@/screens/settings/BrowseSettings.tsx';
|
|||||||
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
|
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
|
||||||
import { Migrate } from '@/screens/Migrate.tsx';
|
import { Migrate } from '@/screens/Migrate.tsx';
|
||||||
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
||||||
|
import { TrackingSettings } from '@/screens/settings/TrackingSettings.tsx';
|
||||||
|
import { TrackerOAuthLogin } from '@/screens/TrackerOAuthLogin.tsx';
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
// Adds messages only in a dev environment
|
// Adds messages only in a dev environment
|
||||||
@@ -104,6 +106,7 @@ export const App: React.FC = () => (
|
|||||||
<Route path="webUI" element={<WebUISettings />} />
|
<Route path="webUI" element={<WebUISettings />} />
|
||||||
<Route path="browseSettings" element={<BrowseSettings />} />
|
<Route path="browseSettings" element={<BrowseSettings />} />
|
||||||
<Route path="device" element={<DeviceSetting />} />
|
<Route path="device" element={<DeviceSetting />} />
|
||||||
|
<Route path="trackingSettings" element={<TrackingSettings />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* Manga Routes */}
|
{/* Manga Routes */}
|
||||||
@@ -127,6 +130,7 @@ export const App: React.FC = () => (
|
|||||||
<Route index element={<Migrate />} />
|
<Route index element={<Migrate />} />
|
||||||
<Route path="manga/:mangaId/search" element={<SearchAll />} />
|
<Route path="manga/:mangaId/search" element={<SearchAll />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="tracker/login/oauth" element={<TrackerOAuthLogin />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Container>
|
</Container>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
|||||||
150
src/components/tracker/SettingsTrackerCard.tsx
Normal file
150
src/components/tracker/SettingsTrackerCard.tsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useTranslation } from 'react-i18next';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import PopupState, { bindDialog, bindTrigger } from 'material-ui-popup-state';
|
||||||
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
|
import { Chip, ListItemAvatar, ListItemSecondaryAction } from '@mui/material';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import { PasswordTextField } from '@/components/atoms/PasswordTextField.tsx';
|
||||||
|
import { makeToast } from '@/components/util/Toast.tsx';
|
||||||
|
import { GetTrackersQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
|
||||||
|
export const SettingsTrackerCard = ({ tracker }: { tracker: GetTrackersQuery['trackers']['nodes'][number] }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [loginTrackerCredentials, { loading: isCredentialLoginInProgress }] =
|
||||||
|
requestManager.useLoginToTrackerCredentials();
|
||||||
|
const [logoutFromTracker] = requestManager.useLogoutFromTracker();
|
||||||
|
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
|
const isOAuthLogin = !tracker.isLoggedIn && !!tracker.authUrl;
|
||||||
|
|
||||||
|
const handleLogout = async () => {
|
||||||
|
try {
|
||||||
|
await logoutFromTracker({ variables: { trackerId: tracker.id } });
|
||||||
|
} catch (e) {
|
||||||
|
makeToast(t('tracking.action.logout.label.failure', { name: tracker.name }), 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
if (isOAuthLogin) {
|
||||||
|
const state = {
|
||||||
|
redirectUrl: `${window.location.origin}/tracker/login/oauth`,
|
||||||
|
clientName: 'Suwayomi-WebUI',
|
||||||
|
trackerId: tracker.id,
|
||||||
|
trackerName: tracker.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
window.open(`${tracker.authUrl}&state=${JSON.stringify(state)}`, '_self');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await loginTrackerCredentials({ variables: { input: { trackerId: tracker.id, username, password } } });
|
||||||
|
} catch (e) {
|
||||||
|
makeToast(t('tracking.action.login.label.failure', { name: tracker.name }), 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopupState variant="popover" popupId="tracker-dialog">
|
||||||
|
{(popupState) => (
|
||||||
|
<>
|
||||||
|
<ListItemButton
|
||||||
|
{...bindTrigger(popupState)}
|
||||||
|
onClick={() => {
|
||||||
|
if (!isOAuthLogin) {
|
||||||
|
popupState.open();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogin();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemAvatar sx={{ paddingRight: '20px' }}>
|
||||||
|
<Avatar
|
||||||
|
alt={`${tracker.name}`}
|
||||||
|
src={requestManager.getValidImgUrlFor(tracker.icon)}
|
||||||
|
variant="rounded"
|
||||||
|
sx={{ width: 64, height: 64 }}
|
||||||
|
/>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText primary={tracker.name} />
|
||||||
|
{tracker.isLoggedIn && (
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<Chip label={t('global.label.logged_in')} color="success" />
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
)}
|
||||||
|
</ListItemButton>
|
||||||
|
<Dialog
|
||||||
|
{...bindDialog(popupState)}
|
||||||
|
open={(tracker.isLoggedIn || !tracker.authUrl) && popupState.isOpen}
|
||||||
|
disableRestoreFocus
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
{t(
|
||||||
|
tracker.isLoggedIn
|
||||||
|
? 'tracking.settings.dialog.title.log_out'
|
||||||
|
: 'tracking.settings.dialog.title.log_in',
|
||||||
|
{ name: tracker.name },
|
||||||
|
)}
|
||||||
|
</DialogTitle>
|
||||||
|
{!isOAuthLogin && !tracker.isLoggedIn && (
|
||||||
|
<DialogContent>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
margin="dense"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
label={t('global.label.username')}
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<PasswordTextField
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
)}
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={popupState.close}>{t('global.button.cancel')}</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
disabled={
|
||||||
|
!isOAuthLogin &&
|
||||||
|
!tracker.isLoggedIn &&
|
||||||
|
(isCredentialLoginInProgress || !username.length || !password.length)
|
||||||
|
}
|
||||||
|
onClick={() => (tracker.isLoggedIn ? handleLogout() : handleLogin())}
|
||||||
|
>
|
||||||
|
{t(tracker.isLoggedIn ? 'global.button.log_out' : 'global.button.log_in')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</PopupState>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -302,6 +302,8 @@
|
|||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"latest": "Latest",
|
"latest": "Latest",
|
||||||
|
"log_in": "Log in",
|
||||||
|
"log_out": "Log out",
|
||||||
"migrate": "Migrate",
|
"migrate": "Migrate",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"open_site": "Open Site",
|
"open_site": "Open Site",
|
||||||
@@ -374,6 +376,7 @@
|
|||||||
"links": "Links",
|
"links": "Links",
|
||||||
"load_in_progress": "Still loading required data…",
|
"load_in_progress": "Still loading required data…",
|
||||||
"loading": "Loading…",
|
"loading": "Loading…",
|
||||||
|
"logged_in": "Logged in",
|
||||||
"never": "Never",
|
"never": "Never",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"other": "Other",
|
"other": "Other",
|
||||||
@@ -1045,6 +1048,34 @@
|
|||||||
"title_one": "Source",
|
"title_one": "Source",
|
||||||
"title_other": "Sources"
|
"title_other": "Sources"
|
||||||
},
|
},
|
||||||
|
"tracking": {
|
||||||
|
"action": {
|
||||||
|
"login": {
|
||||||
|
"label": {
|
||||||
|
"failure": "Could not log in to {{name}}",
|
||||||
|
"progress": "Logging in to {{name}}…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logout": {
|
||||||
|
"label": {
|
||||||
|
"failure": "Could not log out from {{name}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"dialog": {
|
||||||
|
"title": {
|
||||||
|
"log_in": "Log in to {{name}}",
|
||||||
|
"log_out": "Log out from {{name}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"settings": "Tracking settings",
|
||||||
|
"trackers": "Trackers"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Tracking"
|
||||||
|
},
|
||||||
"updates": {
|
"updates": {
|
||||||
"error": {
|
"error": {
|
||||||
"label": {
|
"label": {
|
||||||
|
|||||||
@@ -2904,6 +2904,27 @@ export type UpdateSourcePreferencesMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type UpdateSourcePreferencesMutation = { __typename?: 'Mutation', updateSourcePreference: { __typename?: 'UpdateSourcePreferencePayload', clientMutationId?: string | null, source: { __typename?: 'SourceType', id: any, preferences: Array<{ __typename?: 'CheckBoxPreference', summary?: string | null, key: string, type: 'CheckBoxPreference', CheckBoxCheckBoxCurrentValue?: boolean | null, CheckBoxDefault: boolean, CheckBoxTitle: string } | { __typename?: 'EditTextPreference', text?: string | null, summary?: string | null, key: string, dialogTitle?: string | null, dialogMessage?: string | null, type: 'EditTextPreference', EditTextPreferenceCurrentValue?: string | null, EditTextPreferenceDefault?: string | null, EditTextPreferenceTitle?: string | null } | { __typename?: 'ListPreference', summary?: string | null, key: string, entryValues: Array<string>, entries: Array<string>, type: 'ListPreference', ListPreferenceCurrentValue?: string | null, ListPreferenceDefault?: string | null, ListPreferenceTitle?: string | null } | { __typename?: 'MultiSelectListPreference', dialogMessage?: string | null, dialogTitle?: string | null, summary?: string | null, key: string, entryValues: Array<string>, entries: Array<string>, type: 'MultiSelectListPreference', MultiSelectListPreferenceTitle?: string | null, MultiSelectListPreferenceDefault?: Array<string> | null, MultiSelectListPreferenceCurrentValue?: Array<string> | null } | { __typename?: 'SwitchPreference', summary?: string | null, key: string, type: 'SwitchPreference', SwitchPreferenceCurrentValue?: boolean | null, SwitchPreferenceDefault: boolean, SwitchPreferenceTitle: string }> } } };
|
export type UpdateSourcePreferencesMutation = { __typename?: 'Mutation', updateSourcePreference: { __typename?: 'UpdateSourcePreferencePayload', clientMutationId?: string | null, source: { __typename?: 'SourceType', id: any, preferences: Array<{ __typename?: 'CheckBoxPreference', summary?: string | null, key: string, type: 'CheckBoxPreference', CheckBoxCheckBoxCurrentValue?: boolean | null, CheckBoxDefault: boolean, CheckBoxTitle: string } | { __typename?: 'EditTextPreference', text?: string | null, summary?: string | null, key: string, dialogTitle?: string | null, dialogMessage?: string | null, type: 'EditTextPreference', EditTextPreferenceCurrentValue?: string | null, EditTextPreferenceDefault?: string | null, EditTextPreferenceTitle?: string | null } | { __typename?: 'ListPreference', summary?: string | null, key: string, entryValues: Array<string>, entries: Array<string>, type: 'ListPreference', ListPreferenceCurrentValue?: string | null, ListPreferenceDefault?: string | null, ListPreferenceTitle?: string | null } | { __typename?: 'MultiSelectListPreference', dialogMessage?: string | null, dialogTitle?: string | null, summary?: string | null, key: string, entryValues: Array<string>, entries: Array<string>, type: 'MultiSelectListPreference', MultiSelectListPreferenceTitle?: string | null, MultiSelectListPreferenceDefault?: Array<string> | null, MultiSelectListPreferenceCurrentValue?: Array<string> | null } | { __typename?: 'SwitchPreference', summary?: string | null, key: string, type: 'SwitchPreference', SwitchPreferenceCurrentValue?: boolean | null, SwitchPreferenceDefault: boolean, SwitchPreferenceTitle: string }> } } };
|
||||||
|
|
||||||
|
export type TrackerLoginOauthMutationVariables = Exact<{
|
||||||
|
input: LoginTrackerOAuthInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type TrackerLoginOauthMutation = { __typename?: 'Mutation', loginTrackerOAuth: { __typename?: 'LoginTrackerOAuthPayload', tracker: { __typename?: 'TrackerType', id: number, isLoggedIn: boolean, authUrl?: string | null } } };
|
||||||
|
|
||||||
|
export type TrackerLoginCredentialsMutationVariables = Exact<{
|
||||||
|
input: LoginTrackerCredentialsInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type TrackerLoginCredentialsMutation = { __typename?: 'Mutation', loginTrackerCredentials: { __typename?: 'LoginTrackerCredentialsPayload', isLoggedIn: boolean, tracker: { __typename?: 'TrackerType', id: number, isLoggedIn: boolean, authUrl?: string | null } } };
|
||||||
|
|
||||||
|
export type TrackerLogoutMutationVariables = Exact<{
|
||||||
|
trackerId: Scalars['Int']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type TrackerLogoutMutation = { __typename?: 'Mutation', logoutTracker: { __typename?: 'LogoutTrackerPayload', clientMutationId?: string | null, tracker: { __typename?: 'TrackerType', id: number, isLoggedIn: boolean, authUrl?: string | null } } };
|
||||||
|
|
||||||
export type UpdateCategoryMangasMutationVariables = Exact<{
|
export type UpdateCategoryMangasMutationVariables = Exact<{
|
||||||
input: UpdateCategoryMangaInput;
|
input: UpdateCategoryMangaInput;
|
||||||
}>;
|
}>;
|
||||||
@@ -3129,6 +3150,11 @@ export type GetMigratableSourcesQueryVariables = Exact<{ [key: string]: never; }
|
|||||||
|
|
||||||
export type GetMigratableSourcesQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', nodes: Array<{ __typename?: 'MangaType', sourceId: any, source?: { __typename?: 'SourceType', id: any, name: string, lang: string, iconUrl: string } | null }> } };
|
export type GetMigratableSourcesQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', nodes: Array<{ __typename?: 'MangaType', sourceId: any, source?: { __typename?: 'SourceType', id: any, name: string, lang: string, iconUrl: string } | null }> } };
|
||||||
|
|
||||||
|
export type GetTrackersQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetTrackersQuery = { __typename?: 'Query', trackers: { __typename?: 'TrackerNodeList', totalCount: number, pageInfo: { __typename?: 'PageInfo', endCursor?: any | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: any | null }, nodes: Array<{ __typename?: 'TrackerType', id: number, name: string, authUrl?: string | null, icon: string, isLoggedIn: boolean }> } };
|
||||||
|
|
||||||
export type GetUpdateStatusQueryVariables = Exact<{ [key: string]: never; }>;
|
export type GetUpdateStatusQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
47
src/lib/graphql/mutations/TrackerMutation.ts
Normal file
47
src/lib/graphql/mutations/TrackerMutation.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const TRACKER_LOGIN_OAUTH = gql`
|
||||||
|
mutation TRACKER_LOGIN_OAUTH($input: LoginTrackerOAuthInput!) {
|
||||||
|
loginTrackerOAuth(input: $input) {
|
||||||
|
tracker {
|
||||||
|
id
|
||||||
|
isLoggedIn
|
||||||
|
authUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TRACKER_LOGIN_CREDENTIALS = gql`
|
||||||
|
mutation TRACKER_LOGIN_CREDENTIALS($input: LoginTrackerCredentialsInput!) {
|
||||||
|
loginTrackerCredentials(input: $input) {
|
||||||
|
isLoggedIn
|
||||||
|
tracker {
|
||||||
|
id
|
||||||
|
isLoggedIn
|
||||||
|
authUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TRACKER_LOGOUT = gql`
|
||||||
|
mutation TRACKER_LOGOUT($trackerId: Int!) {
|
||||||
|
logoutTracker(input: { trackerId: $trackerId }) {
|
||||||
|
clientMutationId
|
||||||
|
tracker {
|
||||||
|
id
|
||||||
|
isLoggedIn
|
||||||
|
authUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
29
src/lib/graphql/queries/TrackerQuery.ts
Normal file
29
src/lib/graphql/queries/TrackerQuery.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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 gql from 'graphql-tag';
|
||||||
|
import { PAGE_INFO } from '@/lib/graphql/Fragments.ts';
|
||||||
|
|
||||||
|
export const GET_TRACKERS = gql`
|
||||||
|
${PAGE_INFO}
|
||||||
|
query GET_TRACKERS {
|
||||||
|
trackers {
|
||||||
|
totalCount
|
||||||
|
pageInfo {
|
||||||
|
...PAGE_INFO
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
authUrl
|
||||||
|
icon
|
||||||
|
isLoggedIn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -177,6 +177,14 @@ import {
|
|||||||
GetMangaToMigrateQueryVariables,
|
GetMangaToMigrateQueryVariables,
|
||||||
GetMangaToMigrateToFetchMutation,
|
GetMangaToMigrateToFetchMutation,
|
||||||
GetMangaToMigrateToFetchMutationVariables,
|
GetMangaToMigrateToFetchMutationVariables,
|
||||||
|
GetTrackersQuery,
|
||||||
|
GetTrackersQueryVariables,
|
||||||
|
TrackerLogoutMutation,
|
||||||
|
TrackerLogoutMutationVariables,
|
||||||
|
TrackerLoginOauthMutation,
|
||||||
|
TrackerLoginOauthMutationVariables,
|
||||||
|
TrackerLoginCredentialsMutation,
|
||||||
|
TrackerLoginCredentialsMutationVariables,
|
||||||
} from '@/lib/graphql/generated/graphql.ts';
|
} from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { GET_GLOBAL_METADATAS } from '@/lib/graphql/queries/GlobalMetadataQuery.ts';
|
import { GET_GLOBAL_METADATAS } from '@/lib/graphql/queries/GlobalMetadataQuery.ts';
|
||||||
import { SET_GLOBAL_METADATA } from '@/lib/graphql/mutations/GlobalMetadataMutation.ts';
|
import { SET_GLOBAL_METADATA } from '@/lib/graphql/mutations/GlobalMetadataMutation.ts';
|
||||||
@@ -257,6 +265,12 @@ import { WEBUI_UPDATE_SUBSCRIPTION } from '@/lib/graphql/subscriptions/ServerInf
|
|||||||
import { GET_DOWNLOAD_STATUS } from '@/lib/graphql/queries/DownloaderQuery.ts';
|
import { GET_DOWNLOAD_STATUS } from '@/lib/graphql/queries/DownloaderQuery.ts';
|
||||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||||
import { Queue, QueuePriority } from '@/lib/Queue.ts';
|
import { Queue, QueuePriority } from '@/lib/Queue.ts';
|
||||||
|
import { GET_TRACKERS } from '@/lib/graphql/queries/TrackerQuery.ts';
|
||||||
|
import {
|
||||||
|
TRACKER_LOGIN_CREDENTIALS,
|
||||||
|
TRACKER_LOGIN_OAUTH,
|
||||||
|
TRACKER_LOGOUT,
|
||||||
|
} from '@/lib/graphql/mutations/TrackerMutation.ts';
|
||||||
|
|
||||||
enum GQLMethod {
|
enum GQLMethod {
|
||||||
QUERY = 'QUERY',
|
QUERY = 'QUERY',
|
||||||
@@ -2359,6 +2373,30 @@ export class RequestManager {
|
|||||||
): AbortableApolloUseQueryResponse<GetMigratableSourcesQuery, GetMigratableSourcesQueryVariables> {
|
): AbortableApolloUseQueryResponse<GetMigratableSourcesQuery, GetMigratableSourcesQueryVariables> {
|
||||||
return this.doRequest(GQLMethod.USE_QUERY, GET_MIGRATABLE_SOURCES, undefined, options);
|
return this.doRequest(GQLMethod.USE_QUERY, GET_MIGRATABLE_SOURCES, undefined, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public useGetTrackerList(
|
||||||
|
options?: QueryHookOptions<GetTrackersQuery, GetTrackersQueryVariables>,
|
||||||
|
): AbortableApolloUseQueryResponse<GetTrackersQuery, GetTrackersQueryVariables> {
|
||||||
|
return this.doRequest(GQLMethod.USE_QUERY, GET_TRACKERS, undefined, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public useLogoutFromTracker(
|
||||||
|
options?: MutationHookOptions<TrackerLogoutMutation, TrackerLogoutMutationVariables>,
|
||||||
|
): AbortableApolloUseMutationResponse<TrackerLogoutMutation, TrackerLogoutMutationVariables> {
|
||||||
|
return this.doRequest(GQLMethod.USE_MUTATION, TRACKER_LOGOUT, undefined, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public useLoginToTrackerOauth(
|
||||||
|
options?: MutationHookOptions<TrackerLoginOauthMutation, TrackerLoginOauthMutationVariables>,
|
||||||
|
): AbortableApolloUseMutationResponse<TrackerLoginOauthMutation, TrackerLoginOauthMutationVariables> {
|
||||||
|
return this.doRequest(GQLMethod.USE_MUTATION, TRACKER_LOGIN_OAUTH, undefined, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public useLoginToTrackerCredentials(
|
||||||
|
options?: MutationHookOptions<TrackerLoginCredentialsMutation, TrackerLoginCredentialsMutationVariables>,
|
||||||
|
): AbortableApolloUseMutationResponse<TrackerLoginCredentialsMutation, TrackerLoginCredentialsMutationVariables> {
|
||||||
|
return this.doRequest(GQLMethod.USE_MUTATION, TRACKER_LOGIN_CREDENTIALS, undefined, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestManager = new RequestManager();
|
export const requestManager = new RequestManager();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import WebIcon from '@mui/icons-material/Web';
|
|||||||
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||||
import ExploreOutlinedIcon from '@mui/icons-material/ExploreOutlined';
|
import ExploreOutlinedIcon from '@mui/icons-material/ExploreOutlined';
|
||||||
import DevicesIcon from '@mui/icons-material/Devices';
|
import DevicesIcon from '@mui/icons-material/Devices';
|
||||||
|
import SyncIcon from '@mui/icons-material/Sync';
|
||||||
import { langCodeToName } from '@/util/language';
|
import { langCodeToName } from '@/util/language';
|
||||||
import { useLocalStorage } from '@/util/useLocalStorage';
|
import { useLocalStorage } from '@/util/useLocalStorage';
|
||||||
import { ListItemLink } from '@/components/util/ListItemLink';
|
import { ListItemLink } from '@/components/util/ListItemLink';
|
||||||
@@ -94,6 +95,12 @@ export function Settings() {
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={t('download.title')} />
|
<ListItemText primary={t('download.title')} />
|
||||||
</ListItemLink>
|
</ListItemLink>
|
||||||
|
<ListItemLink to="/settings/trackingSettings">
|
||||||
|
<ListItemIcon>
|
||||||
|
<SyncIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary={t('tracking.title')} />
|
||||||
|
</ListItemLink>
|
||||||
<ListItemLink to="/settings/backup">
|
<ListItemLink to="/settings/backup">
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<BackupIcon />
|
<BackupIcon />
|
||||||
|
|||||||
52
src/screens/TrackerOAuthLogin.tsx
Normal file
52
src/screens/TrackerOAuthLogin.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
import { makeToast } from '@/components/util/Toast.tsx';
|
||||||
|
|
||||||
|
export const TrackerOAuthLogin = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
const { trackerId, trackerName }: { trackerId: number; trackerName: string } = JSON.parse(
|
||||||
|
url.searchParams.get('state') ?? '{}',
|
||||||
|
);
|
||||||
|
|
||||||
|
const [loginTrackerOAuth, { loading: isLoginInProgress }] = requestManager.useLoginToTrackerOauth();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const login = async () => {
|
||||||
|
try {
|
||||||
|
await loginTrackerOAuth({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
callbackUrl: window.location.href,
|
||||||
|
trackerId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
makeToast(t('tracking.action.login.label.failure', { name: trackerName }), 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate('/settings/trackingSettings', { replace: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
login();
|
||||||
|
}, [trackerId]);
|
||||||
|
|
||||||
|
if (isLoginInProgress) {
|
||||||
|
return t('tracking.action.login.label.progress', { name: trackerName });
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
52
src/screens/settings/TrackingSettings.tsx
Normal file
52
src/screens/settings/TrackingSettings.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useTranslation } from 'react-i18next';
|
||||||
|
import { useContext, useEffect } from 'react';
|
||||||
|
import List from '@mui/material/List';
|
||||||
|
|
||||||
|
import ListSubheader from '@mui/material/ListSubheader';
|
||||||
|
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
import { EmptyView } from '@/components/util/EmptyView.tsx';
|
||||||
|
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
|
||||||
|
import { SettingsTrackerCard } from '@/components/tracker/SettingsTrackerCard.tsx';
|
||||||
|
|
||||||
|
export const TrackingSettings = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { setTitle } = useContext(NavBarContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle(t('tracking.settings.title.settings'));
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
|
const { data, loading, error } = requestManager.useGetTrackerList();
|
||||||
|
const trackers = data?.trackers.nodes ?? [];
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <EmptyView message={error.message ?? error} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <LoadingPlaceholder />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
subheader={
|
||||||
|
<ListSubheader component="div" id="tracking-trackers">
|
||||||
|
{t('tracking.settings.title.trackers')}
|
||||||
|
</ListSubheader>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{trackers.map((tracker) => (
|
||||||
|
<SettingsTrackerCard key={tracker.id} tracker={tracker} />
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user