Feature/improve typing of metadata related logic (#268)
* Improve metadata typing * Rename types Still included the "I" prefix for interfaces * Improve typing in setting util files
This commit is contained in:
@@ -74,13 +74,13 @@ export interface IMetadataMigration {
|
|||||||
keys?: { oldKey: string; newKey: string }[];
|
keys?: { oldKey: string; newKey: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMetadata<VALUES extends AllowedMetadataValueTypes = string> {
|
export type Metadata<Keys extends string = string, Values = string> = {
|
||||||
[key: string]: VALUES;
|
[key in Keys]: Values;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface IMetadataHolder<VALUES extends AllowedMetadataValueTypes = string> {
|
export type MetadataHolder<Keys extends string = string, Values = string> = {
|
||||||
meta?: IMetadata<VALUES>;
|
meta?: Metadata<Keys, Values>;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AllowedMetadataValueTypes = string | boolean | number | undefined;
|
export type AllowedMetadataValueTypes = string | boolean | number | undefined;
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export interface IMangaCard {
|
|||||||
unreadCount?: number;
|
unreadCount?: number;
|
||||||
downloadCount?: number;
|
downloadCount?: number;
|
||||||
inLibrary?: boolean;
|
inLibrary?: boolean;
|
||||||
meta?: IMetadata;
|
meta?: Metadata;
|
||||||
inLibraryAt: number;
|
inLibraryAt: number;
|
||||||
lastReadAt: number;
|
lastReadAt: number;
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ export interface IManga {
|
|||||||
inLibrary: boolean;
|
inLibrary: boolean;
|
||||||
source: ISource;
|
source: ISource;
|
||||||
|
|
||||||
meta: IMetadata;
|
meta: Metadata;
|
||||||
|
|
||||||
realUrl: string;
|
realUrl: string;
|
||||||
freshData: boolean;
|
freshData: boolean;
|
||||||
@@ -158,7 +158,7 @@ export interface IChapter {
|
|||||||
chapterCount: number;
|
chapterCount: number;
|
||||||
pageCount: number;
|
pageCount: number;
|
||||||
downloaded: boolean;
|
downloaded: boolean;
|
||||||
meta: IMetadata;
|
meta: Metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMangaChapter {
|
export interface IMangaChapter {
|
||||||
@@ -178,7 +178,7 @@ export interface ICategory {
|
|||||||
order: number;
|
order: number;
|
||||||
name: string;
|
name: string;
|
||||||
default: boolean;
|
default: boolean;
|
||||||
meta: IMetadata;
|
meta: Metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INavbarOverride {
|
export interface INavbarOverride {
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
IManga,
|
IManga,
|
||||||
IMangaCard,
|
IMangaCard,
|
||||||
IMangaChapter,
|
IMangaChapter,
|
||||||
IMetadata,
|
Metadata,
|
||||||
IMetadataHolder,
|
MetadataHolder,
|
||||||
IMetadataMigration,
|
IMetadataMigration,
|
||||||
MetadataKeyValuePair,
|
MetadataKeyValuePair,
|
||||||
} from 'typings';
|
} from 'typings';
|
||||||
@@ -96,7 +96,7 @@ const getAppKeyPrefixForMigration = (migrationId: number): string => {
|
|||||||
|
|
||||||
const getMetadataKey = (key: string, appPrefix: string = APP_METADATA_KEY_PREFIX) => `${appPrefix}${key}`;
|
const getMetadataKey = (key: string, appPrefix: string = APP_METADATA_KEY_PREFIX) => `${appPrefix}${key}`;
|
||||||
|
|
||||||
const doesMetadataKeyExistIn = (meta: IMetadata | undefined, key: string, appPrefix?: string): boolean =>
|
const doesMetadataKeyExistIn = (meta: Metadata | undefined, key: string, appPrefix?: string): boolean =>
|
||||||
Object.prototype.hasOwnProperty.call(meta ?? {}, getMetadataKey(key, appPrefix));
|
Object.prototype.hasOwnProperty.call(meta ?? {}, getMetadataKey(key, appPrefix));
|
||||||
|
|
||||||
const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
|
const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
|
||||||
@@ -117,8 +117,8 @@ const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedM
|
|||||||
return value as T;
|
return value as T;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAppMetadataFrom = (meta: IMetadata, appPrefix: string = APP_METADATA_KEY_PREFIX): IMetadata => {
|
const getAppMetadataFrom = (meta: Metadata, appPrefix: string = APP_METADATA_KEY_PREFIX): Metadata => {
|
||||||
const appMetadata: IMetadata = {};
|
const appMetadata: Metadata = {};
|
||||||
|
|
||||||
Object.entries(meta).forEach(([key, value]) => {
|
Object.entries(meta).forEach(([key, value]) => {
|
||||||
if (key.startsWith(appPrefix)) {
|
if (key.startsWith(appPrefix)) {
|
||||||
@@ -129,8 +129,8 @@ const getAppMetadataFrom = (meta: IMetadata, appPrefix: string = APP_METADATA_KE
|
|||||||
return appMetadata;
|
return appMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyAppKeyPrefixMigration = (meta: IMetadata, migration: IMetadataMigration): IMetadata => {
|
const applyAppKeyPrefixMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
|
||||||
const migratedMetadata: IMetadata = { ...meta };
|
const migratedMetadata: Metadata = { ...meta };
|
||||||
|
|
||||||
if (!migration.appKeyPrefix) {
|
if (!migration.appKeyPrefix) {
|
||||||
return migratedMetadata;
|
return migratedMetadata;
|
||||||
@@ -154,12 +154,8 @@ const applyAppKeyPrefixMigration = (meta: IMetadata, migration: IMetadataMigrati
|
|||||||
return migratedMetadata;
|
return migratedMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyMetadataValueMigration = (
|
const applyMetadataValueMigration = (meta: Metadata, migration: IMetadataMigration, appKeyPrefix: string): Metadata => {
|
||||||
meta: IMetadata,
|
const migratedMetadata: Metadata = { ...meta };
|
||||||
migration: IMetadataMigration,
|
|
||||||
appKeyPrefix: string,
|
|
||||||
): IMetadata => {
|
|
||||||
const migratedMetadata: IMetadata = { ...meta };
|
|
||||||
|
|
||||||
if (!migration.values) {
|
if (!migration.values) {
|
||||||
return migratedMetadata;
|
return migratedMetadata;
|
||||||
@@ -193,8 +189,8 @@ const applyMetadataValueMigration = (
|
|||||||
return migratedMetadata;
|
return migratedMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyMetadataKeyMigration = (meta: IMetadata, migration: IMetadataMigration): IMetadata => {
|
const applyMetadataKeyMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
|
||||||
const migratedMetadata: IMetadata = { ...meta };
|
const migratedMetadata: Metadata = { ...meta };
|
||||||
|
|
||||||
if (!migration.keys) {
|
if (!migration.keys) {
|
||||||
return migratedMetadata;
|
return migratedMetadata;
|
||||||
@@ -217,12 +213,12 @@ const applyMetadataKeyMigration = (meta: IMetadata, migration: IMetadataMigratio
|
|||||||
return migratedMetadata;
|
return migratedMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyMetadataMigrations = (meta?: IMetadata): IMetadata | undefined => {
|
const applyMetadataMigrations = (meta?: Metadata): Metadata | undefined => {
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const migrationToMetadata: [number, IMetadata][] = [[0, meta]];
|
const migrationToMetadata: [number, Metadata][] = [[0, meta]];
|
||||||
|
|
||||||
migrations.forEach((migration, index) => {
|
migrations.forEach((migration, index) => {
|
||||||
const migrationId = index + 1;
|
const migrationId = index + 1;
|
||||||
@@ -246,12 +242,12 @@ const applyMetadataMigrations = (meta?: IMetadata): IMetadata | undefined => {
|
|||||||
return migrationToMetadata.pop()![1];
|
return migrationToMetadata.pop()![1];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMetadataValueFrom = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
|
export const getMetadataValueFrom = <Key extends AppMetadataKeys, Value extends AllowedMetadataValueTypes>(
|
||||||
{ meta }: IMetadataHolder,
|
{ meta }: MetadataHolder,
|
||||||
key: AppMetadataKeys,
|
key: Key,
|
||||||
defaultValue?: T,
|
defaultValue?: Value,
|
||||||
applyMigrations: boolean = true,
|
applyMigrations: boolean = true,
|
||||||
): T | undefined => {
|
): Value | undefined => {
|
||||||
const metadata = applyMigrations ? applyMetadataMigrations(meta) : meta;
|
const metadata = applyMigrations ? applyMetadataMigrations(meta) : meta;
|
||||||
|
|
||||||
if (metadata === undefined || !doesMetadataKeyExistIn(metadata, key)) {
|
if (metadata === undefined || !doesMetadataKeyExistIn(metadata, key)) {
|
||||||
@@ -261,21 +257,26 @@ export const getMetadataValueFrom = <T extends AllowedMetadataValueTypes = Allow
|
|||||||
return convertValueFromMetadata(metadata[getMetadataKey(key)]);
|
return convertValueFromMetadata(metadata[getMetadataKey(key)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMetadataFrom = (
|
export const getMetadataFrom = <METADATA extends Partial<Metadata<AppMetadataKeys, AllowedMetadataValueTypes>>>(
|
||||||
{ meta }: IMetadataHolder,
|
{ meta }: MetadataHolder,
|
||||||
keysToDefaultValues: MetadataKeyValuePair[],
|
metadataWithDefaultValues: METADATA,
|
||||||
applyMigrations?: boolean,
|
applyMigrations?: boolean,
|
||||||
): IMetadata<AllowedMetadataValueTypes> => {
|
): METADATA => {
|
||||||
const appMetadata: IMetadata<AllowedMetadataValueTypes> = {};
|
const appMetadata = {} as METADATA;
|
||||||
|
|
||||||
keysToDefaultValues.forEach(([key, defaultValue]) => {
|
Object.entries(metadataWithDefaultValues).forEach(([key, defaultValue]) => {
|
||||||
appMetadata[key] = getMetadataValueFrom({ meta }, key, defaultValue, applyMigrations);
|
appMetadata[key as AppMetadataKeys] = getMetadataValueFrom(
|
||||||
|
{ meta },
|
||||||
|
key as AppMetadataKeys,
|
||||||
|
defaultValue,
|
||||||
|
applyMigrations,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return appMetadata;
|
return appMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapMetadataWithMetaKey = (wrap: boolean, metadata: IMetadata): IMetadataHolder => {
|
const wrapMetadataWithMetaKey = (wrap: boolean, metadata: Metadata): MetadataHolder => {
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
return {
|
return {
|
||||||
meta: {
|
meta: {
|
||||||
@@ -291,7 +292,7 @@ const wrapMetadataWithMetaKey = (wrap: boolean, metadata: IMetadata): IMetadataH
|
|||||||
|
|
||||||
export const requestUpdateMetadataValue = async (
|
export const requestUpdateMetadataValue = async (
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
metadataHolder: IMetadataHolder,
|
metadataHolder: MetadataHolder,
|
||||||
key: AppMetadataKeys,
|
key: AppMetadataKeys,
|
||||||
value: AllowedMetadataValueTypes,
|
value: AllowedMetadataValueTypes,
|
||||||
endpointToMutate: string = endpoint,
|
endpointToMutate: string = endpoint,
|
||||||
@@ -323,7 +324,7 @@ export const requestUpdateMetadataValue = async (
|
|||||||
|
|
||||||
export const requestUpdateMetadata = async (
|
export const requestUpdateMetadata = async (
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
metadataHolder: IMetadataHolder,
|
metadataHolder: MetadataHolder,
|
||||||
keysToValues: [AppMetadataKeys, AllowedMetadataValueTypes][],
|
keysToValues: [AppMetadataKeys, AllowedMetadataValueTypes][],
|
||||||
endpointToMutate?: string,
|
endpointToMutate?: string,
|
||||||
wrapWithMetaKey?: boolean,
|
wrapWithMetaKey?: boolean,
|
||||||
@@ -335,7 +336,7 @@ export const requestUpdateMetadata = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const requestUpdateServerMetadata = async (
|
export const requestUpdateServerMetadata = async (
|
||||||
serverMetadata: IMetadata,
|
serverMetadata: Metadata,
|
||||||
keysToValues: MetadataKeyValuePair[],
|
keysToValues: MetadataKeyValuePair[],
|
||||||
): Promise<void[]> => requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);
|
): Promise<void[]> => requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);
|
||||||
|
|
||||||
|
|||||||
@@ -8,51 +8,45 @@
|
|||||||
|
|
||||||
import { getMetadataFrom, requestUpdateMangaMetadata, requestUpdateServerMetadata } from 'util/metadata';
|
import { getMetadataFrom, requestUpdateMangaMetadata, requestUpdateServerMetadata } from 'util/metadata';
|
||||||
import { useQuery } from 'util/client';
|
import { useQuery } from 'util/client';
|
||||||
import { IManga, IMetadata, IMetadataHolder, IReaderSettings, MetadataKeyValuePair } from 'typings';
|
import { IManga, Metadata, MetadataHolder, IReaderSettings, MetadataKeyValuePair } from 'typings';
|
||||||
|
|
||||||
export const getDefaultSettings = (forceUndefined: boolean = false) =>
|
type UndefinedReaderSettings = {
|
||||||
({
|
[setting in keyof IReaderSettings]: IReaderSettings[setting] | undefined;
|
||||||
staticNav: forceUndefined ? undefined : false,
|
};
|
||||||
showPageNumber: forceUndefined ? undefined : true,
|
|
||||||
continuesPageGap: forceUndefined ? undefined : false,
|
|
||||||
loadNextOnEnding: forceUndefined ? undefined : false,
|
|
||||||
skipDupChapters: forceUndefined ? undefined : true,
|
|
||||||
readerType: forceUndefined ? undefined : 'ContinuesVertical',
|
|
||||||
} as IReaderSettings);
|
|
||||||
|
|
||||||
const getReaderSettingsWithDefaultValueFallback = (
|
export const getDefaultSettings = (): IReaderSettings => ({
|
||||||
meta?: IMetadata,
|
staticNav: false,
|
||||||
defaultSettings?: IReaderSettings,
|
showPageNumber: true,
|
||||||
applyMetadataMigration: boolean = true,
|
loadNextOnEnding: false,
|
||||||
): IReaderSettings => ({
|
skipDupChapters: true,
|
||||||
...(getMetadataFrom(
|
readerType: 'ContinuesVertical',
|
||||||
{ meta },
|
|
||||||
Object.entries(defaultSettings ?? getDefaultSettings()) as MetadataKeyValuePair[],
|
|
||||||
applyMetadataMigration,
|
|
||||||
) as unknown as IReaderSettings),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getReaderSettingsWithDefaultValueFallback = <DefaultSettings extends IReaderSettings | UndefinedReaderSettings>(
|
||||||
|
meta?: Metadata,
|
||||||
|
defaultSettings: DefaultSettings = getDefaultSettings() as DefaultSettings,
|
||||||
|
applyMetadataMigration: boolean = true,
|
||||||
|
): DefaultSettings => getMetadataFrom({ meta }, defaultSettings, applyMetadataMigration);
|
||||||
|
|
||||||
export const getReaderSettingsFromMetadata = (
|
export const getReaderSettingsFromMetadata = (
|
||||||
meta?: IMetadata,
|
meta?: Metadata,
|
||||||
defaultSettings?: IReaderSettings,
|
defaultSettings?: IReaderSettings,
|
||||||
applyMetadataMigration?: boolean,
|
applyMetadataMigration?: boolean,
|
||||||
): IReaderSettings => ({
|
): IReaderSettings => getReaderSettingsWithDefaultValueFallback(meta, defaultSettings, applyMetadataMigration);
|
||||||
...getReaderSettingsWithDefaultValueFallback(meta, defaultSettings, applyMetadataMigration),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getReaderSettingsFor = (
|
export const getReaderSettingsFor = (
|
||||||
{ meta }: IMetadataHolder,
|
{ meta }: MetadataHolder,
|
||||||
defaultSettings?: IReaderSettings,
|
defaultSettings?: IReaderSettings,
|
||||||
applyMetadataMigration?: boolean,
|
applyMetadataMigration?: boolean,
|
||||||
): IReaderSettings => getReaderSettingsFromMetadata(meta, defaultSettings, applyMetadataMigration);
|
): IReaderSettings => getReaderSettingsFromMetadata(meta, defaultSettings, applyMetadataMigration);
|
||||||
|
|
||||||
export const useDefaultReaderSettings = (): {
|
export const useDefaultReaderSettings = (): {
|
||||||
metadata?: IMetadata;
|
metadata?: Metadata;
|
||||||
settings: IReaderSettings;
|
settings: IReaderSettings;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
} => {
|
} => {
|
||||||
const { data: meta, loading } = useQuery<IMetadata>('/api/v1/meta');
|
const { data: meta, loading } = useQuery<Metadata>('/api/v1/meta');
|
||||||
const settings = getReaderSettingsWithDefaultValueFallback(meta);
|
const settings = getReaderSettingsWithDefaultValueFallback<IReaderSettings>(meta);
|
||||||
|
|
||||||
return { metadata: meta, settings, loading };
|
return { metadata: meta, settings, loading };
|
||||||
};
|
};
|
||||||
@@ -65,12 +59,22 @@ export const useDefaultReaderSettings = (): {
|
|||||||
* @param defaultSettings
|
* @param defaultSettings
|
||||||
*/
|
*/
|
||||||
export const checkAndHandleMissingStoredReaderSettings = async (
|
export const checkAndHandleMissingStoredReaderSettings = async (
|
||||||
metadataHolder: IManga | IMetadataHolder,
|
metadataHolder: IManga | MetadataHolder,
|
||||||
metadataHolderType: 'manga' | 'server',
|
metadataHolderType: 'manga' | 'server',
|
||||||
defaultSettings: IReaderSettings,
|
defaultSettings: IReaderSettings,
|
||||||
): Promise<void | void[]> => {
|
): Promise<void | void[]> => {
|
||||||
const meta = metadataHolder.meta ?? (metadataHolder as IMetadata);
|
const meta = metadataHolder.meta ?? (metadataHolder as Metadata);
|
||||||
const settingsToCheck = getReaderSettingsFor({ meta }, getDefaultSettings(true), false);
|
const settingsToCheck = getReaderSettingsWithDefaultValueFallback(
|
||||||
|
meta,
|
||||||
|
{
|
||||||
|
staticNav: undefined,
|
||||||
|
showPageNumber: undefined,
|
||||||
|
loadNextOnEnding: undefined,
|
||||||
|
skipDupChapters: undefined,
|
||||||
|
readerType: undefined,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
const newSettings = getReaderSettingsFor({ meta }, defaultSettings);
|
const newSettings = getReaderSettingsFor({ meta }, defaultSettings);
|
||||||
|
|
||||||
const undefinedSettings = Object.entries(settingsToCheck).filter((setting) => setting[1] === undefined);
|
const undefinedSettings = Object.entries(settingsToCheck).filter((setting) => setting[1] === undefined);
|
||||||
|
|||||||
@@ -1,30 +1,22 @@
|
|||||||
import { useQuery } from 'util/client';
|
import { useQuery } from 'util/client';
|
||||||
import { getMetadataFrom } from 'util/metadata';
|
import { getMetadataFrom } from 'util/metadata';
|
||||||
import { IMetadata, ISearchSettings, MetadataKeyValuePair } from 'typings';
|
import { Metadata, ISearchSettings } from 'typings';
|
||||||
|
|
||||||
export const getDefaultSettings = () =>
|
export const getDefaultSettings = (): ISearchSettings => ({
|
||||||
({
|
ignoreFilters: false,
|
||||||
ignoreFilters: false,
|
|
||||||
} as ISearchSettings);
|
|
||||||
|
|
||||||
const getSearchSettingsWithDefaultValueFallback = (
|
|
||||||
meta?: IMetadata,
|
|
||||||
defaultSettings?: ISearchSettings,
|
|
||||||
applyMetadataMigration: boolean = true,
|
|
||||||
): ISearchSettings => ({
|
|
||||||
...(getMetadataFrom(
|
|
||||||
{ meta },
|
|
||||||
Object.entries(defaultSettings ?? getDefaultSettings()) as MetadataKeyValuePair[],
|
|
||||||
applyMetadataMigration,
|
|
||||||
) as unknown as ISearchSettings),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getSearchSettingsWithDefaultValueFallback = (
|
||||||
|
meta?: Metadata,
|
||||||
|
defaultSettings: ISearchSettings = getDefaultSettings(),
|
||||||
|
applyMetadataMigration: boolean = true,
|
||||||
|
): ISearchSettings => getMetadataFrom({ meta }, defaultSettings, applyMetadataMigration);
|
||||||
export const useSearchSettings = (): {
|
export const useSearchSettings = (): {
|
||||||
metadata?: IMetadata;
|
metadata?: Metadata;
|
||||||
settings: ISearchSettings;
|
settings: ISearchSettings;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
} => {
|
} => {
|
||||||
const { data: meta, loading } = useQuery<IMetadata>('/api/v1/meta');
|
const { data: meta, loading } = useQuery<Metadata>('/api/v1/meta');
|
||||||
const settings = getSearchSettingsWithDefaultValueFallback(meta);
|
const settings = getSearchSettingsWithDefaultValueFallback(meta);
|
||||||
|
|
||||||
return { metadata: meta, settings, loading };
|
return { metadata: meta, settings, loading };
|
||||||
|
|||||||
Reference in New Issue
Block a user