Skip to content

Commit

Permalink
Merge pull request #125 from Suwayomi/main
Browse files Browse the repository at this point in the history
manually add external extension button
  • Loading branch information
Robonau authored Jan 10, 2024
2 parents b73570e + 434c026 commit 68d3d96
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 109 deletions.
19 changes: 19 additions & 0 deletions src/gql/Mutations.gql
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ mutation updateExtension(
isNsfw
extension {
pkgName
repo
}
}
}
Expand Down Expand Up @@ -412,3 +413,21 @@ mutation clearCachedImages {
clientMutationId
}
}

mutation installExternalExtension($extensionFile: Upload!) {
installExternalExtension(input: { extensionFile: $extensionFile }) {
extension {
...ExtensionTypeFragment
source {
nodes {
...SourceTypeFragment
isNsfw
extension {
pkgName
repo
}
}
}
}
}
}
1 change: 1 addition & 0 deletions src/gql/Queries.gql
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ query sources($isNsfw: Boolean = null) {
isNsfw
extension {
pkgName
repo
}
}
}
Expand Down
45 changes: 43 additions & 2 deletions src/lib/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2232,7 +2232,7 @@ export type UpdateExtensionMutationVariables = Exact<{
}>;


export type UpdateExtensionMutation = { __typename?: 'Mutation', updateExtension: { __typename?: 'UpdateExtensionPayload', extension?: { __typename?: 'ExtensionType', name: string, repo?: string | null, versionName: string, pkgName: string, lang: string, iconUrl: string, isNsfw: boolean, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean, source: { __typename?: 'SourceNodeList', nodes: Array<{ __typename?: 'SourceType', isNsfw: boolean, id: any, displayName: string, iconUrl: string, lang: string, extension: { __typename?: 'ExtensionType', pkgName: string } }> } } | null } };
export type UpdateExtensionMutation = { __typename?: 'Mutation', updateExtension: { __typename?: 'UpdateExtensionPayload', extension?: { __typename?: 'ExtensionType', name: string, repo?: string | null, versionName: string, pkgName: string, lang: string, iconUrl: string, isNsfw: boolean, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean, source: { __typename?: 'SourceNodeList', nodes: Array<{ __typename?: 'SourceType', isNsfw: boolean, id: any, displayName: string, iconUrl: string, lang: string, extension: { __typename?: 'ExtensionType', pkgName: string, repo?: string | null } }> } } | null } };

