Skip to content

Commit

Permalink
Added web view id to web view menu commands, moved projectId(s) to sa…
Browse files Browse the repository at this point in the history
…ved web view definition, spread saved web view definition into web view props instead of just updatable props and run function to access, allowed getting saved web view definitions from backend, exercised with hello world extension
  • Loading branch information
tjcouch-sil committed May 20, 2024
1 parent 5003dc5 commit 77dc12e
Show file tree
Hide file tree
Showing 20 changed files with 559 additions and 240 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"%mainMenu_openHelloWorldProject%": "Open Hello World Project",
"%mainMenu_createNewHelloWorldProject%": "Create New Hello World Project",
"%mainMenu_deleteHelloWorldProject%": "Delete Hello World Project",
"%helloWorld_webViewMenu_project%": "Project",
"%helloWorld_webViewMenu_view%": "View",
"%helloWorld_webViewMenu_deleteProject%": "Delete this Project",
"%helloWorld_webViewMenu_openViewer%": "Open Viewer",
"%settings_hello_world_group1_label%": "Hello World Settings",
"%settings_hello_world_personName_label%": "Selected Person's Name on Hello World Web View",
"%project_settings_helloWorld_group1_label%": "Hello World Project Settings",
Expand Down
41 changes: 40 additions & 1 deletion extensions/src/hello-world/contributions/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,44 @@
"groups": {},
"items": []
},
"webViewMenus": {}
"webViewMenus": {
"helloWorld.projectWebView": {
"topMenu": {
"columns": {
"helloWorld.project": {
"label": "%helloWorld_webViewMenu_project%",
"order": 1
},
"helloWorld.view": {
"label": "%helloWorld_webViewMenu_view%",
"order": 2
}
},
"groups": {
"helloWorld.projectGroup": {
"column": "helloWorld.project",
"order": 1
},
"helloWorld.viewGroup": {
"column": "helloWorld.view",
"order": 1
}
},
"items": [
{
"label": "%helloWorld_webViewMenu_deleteProject%",
"group": "helloWorld.projectGroup",
"order": 1,
"command": "helloWorld.deleteProjectByWebViewId"
},
{
"label": "%helloWorld_webViewMenu_openViewer%",
"group": "helloWorld.viewGroup",
"order": 1,
"command": "helloWorld.openViewerByWebViewId"
}
]
}
}
}
}
129 changes: 102 additions & 27 deletions extensions/src/hello-world/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import helloWorldReactWebView2 from './web-views/hello-world-2.web-view?inline';
import helloWorldReactWebView2Styles from './web-views/hello-world-2.web-view.scss?inline';
import helloWorldHtmlWebView from './web-views/hello-world.web-view.html?inline';
import HelloWorldProjectDataProviderEngineFactory from './models/hello-world-project-data-provider-engine-factory.model';
import helloWorldProjectWebView from './web-views/hello-world-project.web-view?inline';
import helloWorldProjectWebViewStyles from './web-views/hello-world-project.web-view.scss?inline';
import helloWorldProjectWebView from './web-views/hello-world-project/hello-world-project.web-view?inline';
import helloWorldProjectWebViewStyles from './web-views/hello-world-project/hello-world-project.web-view.scss?inline';
import helloWorldProjectViewerWebView from './web-views/hello-world-project/hello-world-project-viewer.web-view?inline';
import { HTML_COLOR_NAMES } from './util';

/** User data storage key for all hello world project data */
Expand Down Expand Up @@ -103,11 +104,7 @@ const helloWorldProjectWebViewProvider: IWebViewProviderWithType = {
);

