Skip to content
Draft
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
31 changes: 24 additions & 7 deletions ReactViewResources/Loader/Internal/Loader.View.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,36 @@ import { ViewMetadata } from "./ViewMetadata";
import { ViewPortalsCollection } from "./ViewPortalsCollection";
import { addView, deleteView } from "./ViewsCollection";

export function createView(componentClass: any, properties: {}, view: ViewMetadata, componentName: string) {
componentClass.contextType = PluginsContext;
export function createView(componentClass: any, properties: {}, view: ViewMetadata, componentName: string, componentNativeObject: any, componentNativeObjectName: string) {
return <View componentClass={componentClass} properties={properties} view={view} componentName={componentName} componentNativeObject={componentNativeObject} componentNativeObjectName={componentNativeObjectName} />;
}

interface IViewProps {
componentClass: any;
properties: {};
view: ViewMetadata;
componentName: string
componentNativeObject: any;
componentNativeObjectName: string
}

const View = ({ componentClass, properties, view, componentName, componentNativeObject, componentNativeObjectName }: IViewProps) => {
componentClass.contextType = PluginsContext;
const makeResourceUrl = (resourceKey: string, ...params: string[]) => formatUrl(view.name, resourceKey, ...params);

const pluginsContext = React.useRef(new PluginsContextHolder(Array.from(view.modules.values())));

React.useEffect(() => {
return () => {
pluginsContext.current.dispose();
pluginsContext.current = null!;
}
}, []);

return (
<ViewMetadataContext.Provider value={view}>
<PluginsContext.Provider value={new PluginsContextHolder(Array.from(view.modules.values()))}>
<PluginsContext.Provider value={pluginsContext.current}>
<ResourceLoader.Provider value={makeResourceUrl}>
<ViewPortalsCollection views={view.childViews}
viewAdded={onChildViewAdded}
viewRemoved={onChildViewRemoved}
viewErrorRaised={onChildViewErrorRaised} />
{React.createElement(componentClass, { ref: e => view.modules.set(componentName, e), ...properties })}
</ResourceLoader.Provider>
</PluginsContext.Provider>
Expand Down
6 changes: 4 additions & 2 deletions ReactViewResources/Loader/Internal/NativeAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ export function notifyViewLoaded(viewName: string, id: string): void {
}

export function notifyViewDestroyed(viewName: string): void {
withAPI(api => api.notifyViewDestroyed(viewName));
}
withAPI(api => {
api.notifyViewDestroyed(viewName)
});
}
57 changes: 45 additions & 12 deletions ReactViewResources/Loader/Internal/ResourcesLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,51 @@ export function loadStyleSheet(stylesheet: string, containerElement: Element, ma
}

function waitForLoad<T extends HTMLElement>(element: T, url: string, timeout: number): Promise<T> {
return new Promise((resolve) => {
const timeoutHandle = setTimeout(
() => {
if (isDebugModeEnabled) {
showWarningMessage(`Timeout loading resouce: '${url}'. If you paused the application to debug, you may disregard this message.`);
}
},
timeout);

element.addEventListener("load", () => {
clearTimeout(timeoutHandle);
return new Promise((resolve, reject) => {
// const timeoutHandle = setTimeout(
// () => {
// if (isDebugModeEnabled) {
// showWarningMessage(`Timeout loading resouce: '${url}'. If you paused the application to debug, you may disregard this message.`);
// }
// },
// timeout);

// element.addEventListener("load", () => {
// clearTimeout(timeoutHandle);
// resolve(element);
// });

let timeoutHandle: number | undefined;

// Define handlers ahead of time
const loadHandler = () => {
cleanup();
resolve(element);
});
};

const errorHandler = () => {
cleanup();
reject(new Error(`Failed to load resource: '${url}'`));
};

// This central function is key to disposing of everything
const cleanup = () => {
clearTimeout(timeoutHandle);
element.removeEventListener("load", loadHandler);
element.removeEventListener("error", errorHandler);
};

// Set the timeout to reject the promise and clean up
timeoutHandle = setTimeout(() => {
cleanup();
const message = `Timeout loading resource: '${url}'.`;
if (isDebugModeEnabled) {
showWarningMessage(`${message} If you paused the application to debug, you may disregard this message.`);
}
reject(new Error(message));
}, timeout);

element.addEventListener("load", loadHandler);
element.addEventListener("error", errorHandler);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export function createPropertiesProxy(rootElement: Element, objProperties: {}, n
} else {
proxy[key] = async function () {
const nativeObject = window[nativeObjName] || await bindNativeObject(nativeObjName);

const result = nativeObject[key].apply(window, arguments);

if (componentRenderedWaitTask) {
Expand Down
5 changes: 4 additions & 1 deletion ReactViewResources/Loader/Internal/ViewsCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { modulesFunctionName } from "./Environment";

const views = new Map<string, ViewMetadata>();

window["my-views"] = views; // for debugging
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete


export function addView(name: string, view: ViewMetadata): void {
views.set(name, view);
}
Expand All @@ -23,6 +25,7 @@ export function getView(viewName: string): ViewMetadata {
return view;
}

console.log("modulesFunctionName", modulesFunctionName);
window[modulesFunctionName] = function getModule(viewName: string, id: string, moduleName: string) {
const view = views.get(viewName);
if (view && view.id.toString() === id) {
Expand All @@ -35,7 +38,7 @@ window[modulesFunctionName] = function getModule(viewName: string, id: string, m

return new Proxy({}, {
get: function () {
// return a dummy function, call will be ingored, but no exception thrown
// return a dummy function, call will be ignored, but no exception thrown
return new Function();
}
});
Expand Down
11 changes: 5 additions & 6 deletions ReactViewResources/Loader/Loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export function loadComponent(
// wait for the stylesheet to load before first render
await defaultStylesheetLoadTask.promise;
}

view = tryGetView(frameName)!;
if (!view) {
return; // view was probably unloaded
Expand Down Expand Up @@ -173,21 +173,20 @@ export function loadComponent(
if (!componentClass) {
throw new Error(`Component ${componentName} is not defined or does not have a default class`);
}

const { createView } = await import("./Internal/Loader.View");

const viewElement = createView(componentClass, properties, view, componentName);
const viewElement = createView(componentClass, properties, view, componentName, componentNativeObject, componentNativeObjectName);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

const render = view.renderHandler;
if (!render) {
throw new Error(`View ${view.name} render handler is not set`);
}

await render(viewElement);

await waitForNextPaint();

if (cacheEntry) {
storeViewRenderInCache(view, cacheEntry, maxPreRenderedCacheEntries); // dont need to await
//storeViewRenderInCache(view, cacheEntry, maxPreRenderedCacheEntries); // dont need to await
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check

}

// queue view loaded notification to run after all other pending promise notifications (ensure order)
Expand Down
5 changes: 5 additions & 0 deletions ReactViewResources/Loader/Public/PluginsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export class PluginsContextHolder implements IPluginsContext {
public getPluginInstance<T>(_class: Type<T>) {
return this.pluginInstances.get(_class.name);
}

public dispose(): void {
this.pluginInstances.clear();
this.pluginInstances = null!;
}
}

export const PluginsContext = React.createContext<PluginsContextHolder>(null!);
Expand Down
Loading