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

Async settings service #752

Merged
merged 25 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
00d132b
initial commit
rolfheij Feb 5, 2024
8ed04f3
format service, build model
Feb 5, 2024
c823490
Merge branch 'main' of github.com:paranext/paranext-core into 502-asy…
Feb 5, 2024
abebb9a
data provider types, adding settings info data
Feb 6, 2024
b8af80a
Merge branch 'main' into 502-async-settings-service
rolfheij Feb 6, 2024
4f8d764
Updates to settings service and useSetting hook
rolfheij-sil Feb 6, 2024
4bc18de
Update papi
rolfheij-sil Feb 6, 2024
1da3c05
Update use setting hook
rolfheij-sil Feb 6, 2024
2439c5a
Processed review comments. Add start service
rolfheij-sil Feb 7, 2024
9ef5ab8
Add loading settings from file
rolfheij-sil Feb 7, 2024
fb9ba84
Process review comments
rolfheij-sil Feb 8, 2024
5340485
Merge branch 'main' into 502-async-settings-service
rolfheij-sil Feb 8, 2024
f56e5d0
Merge branch 'main' into 502-async-settings-service
rolfheij-sil Feb 9, 2024
6633969
Process more review comments
rolfheij-sil Feb 9, 2024
388a369
Remove TODOs
rolfheij-sil Feb 9, 2024
3d48b47
fix unit test bug, start settings service unit tests, and small fix i…
Feb 12, 2024
f4dc247
Working on getter, setter and reset
rolfheij-sil Feb 12, 2024
b50eea5
fix reset, work on unit tests
Feb 12, 2024
389cd2d
Merge branch 'main' into 502-async-settings-service
rolfheij-sil Feb 13, 2024
91290ab
Update service host
rolfheij-sil Feb 13, 2024
4baf9a1
finish unit tests
Feb 13, 2024
dcda496
Processed some more review comments
rolfheij-sil Feb 13, 2024
7c0d51d
Add beforeEach function for tests, and deep clone data passed into da…
rolfheij-sil Feb 13, 2024
1489050
Small update to settings service host
rolfheij-sil Feb 14, 2024
fc982ca
Merge branch 'main' into 502-async-settings-service
rolfheij-sil Feb 14, 2024
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
198 changes: 115 additions & 83 deletions lib/papi-dts/papi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3727,76 +3727,6 @@ declare module 'renderer/hooks/papi-hooks/use-dialog-callback.hook' {
): (optionOverrides?: Partial<DialogOptions & UseDialogCallbackOptions>) => Promise<void>;
export default useDialogCallback;
}
declare module 'shared/services/settings.service' {
import { Unsubscriber } from 'platform-bible-utils';
import { SettingNames, SettingTypes } from 'papi-shared-types';
/** Event to set or update a setting */
export type UpdateSettingEvent<SettingName extends SettingNames> = {
type: 'update-setting';
setting: SettingTypes[SettingName];
};
/** Event to remove a setting */
export type ResetSettingEvent = {
type: 'reset-setting';
};
/** All supported setting events */
export type SettingEvent<SettingName extends SettingNames> =
| UpdateSettingEvent<SettingName>
| ResetSettingEvent;
/**
* Retrieves the value of the specified setting
*
* @param key The string id of the setting for which the value is being retrieved
* @param defaultSetting The default value used for the setting if no value is available for the key
* @returns The value of the specified setting, parsed to an object. Returns default setting if
* setting does not exist
*/
const getSetting: <SettingName extends keyof SettingTypes>(
key: SettingName,
defaultSetting: SettingTypes[SettingName],
) => SettingTypes[SettingName];
/**
* Sets the value of the specified setting
*
* @param key The string id of the setting for which the value is being retrieved
* @param newSetting The value that is to be stored. Setting the new value to `undefined` is the
* equivalent of deleting the setting
*/
const setSetting: <SettingName extends keyof SettingTypes>(
key: SettingName,
newSetting: SettingTypes[SettingName],
) => void;
/**
* Removes the setting from memory
*
* @param key The string id of the setting for which the value is being removed
*/
const resetSetting: <SettingName extends keyof SettingTypes>(key: SettingName) => void;
/**
* Subscribes to updates of the specified setting. Whenever the value of the setting changes, the
* callback function is executed.
*
* @param key The string id of the setting for which the value is being subscribed to
* @param callback The function that will be called whenever the specified setting is updated
* @returns Unsubscriber that should be called whenever the subscription should be deleted
*/
const subscribeToSetting: <SettingName extends keyof SettingTypes>(
key: SettingName,
callback: (newSetting: SettingEvent<SettingName>) => void,
) => Unsubscriber;
export interface SettingsService {
get: typeof getSetting;
set: typeof setSetting;
reset: typeof resetSetting;
subscribe: typeof subscribeToSetting;
}
/**
*
* Service that allows to get and set settings in local storage
*/
const settingsService: SettingsService;
export default settingsService;
}
declare module '@papi/core' {
/** Exporting empty object so people don't have to put 'type' in their import statements */
const core: {};
Expand Down Expand Up @@ -3828,7 +3758,6 @@ declare module '@papi/core' {
WebViewProps,
} from 'shared/models/web-view.model';
export type { IWebViewProvider } from 'shared/models/web-view-provider.model';
export type { SettingEvent } from 'shared/services/settings.service';
}
declare module 'shared/services/menu-data.service-model' {
import {
Expand Down Expand Up @@ -4197,6 +4126,101 @@ declare module 'extension-host/extension-types/extension-manifest.model' {
activationEvents: string[];
};
}
declare module 'shared/services/settings.service-model' {
import { SettingNames, SettingTypes } from 'papi-shared-types';
import { OnDidDispose, Unsubscriber } from 'platform-bible-utils';
import { DataProviderUpdateInstructions, IDataProvider } from '@papi/core';
/**
*
* This name is used to register the settings service data provider on the papi. You can use this
* name to find the data provider when accessing it using the useData hook
*/
export const settingsServiceDataProviderName = 'platform.settingsServiceDataProvider';
export const settingsServiceObjectToProxy: Readonly<{
/**
*
* This name is used to register the settings service data provider on the papi. You can use this
* name to find the data provider when accessing it using the useData hook
*/
dataProviderName: 'platform.settingsServiceDataProvider';
}>;
/**
* SettingDataTypes handles getting and setting Platform.Bible core application and extension
* settings.
*
* Note: the unnamed (`''`) data type is not actually part of `SettingDataTypes` because the methods
* would not be able to create a generic type extending from `SettingNames` in order to return the
* specific setting type being requested. As such, `get`, `set`, `reset` and `subscribe` are all
* specified on {@link ISettingsService} instead. Unfortunately, as a result, using Intellisense with
* `useData` will not show the unnamed data type (`''`) as an option, but you can use `useSetting`
* instead. However, do note that the unnamed data type (`''`) is fully functional.
*/
export type SettingDataTypes = {};
module 'papi-shared-types' {
interface DataProviders {
[settingsServiceDataProviderName]: ISettingsService;
}
}
/** Event to set or update a setting */
export type UpdateSettingEvent<SettingName extends SettingNames> = {
type: 'update-setting';
setting: SettingTypes[SettingName];
};
/** Event to remove a setting */
export type ResetSettingEvent = {
type: 'reset-setting';
};
/** */
export type ISettingsService = {
/**
* Retrieves the value of the specified setting
*
* @param key The string id of the setting for which the value is being retrieved
* @param defaultSetting The default value used for the setting if no value is available for the
* key
* @returns The value of the specified setting, parsed to an object. Returns default setting if
* setting does not exist
*/
get<SettingName extends SettingNames>(key: SettingName): Promise<SettingTypes[SettingName]>;
/**
* Sets the value of the specified setting
*
* @param key The string id of the setting for which the value is being retrieved
* @param newSetting The value that is to be stored. Setting the new value to `undefined` is the
* equivalent of deleting the setting
*/
set<SettingName extends SettingNames>(
key: SettingName,
newSetting: SettingTypes[SettingName],
): Promise<DataProviderUpdateInstructions<SettingDataTypes>>;
/**
* Removes the setting from memory
*
* @param key The string id of the setting for which the value is being removed
* @returns `true` if successfully reset the project setting. `false` otherwise
*/
reset<SettingName extends SettingNames>(key: SettingName): Promise<boolean>;
/**
* Subscribes to updates of the specified setting. Whenever the value of the setting changes, the
* callback function is executed.
*
* @param key The string id of the setting for which the value is being subscribed to
* @param callback The function that will be called whenever the specified setting is updated
* @returns Unsubscriber that should be called whenever the subscription should be deleted
*/
subscribe<SettingName extends SettingNames>(
key: SettingName,
callback: (newSetting: SettingTypes[SettingName]) => void,
): Promise<Unsubscriber>;
} & OnDidDispose &
IDataProvider<SettingDataTypes> &
typeof settingsServiceObjectToProxy;
}
declare module 'shared/services/settings.service' {
import { ISettingsService } from 'shared/services/settings.service-model';
const settingsService: ISettingsService;
export default settingsService;
}
declare module 'renderer/hooks/hook-generators/create-use-network-object-hook.util' {
import { NetworkObject } from 'shared/models/network-object.model';
/**
Expand Down Expand Up @@ -4409,6 +4433,11 @@ declare module 'renderer/hooks/papi-hooks/use-data.hook' {
}
declare module 'renderer/hooks/papi-hooks/use-setting.hook' {
import { SettingTypes } from 'papi-shared-types';
import {
DataProviderSubscriberOptions,
DataProviderUpdateInstructions,
} from 'shared/models/data-provider.model';
import { SettingDataTypes } from 'shared/services/settings.service-model';
/**
* Gets, sets and resets a setting on the papi. Also notifies subscribers when the setting changes
* and gets updated when the setting is changed by others.
Expand All @@ -4426,6 +4455,12 @@ declare module 'renderer/hooks/papi-hooks/use-setting.hook' {
* `defaultState`. However, if `defaultState` is changed while a setting is `defaultState`
* (meaning it is reset and has no value), the returned setting value will not be updated to the
* new `defaultState`.
* @param subscriberOptions Various options to adjust how the subscriber emits updates
*
* Note: this parameter is internally assigned to a `ref`, so changing it will not cause any hooks
* to re-run with its new value. This means that `subscriberOptions` will be passed to the data
* provider's `subscribe<data_type>` method as soon as possible and will not be updated again
* until `dataProviderSource` or `selector` changes.
* @returns `[setting, setSetting, resetSetting]`
*
* - `setting`: The current state of the setting, either `defaultState` or the stored state on the
Expand All @@ -4439,9 +4474,12 @@ declare module 'renderer/hooks/papi-hooks/use-setting.hook' {
const useSetting: <SettingName extends keyof SettingTypes>(
key: SettingName,
defaultState: SettingTypes[SettingName],
subscriberOptions?: DataProviderSubscriberOptions,
) => [
setting: SettingTypes[SettingName],
setSetting: (newSetting: SettingTypes[SettingName]) => void,
setSetting: (
newData: SettingTypes[SettingName],
) => Promise<DataProviderUpdateInstructions<SettingDataTypes>>,
resetSetting: () => void,
];
export default useSetting;
Expand Down Expand Up @@ -4692,7 +4730,7 @@ declare module '@papi/frontend' {
import { DataProviderService } from 'shared/services/data-provider.service';
import { ProjectLookupServiceType } from 'shared/services/project-lookup.service-model';
import { PapiFrontendProjectDataProviderService } from 'shared/services/project-data-provider.service';
import { SettingsService } from 'shared/services/settings.service';
import { ISettingsService } from 'shared/services/settings.service-model';
import { DialogService } from 'shared/services/dialog.service-model';
import * as papiReact from '@papi/frontend/react';
import PapiRendererWebSocket from 'renderer/services/renderer-web-socket.service';
Expand Down Expand Up @@ -4772,11 +4810,8 @@ declare module '@papi/frontend' {
* React hooks that enable interacting with the `papi` in React components more easily.
*/
react: typeof papiReact;
/**
*
* Service that allows to get and set settings in local storage
*/
settings: SettingsService;
/** */
settings: ISettingsService;
/**
*
* Service that allows to get and store menu data
Expand Down Expand Up @@ -4857,11 +4892,8 @@ declare module '@papi/frontend' {
* React hooks that enable interacting with the `papi` in React components more easily.
*/
export const react: typeof papiReact;
/**
*
* Service that allows to get and set settings in local storage
*/
export const settings: SettingsService;
/** */
export const settings: ISettingsService;
/**
*
* Service that allows to get and store menu data
Expand Down
1 change: 1 addition & 0 deletions src/declarations/papi-shared-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ declare module 'papi-shared-types' {

export interface SettingTypes {
'platform.verseRef': ScriptureReference;
// 'platform.interfaceLanguage': InterfaceLanguage;
// With only one key in this interface, `papi.d.ts` was baking in the literal string when
// `SettingNames` was being used. Adding a placeholder key makes TypeScript generate `papi.d.ts`
// correctly. When we add another setting, we can remove this placeholder.
Expand Down
18 changes: 18 additions & 0 deletions src/main/data/core-settings-info.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { SettingNames, SettingTypes } from 'papi-shared-types';

/** Information about one setting */
type SettingInfo<SettingName extends SettingNames> = {
default: SettingTypes[SettingName];
};

/** Information about all settings. Keys are setting keys, values are information for that setting */
export type AllSettingsInfo = {
[SettingName in SettingNames]: SettingInfo<SettingName>;
};

/** Info about all settings built into core. Does not contain info for extensions' settings */
const coreSettingsInfo: Partial<AllSettingsInfo> = {
'platform.verseRef': { default: { bookNum: 1, chapterNum: 1, verseNum: 1 } },
};

export default coreSettingsInfo;
3 changes: 3 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { get } from '@shared/services/project-data-provider.service';
import { VerseRef } from '@sillsdev/scripture';
import { startNetworkObjectStatusService } from './services/network-object-status.service-host';
import { startLocalizationService } from './services/localization.service-host';
import { initialize as initializeSettingsService } from './services/settings.service-host';

const PROCESS_CLOSE_TIME_OUT = 2000;

Expand Down Expand Up @@ -82,6 +83,8 @@ async function main() {

await startLocalizationService();

await initializeSettingsService();

// TODO (maybe): Wait for signal from the extension host process that it is ready (except 'getWebView')
// We could then wait for the renderer to be ready and signal the extension host

Expand Down
Loading
Loading