Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/improve typing of metadata related logic #268

Merged
merged 3 commits into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ export interface IMetadataMigration {
keys?: { oldKey: string; newKey: string }[];
}

export interface IMetadata<VALUES extends AllowedMetadataValueTypes = string> {
[key: string]: VALUES;
}
export type Metadata<Keys extends string = string, Values = string> = {
[key in Keys]: Values;
};

export interface IMetadataHolder<VALUES extends AllowedMetadataValueTypes = string> {
meta?: IMetadata<VALUES>;
}
export type MetadataHolder<Keys extends string = string, Values = string> = {
meta?: Metadata<Keys, Values>;
};

export type AllowedMetadataValueTypes = string | boolean | number | undefined;

Expand All @@ -100,7 +100,7 @@ export interface IMangaCard {
unreadCount?: number;
downloadCount?: number;
inLibrary?: boolean;
meta?: IMetadata;
meta?: Metadata;
inLibraryAt: number;
lastReadAt: number;
}
Expand All @@ -122,7 +122,7 @@ export interface IManga {
inLibrary: boolean;
source: ISource;

meta: IMetadata;
meta: Metadata;

realUrl: string;
freshData: boolean;
Expand Down Expand Up @@ -158,7 +158,7 @@ export interface IChapter {
chapterCount: number;
pageCount: number;
downloaded: boolean;
meta: IMetadata;
meta: Metadata;
}

export interface IMangaChapter {
Expand All @@ -178,7 +178,7 @@ export interface ICategory {
order: number;
name: string;
default: boolean;
meta: IMetadata;
meta: Metadata;
}

export interface INavbarOverride {
Expand Down
69 changes: 35 additions & 34 deletions src/util/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {
IManga,
IMangaCard,
IMangaChapter,
IMetadata,
IMetadataHolder,
Metadata,
MetadataHolder,
IMetadataMigration,
MetadataKeyValuePair,
} from 'typings';
Expand Down Expand Up @@ -96,7 +96,7 @@ const getAppKeyPrefixForMigration = (migrationId: number): string => {

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));

const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
Expand All @@ -117,8 +117,8 @@ const convertValueFromMetadata = <T extends AllowedMetadataValueTypes = AllowedM
return value as T;
};

const getAppMetadataFrom = (meta: IMetadata, appPrefix: string = APP_METADATA_KEY_PREFIX): IMetadata => {
const appMetadata: IMetadata = {};
const getAppMetadataFrom = (meta: Metadata, appPrefix: string = APP_METADATA_KEY_PREFIX): Metadata => {
const appMetadata: Metadata = {};

Object.entries(meta).forEach(([key, value]) => {
if (key.startsWith(appPrefix)) {
Expand All @@ -129,8 +129,8 @@ const getAppMetadataFrom = (meta: IMetadata, appPrefix: string = APP_METADATA_KE
return appMetadata;
};

const applyAppKeyPrefixMigration = (meta: IMetadata, migration: IMetadataMigration): IMetadata => {
const migratedMetadata: IMetadata = { ...meta };
const applyAppKeyPrefixMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
const migratedMetadata: Metadata = { ...meta };

if (!migration.appKeyPrefix) {
return migratedMetadata;
Expand All @@ -154,12 +154,8 @@ const applyAppKeyPrefixMigration = (meta: IMetadata, migration: IMetadataMigrati
return migratedMetadata;
};

const applyMetadataValueMigration = (
meta: IMetadata,
migration: IMetadataMigration,
appKeyPrefix: string,
): IMetadata => {
const migratedMetadata: IMetadata = { ...meta };
const applyMetadataValueMigration = (meta: Metadata, migration: IMetadataMigration, appKeyPrefix: string): Metadata => {
const migratedMetadata: Metadata = { ...meta };

if (!migration.values) {
return migratedMetadata;
Expand Down Expand Up @@ -193,8 +189,8 @@ const applyMetadataValueMigration = (
return migratedMetadata;
};

const applyMetadataKeyMigration = (meta: IMetadata, migration: IMetadataMigration): IMetadata => {
const migratedMetadata: IMetadata = { ...meta };
const applyMetadataKeyMigration = (meta: Metadata, migration: IMetadataMigration): Metadata => {
const migratedMetadata: Metadata = { ...meta };

if (!migration.keys) {
return migratedMetadata;
Expand All @@ -217,12 +213,12 @@ const applyMetadataKeyMigration = (meta: IMetadata, migration: IMetadataMigratio
return migratedMetadata;
};

const applyMetadataMigrations = (meta?: IMetadata): IMetadata | undefined => {
const applyMetadataMigrations = (meta?: Metadata): Metadata | undefined => {
if (!meta) {
return undefined;
}

const migrationToMetadata: [number, IMetadata][] = [[0, meta]];
const migrationToMetadata: [number, Metadata][] = [[0, meta]];

migrations.forEach((migration, index) => {
const migrationId = index + 1;
Expand All @@ -246,12 +242,12 @@ const applyMetadataMigrations = (meta?: IMetadata): IMetadata | undefined => {
return migrationToMetadata.pop()![1];
};

export const getMetadataValueFrom = <T extends AllowedMetadataValueTypes = AllowedMetadataValueTypes>(
{ meta }: IMetadataHolder,
key: AppMetadataKeys,
defaultValue?: T,
export const getMetadataValueFrom = <Key extends AppMetadataKeys, Value extends AllowedMetadataValueTypes>(
{ meta }: MetadataHolder,
key: Key,
defaultValue?: Value,
applyMigrations: boolean = true,
): T | undefined => {
): Value | undefined => {
const metadata = applyMigrations ? applyMetadataMigrations(meta) : meta;

if (metadata === undefined || !doesMetadataKeyExistIn(metadata, key)) {
Expand All @@ -261,21 +257,26 @@ export const getMetadataValueFrom = <T extends AllowedMetadataValueTypes = Allow
return convertValueFromMetadata(metadata[getMetadataKey(key)]);
};

export const getMetadataFrom = (
{ meta }: IMetadataHolder,
keysToDefaultValues: MetadataKeyValuePair[],
export const getMetadataFrom = <METADATA extends Partial<Metadata<AppMetadataKeys, AllowedMetadataValueTypes>>>(
{ meta }: MetadataHolder,
metadataWithDefaultValues: METADATA,
applyMigrations?: boolean,
): IMetadata<AllowedMetadataValueTypes> => {
const appMetadata: IMetadata<AllowedMetadataValueTypes> = {};

keysToDefaultValues.forEach(([key, defaultValue]) => {
appMetadata[key] = getMetadataValueFrom({ meta }, key, defaultValue, applyMigrations);
): METADATA => {
const appMetadata = {} as METADATA;

Object.entries(metadataWithDefaultValues).forEach(([key, defaultValue]) => {
appMetadata[key as AppMetadataKeys] = getMetadataValueFrom(
{ meta },
key as AppMetadataKeys,
defaultValue,
applyMigrations,
);
});

return appMetadata;
};

const wrapMetadataWithMetaKey = (wrap: boolean, metadata: IMetadata): IMetadataHolder => {
const wrapMetadataWithMetaKey = (wrap: boolean, metadata: Metadata): MetadataHolder => {
if (wrap) {
return {
meta: {
Expand All @@ -291,7 +292,7 @@ const wrapMetadataWithMetaKey = (wrap: boolean, metadata: IMetadata): IMetadataH

export const requestUpdateMetadataValue = async (
endpoint: string,
metadataHolder: IMetadataHolder,
metadataHolder: MetadataHolder,
key: AppMetadataKeys,
value: AllowedMetadataValueTypes,
endpointToMutate: string = endpoint,
Expand Down Expand Up @@ -323,7 +324,7 @@ export const requestUpdateMetadataValue = async (

export const requestUpdateMetadata = async (
endpoint: string,
metadataHolder: IMetadataHolder,
metadataHolder: MetadataHolder,
keysToValues: [AppMetadataKeys, AllowedMetadataValueTypes][],
endpointToMutate?: string,
wrapWithMetaKey?: boolean,
Expand All @@ -335,7 +336,7 @@ export const requestUpdateMetadata = async (
);

export const requestUpdateServerMetadata = async (
serverMetadata: IMetadata,
serverMetadata: Metadata,
keysToValues: MetadataKeyValuePair[],
): Promise<void[]> => requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false);

Expand Down
66 changes: 35 additions & 31 deletions src/util/readerSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,45 @@

import { getMetadataFrom, requestUpdateMangaMetadata, requestUpdateServerMetadata } from 'util/metadata';
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) =>
({
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);
type UndefinedReaderSettings = {
[setting in keyof IReaderSettings]: IReaderSettings[setting] | undefined;
};

const getReaderSettingsWithDefaultValueFallback = (
meta?: IMetadata,
defaultSettings?: IReaderSettings,
applyMetadataMigration: boolean = true,
): IReaderSettings => ({
...(getMetadataFrom(
{ meta },
Object.entries(defaultSettings ?? getDefaultSettings()) as MetadataKeyValuePair[],
applyMetadataMigration,
) as unknown as IReaderSettings),
export const getDefaultSettings = (): IReaderSettings => ({
staticNav: false,
showPageNumber: true,
loadNextOnEnding: false,
skipDupChapters: true,
readerType: 'ContinuesVertical',
});

const getReaderSettingsWithDefaultValueFallback = <DefaultSettings extends IReaderSettings | UndefinedReaderSettings>(
meta?: Metadata,
defaultSettings: DefaultSettings = getDefaultSettings() as DefaultSettings,
applyMetadataMigration: boolean = true,
): DefaultSettings => getMetadataFrom({ meta }, defaultSettings, applyMetadataMigration);

export const getReaderSettingsFromMetadata = (
meta?: IMetadata,
meta?: Metadata,
defaultSettings?: IReaderSettings,
applyMetadataMigration?: boolean,
): IReaderSettings => ({
...getReaderSettingsWithDefaultValueFallback(meta, defaultSettings, applyMetadataMigration),
});
): IReaderSettings => getReaderSettingsWithDefaultValueFallback(meta, defaultSettings, applyMetadataMigration);

export const getReaderSettingsFor = (
{ meta }: IMetadataHolder,
{ meta }: MetadataHolder,
defaultSettings?: IReaderSettings,
applyMetadataMigration?: boolean,
): IReaderSettings => getReaderSettingsFromMetadata(meta, defaultSettings, applyMetadataMigration);

export const useDefaultReaderSettings = (): {
metadata?: IMetadata;
metadata?: Metadata;
settings: IReaderSettings;
loading: boolean;
} => {
const { data: meta, loading } = useQuery<IMetadata>('/api/v1/meta');
const settings = getReaderSettingsWithDefaultValueFallback(meta);
const { data: meta, loading } = useQuery<Metadata>('/api/v1/meta');
const settings = getReaderSettingsWithDefaultValueFallback<IReaderSettings>(meta);

return { metadata: meta, settings, loading };
};
Expand All @@ -65,12 +59,22 @@ export const useDefaultReaderSettings = (): {
* @param defaultSettings
*/
export const checkAndHandleMissingStoredReaderSettings = async (
metadataHolder: IManga | IMetadataHolder,
metadataHolder: IManga | MetadataHolder,
metadataHolderType: 'manga' | 'server',
defaultSettings: IReaderSettings,
): Promise<void | void[]> => {
const meta = metadataHolder.meta ?? (metadataHolder as IMetadata);
const settingsToCheck = getReaderSettingsFor({ meta }, getDefaultSettings(true), false);
const meta = metadataHolder.meta ?? (metadataHolder as Metadata);
const settingsToCheck = getReaderSettingsWithDefaultValueFallback(
meta,
{
staticNav: undefined,
showPageNumber: undefined,
loadNextOnEnding: undefined,
skipDupChapters: undefined,
readerType: undefined,
},
false,
);
const newSettings = getReaderSettingsFor({ meta }, defaultSettings);

const undefinedSettings = Object.entries(settingsToCheck).filter((setting) => setting[1] === undefined);
Expand Down
26 changes: 9 additions & 17 deletions src/util/searchSettings.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
import { useQuery } from 'util/client';
import { getMetadataFrom } from 'util/metadata';
import { IMetadata, ISearchSettings, MetadataKeyValuePair } from 'typings';
import { Metadata, ISearchSettings } from 'typings';

export const getDefaultSettings = () =>
({
ignoreFilters: false,
} as ISearchSettings);
export const getDefaultSettings = (): ISearchSettings => ({
ignoreFilters: false,
});

const getSearchSettingsWithDefaultValueFallback = (
meta?: IMetadata,
defaultSettings?: ISearchSettings,
meta?: Metadata,
defaultSettings: ISearchSettings = getDefaultSettings(),
applyMetadataMigration: boolean = true,
): ISearchSettings => ({
...(getMetadataFrom(
{ meta },
Object.entries(defaultSettings ?? getDefaultSettings()) as MetadataKeyValuePair[],
applyMetadataMigration,
) as unknown as ISearchSettings),
});

): ISearchSettings => getMetadataFrom({ meta }, defaultSettings, applyMetadataMigration);
export const useSearchSettings = (): {
metadata?: IMetadata;
metadata?: Metadata;
settings: ISearchSettings;
loading: boolean;
} => {
const { data: meta, loading } = useQuery<IMetadata>('/api/v1/meta');
const { data: meta, loading } = useQuery<Metadata>('/api/v1/meta');
const settings = getSearchSettingsWithDefaultValueFallback(meta);

return { metadata: meta, settings, loading };
Expand Down