// We know that the projectId (if present in the state) will be a string.
const projectId =
getWebViewOptions.projectId ||
// eslint-disable-next-line no-type-assertion/no-type-assertion
(savedWebView.state?.projectId as string) ||
undefined;
const projectId = getWebViewOptions.projectId || savedWebView.projectId || undefined;
return {
title: projectId
? `Hello World Project: ${
Expand All @@ -117,10 +114,7 @@ const helloWorldProjectWebViewProvider: IWebViewProviderWithType = {
...savedWebView,
content: helloWorldProjectWebView,
styles: helloWorldProjectWebViewStyles,
state: {
...savedWebView.state,
projectId,
},
projectId,
};
},
};
Expand All @@ -144,19 +138,53 @@ async function openHelloWorldProjectWebView(
includeProjectTypes: '^helloWorld$',
});
}
if (projectIdForWebView) {
const options: HelloWorldProjectViewerOptions = { projectId: projectIdForWebView };
return papi.webViews.getWebView(
helloWorldProjectWebViewProvider.webViewType,
undefined,
options,
);
}
return undefined;
if (!projectIdForWebView) return undefined;

const options: HelloWorldProjectViewerOptions = { projectId: projectIdForWebView };
return papi.webViews.getWebView(helloWorldProjectWebViewProvider.webViewType, undefined, options);
}

// #endregion

function selectProjectToDelete(): Promise<string | undefined> {
return papi.dialogs.selectProject({
includeProjectTypes: 'helloWorld',
title: 'Delete Hello World Project',
prompt: 'Please choose a project to delete:',
});
}

/**
* Simple web view provider that provides helloWorld project viewer web views when papi requests
* them
*/
const helloWorldProjectViewerProvider: IWebViewProviderWithType = {
webViewType: 'helloWorld.projectViewer',
async getWebView(
savedWebView: SavedWebViewDefinition,
getWebViewOptions: HelloWorldProjectViewerOptions,
): Promise<WebViewDefinition | undefined> {
if (savedWebView.webViewType !== this.webViewType)
throw new Error(
`${this.webViewType} provider received request to provide a ${savedWebView.webViewType} web view`,
);

// We know that the projectId (if present in the state) will be a string.
const projectId = getWebViewOptions.projectId || savedWebView.projectId || undefined;
return {
title: projectId
? `Hello World Project Viewer: ${
(await papi.projectLookup.getMetadataForProject(projectId)).name ?? projectId
}`
: 'Hello World Project Viewer',
...savedWebView,
content: helloWorldProjectViewerWebView,
styles: helloWorldProjectWebViewStyles,
projectId,
};
},
};

/** Number of times the `helloWorld` function has been called */
let helloWorldCount = 0;
/** Emitter to inform subscribers when `helloWorld` is called */
Expand Down Expand Up @@ -226,13 +254,25 @@ export async function activate(context: ExecutionActivationContext): Promise<voi
const deleteHelloWorldProjectPromise = papi.commands.registerCommand(
'helloWorld.deleteProject',
async (projectId) => {
const projectIdToDelete =
projectId ??
(await papi.dialogs.selectProject({
includeProjectTypes: 'helloWorld',
title: 'Delete Hello World Project',
prompt: 'Please choose a project to delete:',
}));
const projectIdToDelete = projectId ?? (await selectProjectToDelete());

if (!projectIdToDelete) return false;

// TODO: close web views if this is successful (we don't currently have a way to close them or
// to query for open ones)
return helloWorldProjectDataProviderEngineFactory.deleteProject(projectIdToDelete);
},
);

const deleteHelloWorldProjectByWebViewIdPromise = papi.commands.registerCommand(
'helloWorld.deleteProjectByWebViewId',
async (webViewId) => {
let projectId: string | undefined;
if (webViewId) {
const webViewDefinition = await papi.webViews.getSavedWebViewDefinition(webViewId);
projectId = webViewDefinition?.projectId;
}
const projectIdToDelete = projectId ?? (await selectProjectToDelete());

if (!projectIdToDelete) return false;

Expand All @@ -242,6 +282,33 @@ export async function activate(context: ExecutionActivationContext): Promise<voi
},
);

const openHelloWorldProjectViewerByWebViewIdPromise = papi.commands.registerCommand(
'helloWorld.openViewerByWebViewId',
async (webViewId) => {
let projectId: string | undefined;
if (webViewId) {
const webViewDefinition = await papi.webViews.getSavedWebViewDefinition(webViewId);
projectId = webViewDefinition?.projectId;
}
const projectIdForWebView =
projectId ??
(await papi.dialogs.selectProject({
includeProjectTypes: 'helloWorld',
title: 'Open Hello World Project Viewer',
prompt: 'Please choose a project for which to open the viewer:',
}));

if (!projectIdForWebView) return undefined;

const options: HelloWorldProjectViewerOptions = { projectId: projectIdForWebView };
return papi.webViews.getWebView(
helloWorldProjectViewerProvider.webViewType,
{ type: 'float', position: 'center' },
options,
);
},
);

const helloWorldPersonNamePromise = papi.settings.registerValidator(
'helloWorld.personName',
async (newValue) => typeof newValue === 'string',
Expand All @@ -262,6 +329,11 @@ export async function activate(context: ExecutionActivationContext): Promise<voi
helloWorldProjectWebViewProvider,
);

const helloWorldProjectViewerProviderPromise = papi.webViewProviders.register(
helloWorldProjectViewerProvider.webViewType,
helloWorldProjectViewerProvider,
);

const htmlWebViewProviderPromise = papi.webViewProviders.register(
htmlWebViewProvider.webViewType,
htmlWebViewProvider,
Expand Down Expand Up @@ -316,9 +388,12 @@ export async function activate(context: ExecutionActivationContext): Promise<voi
context.registrations.add(
await helloWorldPdpefPromise,
await helloWorldProjectWebViewProviderPromise,
await helloWorldProjectViewerProviderPromise,
await openHelloWorldProjectPromise,
await createNewHelloWorldProjectPromise,
await deleteHelloWorldProjectPromise,
await deleteHelloWorldProjectByWebViewIdPromise,
await openHelloWorldProjectViewerByWebViewIdPromise,
await helloWorldPersonNamePromise,
await helloWorldHeaderSizePromise,
await helloWorldHeaderColorPromise,
Expand Down
20 changes: 20 additions & 0 deletions extensions/src/hello-world/src/types/hello-world.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,26 @@ declare module 'papi-shared-types' {
* @returns `true` if successfully deleted
*/
'helloWorld.deleteProject': (projectId?: string) => Promise<boolean>;
/**
* Deletes a Hello World project
*
* Note: this command is intended to work from the web view menu
*
* @param webViewId Optional web view ID of a hello world project web view associated with the
* project to delete. Prompts the user to select a project if not provided
* @returns `true` if successfully deleted
*/
'helloWorld.deleteProjectByWebViewId': (webViewId?: string) => Promise<boolean>;
/**
* Opens the viewer for a Hello World project
*
* Note: this command is intended to work from the web view menu
*
* @param webViewId Optional web view ID of a hello world project web view associated with the
* project to open the viewer for. Prompts the user to select a project if not provided
* @returns WebView id for new viewer or `undefined` if the user canceled the dialog
*/
'helloWorld.openViewerByWebViewId': (webViewId?: string) => Promise<string | undefined>;
}

export interface ProjectDataProviders {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { WebViewProps } from '@papi/core';
import { useProjectData, useProjectSetting } from '@papi/frontend/react';
import { CSSProperties, useMemo } from 'react';

const namesDefault: string[] = [];

globalThis.webViewComponent = function HelloWorldProjectViewer({ projectId }: WebViewProps) {
const [names] = useProjectData('helloWorld', projectId).Names(undefined, namesDefault);

const [headerSize] = useProjectSetting('helloWorld', projectId, 'helloWorld.headerSize', 15);

const [headerColor] = useProjectSetting(
'helloWorld',
projectId,
'helloWorld.headerColor',
'Black',
);

const headerStyle = useMemo<CSSProperties>(
() => ({ fontSize: `${headerSize}pt`, color: headerColor }),
[headerSize, headerColor],
);

return (
<div className="top">
{names.map((name) => (
<div style={headerStyle}>Hello, {name}!</div>
))}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
display: flex;
align-items: center;
}

.remove-name-button {
margin-right: 10px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useProjectData, useProjectDataProvider, useProjectSetting } from '@papi
import { ComboBox } from 'platform-bible-react';
import { CSSProperties, useCallback, useMemo } from 'react';
import { HTMLColorNames } from 'hello-world';
import { HTML_COLOR_NAMES } from '../util';
import { HTML_COLOR_NAMES } from '../../util';

const namesDefault: string[] = [];

Expand All @@ -13,9 +13,10 @@ const testExtensionDataScope = {
dataQualifier: 'webViewTestExtensionData',
};

globalThis.webViewComponent = function HelloWorldProjectWebView({ useWebViewState }: WebViewProps) {
const [projectId] = useWebViewState('projectId', '');

globalThis.webViewComponent = function HelloWorldProjectWebView({
projectId,
useWebViewState,
}: WebViewProps) {
const [max, setMax] = useWebViewState('max', 1);

const pdp = useProjectDataProvider('helloWorld', projectId);
Expand Down Expand Up @@ -81,7 +82,21 @@ globalThis.webViewComponent = function HelloWorldProjectWebView({ useWebViewStat
}}
/>
</div>
<div>Names: {names.join(', ')}</div>
<div>
Names:{' '}
{names.map((name) => (
<span>
{name}
<button
type="button"
className="remove-name-button"
onClick={() => pdp?.removeName(name)}
>
-
</button>
</span>
))}
</div>
<input
value={currentName}
onChange={(e) => setCurrentName(e.target.value)}
Expand All @@ -90,17 +105,6 @@ globalThis.webViewComponent = function HelloWorldProjectWebView({ useWebViewStat
<button type="button" onClick={addCurrentName}>
Add Name
</button>
<button
type="button"
onClick={() => {
if (!pdp) return;

pdp.removeName(currentName);
setCurrentName('');
}}
>
Remove Name
</button>
<hr />
<h3 style={headerStyle}>Extension Data</h3>
<input value={extensionData} onChange={(e) => setExtensionData?.(e.target.value)} />
Expand Down
Loading

0 comments on commit 77dc12e

Please sign in to comment.