feature: add mgraphql codegen
This commit is contained in:
110
apps/webui/src/infra/graphql/gql/fragment-masking.ts
Normal file
110
apps/webui/src/infra/graphql/gql/fragment-masking.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/* eslint-disable */
|
||||
import type {
|
||||
DocumentTypeDecoration,
|
||||
ResultOf,
|
||||
TypedDocumentNode,
|
||||
} from '@graphql-typed-document-node/core';
|
||||
import type { FragmentDefinitionNode } from 'graphql';
|
||||
import type { Incremental } from './graphql';
|
||||
|
||||
export type FragmentType<
|
||||
TDocumentType extends DocumentTypeDecoration<any, any>,
|
||||
> = TDocumentType extends DocumentTypeDecoration<infer TType, any>
|
||||
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
|
||||
? TKey extends string
|
||||
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
// return non-nullable if `fragmentType` is non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
|
||||
): TType;
|
||||
// return nullable if `fragmentType` is undefined
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined
|
||||
): TType | undefined;
|
||||
// return nullable if `fragmentType` is nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null
|
||||
): TType | null;
|
||||
// return nullable if `fragmentType` is nullable or undefined
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType:
|
||||
| FragmentType<DocumentTypeDecoration<TType, any>>
|
||||
| null
|
||||
| undefined
|
||||
): TType | null | undefined;
|
||||
// return array of non-nullable if `fragmentType` is array of non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
): Array<TType>;
|
||||
// return array of nullable if `fragmentType` is array of nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType:
|
||||
| Array<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
| null
|
||||
| undefined
|
||||
): Array<TType> | null | undefined;
|
||||
// return readonly array of non-nullable if `fragmentType` is array of non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
): ReadonlyArray<TType>;
|
||||
// return readonly array of nullable if `fragmentType` is array of nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType:
|
||||
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
| null
|
||||
| undefined
|
||||
): ReadonlyArray<TType> | null | undefined;
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType:
|
||||
| FragmentType<DocumentTypeDecoration<TType, any>>
|
||||
| Array<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
| null
|
||||
| undefined
|
||||
): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
|
||||
return fragmentType as any;
|
||||
}
|
||||
|
||||
export function makeFragmentData<
|
||||
F extends DocumentTypeDecoration<any, any>,
|
||||
FT extends ResultOf<F>,
|
||||
>(data: FT, _fragment: F): FragmentType<F> {
|
||||
return data as FragmentType<F>;
|
||||
}
|
||||
export function isFragmentReady<TQuery, TFrag>(
|
||||
queryNode: DocumentTypeDecoration<TQuery, any>,
|
||||
fragmentNode: TypedDocumentNode<TFrag>,
|
||||
data:
|
||||
| FragmentType<TypedDocumentNode<Incremental<TFrag>, any>>
|
||||
| null
|
||||
| undefined
|
||||
): data is FragmentType<typeof fragmentNode> {
|
||||
const deferredFields = (
|
||||
queryNode as {
|
||||
__meta__?: { deferredFields: Record<string, (keyof TFrag)[]> };
|
||||
}
|
||||
).__meta__?.deferredFields;
|
||||
|
||||
if (!deferredFields) return true;
|
||||
|
||||
const fragDef = fragmentNode.definitions[0] as
|
||||
| FragmentDefinitionNode
|
||||
| undefined;
|
||||
const fragName = fragDef?.name?.value;
|
||||
|
||||
const fields = (fragName && deferredFields[fragName]) || [];
|
||||
return fields.length > 0 && fields.every((field) => data && field in data);
|
||||
}
|
||||
59
apps/webui/src/infra/graphql/gql/gql.ts
Normal file
59
apps/webui/src/infra/graphql/gql/gql.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
/* eslint-disable */
|
||||
import * as types from './graphql';
|
||||
|
||||
/**
|
||||
* Map of all GraphQL operations in the project.
|
||||
*
|
||||
* This map has several performance disadvantages:
|
||||
* 1. It is not tree-shakeable, so it will include all operations in the project.
|
||||
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
|
||||
* 3. It does not support dead code elimination, so it will add unused operations.
|
||||
*
|
||||
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
||||
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
|
||||
*/
|
||||
type Documents = {
|
||||
'\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n': typeof types.CreateSubscriptionDocument;
|
||||
'\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n': typeof types.GetSubscriptionsDocument;
|
||||
};
|
||||
const documents: Documents = {
|
||||
'\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n':
|
||||
types.CreateSubscriptionDocument,
|
||||
'\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n':
|
||||
types.GetSubscriptionsDocument,
|
||||
};
|
||||
|
||||
/**
|
||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const query = gql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
|
||||
* ```
|
||||
*
|
||||
* The query argument is unknown!
|
||||
* Please regenerate the types.
|
||||
*/
|
||||
export function gql(source: string): unknown;
|
||||
|
||||
/**
|
||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function gql(
|
||||
source: '\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n'
|
||||
): (typeof documents)['\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n subscriberId\n }\n }\n'];
|
||||
/**
|
||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function gql(
|
||||
source: '\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n'
|
||||
): (typeof documents)['\n query GetSubscriptions($page: Int!, $pageSize: Int!) {\n subscriptions(\n pagination: {\n page: {\n page: $page,\n limit: $pageSize\n }\n }\n ) {\n nodes {\n id\n displayName\n category\n enabled\n bangumi {\n nodes {\n id\n displayName\n posterLink\n season\n fansub\n homepage\n }\n }\n }\n }\n }\n'];
|
||||
|
||||
export function gql(source: string) {
|
||||
return (documents as any)[source] ?? {};
|
||||
}
|
||||
|
||||
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> =
|
||||
TDocumentNode extends DocumentNode<infer TType, any> ? TType : never;
|
||||
1538
apps/webui/src/infra/graphql/gql/graphql.ts
Normal file
1538
apps/webui/src/infra/graphql/gql/graphql.ts
Normal file
File diff suppressed because it is too large
Load Diff
2
apps/webui/src/infra/graphql/gql/index.ts
Normal file
2
apps/webui/src/infra/graphql/gql/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './fragment-masking';
|
||||
export * from './gql';
|
||||
@@ -1,3 +1,7 @@
|
||||
import type { Injector } from '@outposts/injection-js';
|
||||
import { atomWithObservable } from 'jotai/utils';
|
||||
import { useInjector } from 'oidc-client-rx/adapters/react';
|
||||
import { useMemo } from 'react';
|
||||
import { ThemeService } from './theme.service';
|
||||
|
||||
export function provideStyles() {
|
||||
@@ -8,3 +12,37 @@ export function provideStyles() {
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function themeContextFromInjector(injector: Injector) {
|
||||
const themeService = injector.get(ThemeService);
|
||||
const systemColorSchema$ = atomWithObservable(
|
||||
() => themeService.systemColorSchema$
|
||||
);
|
||||
return {
|
||||
themeService,
|
||||
systemColorSchema$,
|
||||
};
|
||||
}
|
||||
|
||||
export function setupThemeContext(injector: Injector) {
|
||||
const { themeService } = themeContextFromInjector(injector);
|
||||
themeService.setup();
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const injector = useInjector();
|
||||
|
||||
const { themeService } = useMemo(() => {
|
||||
return themeContextFromInjector(injector);
|
||||
}, [injector]);
|
||||
|
||||
const colorTheme = useMemo(
|
||||
() => atomWithObservable(() => themeService.colorSchema$),
|
||||
[themeService]
|
||||
);
|
||||
|
||||
return {
|
||||
themeService,
|
||||
colorTheme,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import { DOCUMENT } from '@/infra/platform/injection';
|
||||
import { LocalStorageService } from '@/infra/storage/web-storage.service';
|
||||
import { Injectable, inject } from '@outposts/injection-js';
|
||||
|
||||
import {
|
||||
BehaviorSubject,
|
||||
ReplaySubject,
|
||||
combineLatest,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
fromEvent,
|
||||
map,
|
||||
shareReplay,
|
||||
startWith,
|
||||
} from 'rxjs';
|
||||
export type PreferColorSchemaType = 'dark' | 'light' | 'system';
|
||||
export type PreferColorSchemaClass = 'dark' | 'light';
|
||||
|
||||
@@ -9,6 +19,71 @@ export type PreferColorSchemaClass = 'dark' | 'light';
|
||||
export class ThemeService {
|
||||
document = inject(DOCUMENT);
|
||||
localStorage = inject(LocalStorageService);
|
||||
systemColorSchema$ = new BehaviorSubject(this.systemColorSchema);
|
||||
storageColorSchema$ = new BehaviorSubject(
|
||||
this.getColorSchemaType(this.localStorage.getItem('prefers-color-scheme'))
|
||||
);
|
||||
colorSchema$ = new BehaviorSubject(
|
||||
this.getColorSchemaByType(
|
||||
this.storageColorSchema$.value,
|
||||
this.systemColorSchema$.value
|
||||
)
|
||||
);
|
||||
|
||||
setup() {
|
||||
const mediaQuery = this.document.defaultView?.matchMedia(
|
||||
'(prefers-color-scheme: dark)'
|
||||
);
|
||||
|
||||
if (mediaQuery) {
|
||||
fromEvent(mediaQuery, 'change')
|
||||
.pipe(
|
||||
map(() => (mediaQuery.matches ? 'dark' : 'light')),
|
||||
startWith(this.systemColorSchema),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
.subscribe(this.systemColorSchema$);
|
||||
}
|
||||
|
||||
if (this.document.defaultView?.localStorage) {
|
||||
fromEvent(this.document.defaultView, 'storage')
|
||||
.pipe(
|
||||
filter(
|
||||
(e): e is StorageEvent =>
|
||||
(e as StorageEvent)?.key === 'prefers-color-scheme'
|
||||
),
|
||||
map((event) => this.getColorSchemaType(event.newValue)),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
.subscribe(this.storageColorSchema$);
|
||||
}
|
||||
|
||||
combineLatest({
|
||||
system: this.systemColorSchema$,
|
||||
storage: this.storageColorSchema$,
|
||||
})
|
||||
.pipe(
|
||||
map(({ system, storage }) => this.getColorSchemaByType(storage, system))
|
||||
)
|
||||
.subscribe(this.colorSchema$);
|
||||
}
|
||||
|
||||
private getColorSchemaType(themeType: string | null): PreferColorSchemaType {
|
||||
if (themeType === 'dark' || themeType === 'light') {
|
||||
return themeType as PreferColorSchemaType;
|
||||
}
|
||||
return 'system';
|
||||
}
|
||||
|
||||
private getColorSchemaByType(
|
||||
themeType: PreferColorSchemaType,
|
||||
systemColorSchema: PreferColorSchemaClass
|
||||
): PreferColorSchemaClass {
|
||||
if (themeType === 'dark' || themeType === 'light') {
|
||||
return themeType;
|
||||
}
|
||||
return systemColorSchema;
|
||||
}
|
||||
|
||||
get systemColorSchema(): PreferColorSchemaClass {
|
||||
return this.document.defaultView?.matchMedia('(prefers-color-scheme: dark)')
|
||||
@@ -17,24 +92,16 @@ export class ThemeService {
|
||||
: 'light';
|
||||
}
|
||||
|
||||
private getColorSchemaByType(
|
||||
themeType: PreferColorSchemaType
|
||||
): PreferColorSchemaClass {
|
||||
this.document.documentElement.classList.remove('dark', 'light');
|
||||
if (themeType === 'dark' || themeType === 'light') {
|
||||
return themeType;
|
||||
}
|
||||
return this.systemColorSchema;
|
||||
}
|
||||
|
||||
get colorSchema() {
|
||||
const theme = this.localStorage.getItem('prefers-color-scheme');
|
||||
return this.getColorSchemaByType(theme as PreferColorSchemaType);
|
||||
return this.colorSchema$.value;
|
||||
}
|
||||
|
||||
set colorSchema(themeType: PreferColorSchemaType) {
|
||||
this.localStorage.setItem('prefers-color-scheme', themeType);
|
||||
const themeClass = this.getColorSchemaByType(themeType);
|
||||
const themeClass = this.getColorSchemaByType(
|
||||
themeType,
|
||||
this.systemColorSchema
|
||||
);
|
||||
this.document.documentElement.classList.remove('dark', 'light');
|
||||
this.document.documentElement.classList.add(themeClass);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user