export type FetchSourceMangaMutationVariables = Exact<{
page: Scalars['Int']['input'];
Expand Down Expand Up @@ -2400,6 +2400,13 @@ export type ClearCachedImagesMutationVariables = Exact<{ [key: string]: never; }

export type ClearCachedImagesMutation = { __typename?: 'Mutation', clearCachedImages: { __typename?: 'ClearCachedImagesPayload', clientMutationId?: string | null } };

export type InstallExternalExtensionMutationVariables = Exact<{
extensionFile: Scalars['Upload']['input'];
}>;


export type InstallExternalExtensionMutation = { __typename?: 'Mutation', installExternalExtension: { __typename?: 'InstallExternalExtensionPayload', extension: { __typename?: 'ExtensionType', name: string, repo?: string | null, versionName: string, pkgName: string, lang: string, iconUrl: string, isNsfw: boolean, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean, source: { __typename?: 'SourceNodeList', nodes: Array<{ __typename?: 'SourceType', isNsfw: boolean, id: any, displayName: string, iconUrl: string, lang: string, extension: { __typename?: 'ExtensionType', pkgName: string, repo?: string | null } }> } } } };

export type CategoriesQueryVariables = Exact<{
notEqualTo?: InputMaybe<Scalars['Int']['input']>;
}>;
Expand Down Expand Up @@ -2440,7 +2447,7 @@ export type SourcesQueryVariables = Exact<{
}>;


export type SourcesQuery = { __typename?: 'Query', sources: { __typename?: 'SourceNodeList', nodes: Array<{ __typename?: 'SourceType', isNsfw: boolean, id: any, displayName: string, iconUrl: string, lang: string, extension: { __typename?: 'ExtensionType', pkgName: string } }> } };
export type SourcesQuery = { __typename?: 'Query', sources: { __typename?: 'SourceNodeList', nodes: Array<{ __typename?: 'SourceType', isNsfw: boolean, id: any, displayName: string, iconUrl: string, lang: string, extension: { __typename?: 'ExtensionType', pkgName: string, repo?: string | null } }> } };

export type SourcesMigrationQueryVariables = Exact<{ [key: string]: never; }>;

Expand Down Expand Up @@ -2749,6 +2756,7 @@ export const UpdateExtensionDoc = gql`
isNsfw
extension {
pkgName
repo
}
}
}
Expand Down Expand Up @@ -3018,6 +3026,26 @@ export const ClearCachedImagesDoc = gql`
}
}
`;
export const InstallExternalExtensionDoc = gql`
mutation installExternalExtension($extensionFile: Upload!) {
installExternalExtension(input: {extensionFile: $extensionFile}) {
extension {
...ExtensionTypeFragment
source {
nodes {
...SourceTypeFragment
isNsfw
extension {
pkgName
repo
}
}
}
}
}
}
${ExtensionTypeFragmentFragmentDoc}
${SourceTypeFragmentFragmentDoc}`;
export const CategoriesDoc = gql`
query categories($notEqualTo: Int = null) {
categories(filter: {id: {notEqualTo: $notEqualTo}}) {
Expand Down Expand Up @@ -3087,6 +3115,7 @@ export const SourcesDoc = gql`
isNsfw
extension {
pkgName
repo
}
}
}
Expand Down Expand Up @@ -3830,6 +3859,18 @@ export const clearCachedImages = (
});
return m;
}
export const installExternalExtension = (
options: Omit<
MutationOptions<any, InstallExternalExtensionMutationVariables>,
"mutation"
>
) => {
const m = client.mutate<InstallExternalExtensionMutation, InstallExternalExtensionMutationVariables>({
mutation: InstallExternalExtensionDoc,
...options,
});
return m;
}
export const categories = (
options: Omit<
WatchQueryOptions<CategoriesQueryVariables>,
Expand Down
37 changes: 11 additions & 26 deletions src/routes/(app)/browse/extensions/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@
<script lang="ts">
import { AppBarData } from '$lib/MountTitleAction';
import {
ExtensionsDoc,
fetchExtensions,
extensions as getExtensions,
type Exact,
type ExtensionsQuery,
type FetchExtensionsMutation,
type InputMaybe
} from '$lib/generated';
import { ErrorHelp, Partition, groupBy } from '$lib/util';
Expand All @@ -24,10 +22,9 @@
import Nav from '../Nav.svelte';
import { FindLangName } from '../languages';
import ExtensionsActions from './ExtensionsActions.svelte';
import { langFilter, lastFetched } from './ExtensionsStores';
import { fetchExtensionsUpdater, langFilter, lastFetched } from './ExtensionsStores';
import ExtensionCard from './ExtensionCard.svelte';
import { Meta } from '$lib/simpleStores';
import type { ApolloCache, FetchResult } from '@apollo/client';
const query = queryParam('q', ssp.string(), { pushHistory: false });
type TExtension = ExtensionsQuery['extensions']['nodes'][0];
Expand All @@ -46,33 +43,21 @@
checkIfFetchNewExtensions();
async function checkIfFetchNewExtensions() {
await ErrorHelp(
'failed to fetch new extensions',
fetchExtensions({
update: fetchExtensionsUpdater
})
);
if ($lastFetched.getTime() > new Date().getTime() - 60000) {
await ErrorHelp(
'failed to fetch new extensions',
fetchExtensions({
update: fetchExtensionsUpdater
})
);
$lastFetched = new Date();
}
extensions = getExtensions({
variables: { isNsfw: $Meta.nsfw ? null : false },
fetchPolicy: 'cache-first'
});
}
function fetchExtensionsUpdater(
cache: ApolloCache<unknown>,
{ data }: FetchResult<FetchExtensionsMutation>
) {
if (!data) return;
let filteredExtensions = data.fetchExtensions.extensions;
if (!$Meta.nsfw) filteredExtensions = filteredExtensions.filter((e) => !e.isNsfw);
cache.writeQuery({
query: ExtensionsDoc,
data: { extensions: { nodes: filteredExtensions } },
variables: { isNsfw: $Meta.nsfw ? null : false }
});
$lastFetched = new Date();
}
$: langs = getLangs($extensions?.data);
$: AppBarData('Extensions', { component: ExtensionsActions, props: { langs } });
Expand Down Expand Up @@ -156,7 +141,7 @@
<h2 class="h2 p-2">
{FindLangName(lang)}
</h2>
{#each sause as ext (ext.pkgName)}
{#each sause as ext (ext.pkgName + ext.repo)}
<ExtensionCard {ext} {scrollingElement} />
{/each}
{/each}
Expand Down
102 changes: 22 additions & 80 deletions src/routes/(app)/browse/extensions/ExtensionCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,125 +10,67 @@
import IntersectionObserver from '$lib/components/IntersectionObserver.svelte';
import Image from '$lib/components/Image.svelte';
import {
ExtensionsDoc,
SourcesDoc,
updateExtension,
type ExtensionsQuery,
type SourcesQuery,
type UpdateExtensionMutation
} from '$lib/generated';
import type { ApolloCache, FetchResult } from '@apollo/client';
import { ErrorHelp } from '$lib/util';
import { ProgressRadial } from '@skeletonlabs/skeleton';
import { Meta } from '$lib/simpleStores';
import { UpdateExtensionUpdater } from './ExtensionsStores';
import type { ApolloCache, FetchResult } from '@apollo/client';
export let scrollingElement: HTMLDivElement;
export let ext: ExtensionsQuery['extensions']['nodes'][0];
function preUpdater(
cache: ApolloCache<unknown>,
{ data }: Omit<FetchResult<UpdateExtensionMutation>, 'context'>
) {
UpdateExtensionUpdater(cache, { data }, ext);
}
let loadingUpdate = false;
let loadingInstall = false;
let loadingUnInstall = false;
function UpdateExtensionUpdater(
cache: ApolloCache<unknown>,
{ data }: Omit<FetchResult<UpdateExtensionMutation>, 'context'>,
pkgName: string
): void {
if (!data) return;
try {
const extensionsData = structuredClone(
cache.readQuery<ExtensionsQuery>({
query: ExtensionsDoc,
variables: { isNsfw: $Meta.nsfw ? null : false }
})
);
if (!extensionsData) throw new Error('failed to read extensions');
const { extensions } = extensionsData;
if (data.updateExtension.extension)
extensions.nodes[extensions.nodes.findIndex((extension) => extension.pkgName === pkgName)] =
data.updateExtension.extension;
cache.writeQuery({
query: ExtensionsDoc,
data: { extensions },
variables: { isNsfw: $Meta.nsfw ? null : false }
});
} catch {}
const sourcesData = structuredClone(
cache.readQuery<SourcesQuery>({
query: SourcesDoc,
variables: { isNsfw: $Meta.nsfw ? null : false }
})
);
if (!sourcesData) return;
const { sources } = sourcesData;
if (data.updateExtension.extension?.isInstalled) {
const sourcesToPush: SourcesQuery['sources']['nodes'] = [];
data.updateExtension.extension.source.nodes.forEach((source) => {
if (!sources.nodes.find((existingSource) => existingSource.id === source.id)) {
sourcesToPush.push(source);
}
});
sources.nodes.push(...sourcesToPush);
} else {
sources.nodes = sources.nodes.filter(
(existingSource) => existingSource.extension.pkgName !== pkgName
);
}
cache.writeQuery({
query: SourcesDoc,
variables: { isNsfw: $Meta.nsfw ? null : false },
data: { sources }
});
}
async function unInstall(pkgName: string) {
async function unInstall() {
loadingUnInstall = true;
try {
await ErrorHelp(
'failed to Uninstall extension',
updateExtension({
variables: { pkgName, uninstall: true },
update: (cache, data) => {
UpdateExtensionUpdater(cache, data, pkgName);
}
variables: { pkgName: ext.pkgName, uninstall: true },
update: preUpdater
})
);
} finally {
loadingUnInstall = false;
}
}
async function Install(pkgName: string) {
async function Install() {
loadingInstall = true;
try {
await ErrorHelp(
'Failed to Install extension',
updateExtension({
variables: { pkgName, install: true },
update: (cache, data) => {
UpdateExtensionUpdater(cache, data, pkgName);
}
variables: { pkgName: ext.pkgName, install: true },
update: preUpdater
})
);
} finally {
loadingInstall = false;
}
}
async function Update(pkgName: string) {
async function Update() {
loadingUpdate = true;
try {
await ErrorHelp(
'Failed to Update extension',
updateExtension({
variables: { pkgName, update: true },
update: (cache, data) => {
UpdateExtensionUpdater(cache, data, pkgName);
}
variables: { pkgName: ext.pkgName, update: true },
update: preUpdater
})
);
} finally {
Expand Down Expand Up @@ -163,7 +105,7 @@
</div>
<div class="flex flex-wrap flex-1 justify-end">
{#if ext.isObsolete}
<button on:click={() => unInstall(ext.pkgName)} class="btn variant-ghost-error m-1">
<button on:click={() => unInstall()} class="btn variant-ghost-error m-1">
{#if loadingUnInstall}
Uninstalling<ProgressRadial class="ml-1 h-4 aspect-square w-auto" />
{:else}
Expand All @@ -172,23 +114,23 @@
</button>
{:else if ext.isInstalled}
{#if ext.hasUpdate}
<button on:click={() => Update(ext.pkgName)} class="btn variant-ghost-surface m-1">
<button on:click={() => Update()} class="btn variant-ghost-surface m-1">
{#if loadingUpdate}
Updating<ProgressRadial class="ml-1 h-4 aspect-square w-auto" />
{:else}
Update
{/if}
</button>
{/if}
<button on:click={() => unInstall(ext.pkgName)} class="btn variant-ghost-surface m-1">
<button on:click={() => unInstall()} class="btn variant-ghost-surface m-1">
{#if loadingUnInstall}
Uninstalling<ProgressRadial class="ml-1 h-4 aspect-square w-auto" />
{:else}
Uninstall
{/if}
</button>
{:else}
<button on:click={() => Install(ext.pkgName)} class="btn variant-ghost-surface m-1">
<button on:click={() => Install()} class="btn variant-ghost-surface m-1">
{#if loadingInstall}
Installing<ProgressRadial class="ml-1 h-4 aspect-square w-auto" />
{:else}
Expand Down
Loading

0 comments on commit 68d3d96

Please sign in to comment.