Skip to content
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
10 changes: 8 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TranslateModule } from "@ngx-translate/core";
import { CommonModule } from '@angular/common';
import { AutoAssetSrcDirective } from './services/auto-asset-src.directive';
import { SHELL_ROUTER } from "./injection-tokens";
import { loadLibChatWidget } from './utils/libchat';



Expand All @@ -32,6 +33,12 @@ export const AppModule = ({ providers, shellRouter }: { providers: any, shellRou
}

ngDoBootstrap(appRef: ApplicationRef) {
// Load chat widget first
loadLibChatWidget();
console.log('loading libchat')


// Then register web components
for (const [key, value] of selectorComponentMap) {
const customElement = createCustomElement(value, { injector: this.injector });
this.webComponentSelectorMap.set(key, customElement);
Expand All @@ -47,5 +54,4 @@ export const AppModule = ({ providers, shellRouter }: { providers: any, shellRou
}
}
return AppModule
}

}
99 changes: 99 additions & 0 deletions src/app/utils/libchat.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { loadLibChatWidget } from "./libchat"

describe('loadLibChatWidget', () => {
const LIBCHAT_HASH = '1e8f3119e6cff530e0d23e2cb1f2b2a7';

beforeEach(() => {
// Clean up any existing LibChat elements before each test
const existingDiv = document.getElementById(`libchat_${LIBCHAT_HASH}`);
if (existingDiv) {
existingDiv.remove();
}

const existingScripts = document.querySelectorAll('script[src*="load_chat.php"]');
existingScripts.forEach(script => script.remove());
});

afterEach(() => {
// Clean up after each test
const div = document.getElementById(`libchat_${LIBCHAT_HASH}`);
if (div) {
div.remove();
}

const scripts = document.querySelectorAll('script[src*="load_chat.php"]');
scripts.forEach(script => script.remove());
});

it('should create a div with the correct libchat ID', () => {
loadLibChatWidget();

const div = document.getElementById(`libchat_${LIBCHAT_HASH}`);
expect(div).toBeTruthy();
expect(div?.tagName).toBe('DIV');
});

it('should append the div to the body', () => {
loadLibChatWidget();

const div = document.getElementById(`libchat_${LIBCHAT_HASH}`);
expect(div?.parentElement).toBe(document.body);
});

it('should load the script immediately', () => {
loadLibChatWidget();

const script = document.querySelector('script[src*="load_chat.php"]');
expect(script).toBeTruthy();
expect(script?.getAttribute('src')).toBe(`https://libanswers.mit.edu/load_chat.php?hash=${LIBCHAT_HASH}`);
});

it('should append the script to the body', () => {
loadLibChatWidget();

const script = document.querySelector('script[src*="load_chat.php"]');
expect(script?.parentElement).toBe(document.body);
});

it('should not create duplicate divs if called multiple times', () => {
loadLibChatWidget();
loadLibChatWidget();
loadLibChatWidget();

const divs = document.querySelectorAll(`#libchat_${LIBCHAT_HASH}`);
expect(divs.length).toBe(1);
});

it('should not load duplicate scripts if called multiple times', () => {
loadLibChatWidget();
loadLibChatWidget();
loadLibChatWidget();

const scripts = document.querySelectorAll('script[src*="load_chat.php"]');
expect(scripts.length).toBe(1);
});

it('should log message when widget is already loaded', () => {
spyOn(console, 'log');

loadLibChatWidget();
loadLibChatWidget();

expect(console.log).toHaveBeenCalledWith('LibChat widget already loaded');
});

it('should log message when widget is successfully loaded', () => {
spyOn(console, 'log');

loadLibChatWidget();

expect(console.log).toHaveBeenCalledWith('LibChat widget loaded');
});

it('should use the correct LibChat hash in the script URL', () => {
loadLibChatWidget();

const script = document.querySelector('script[src*="load_chat.php"]');
expect(script?.getAttribute('src')).toContain(LIBCHAT_HASH);
});
});
44 changes: 22 additions & 22 deletions src/app/utils/libchat.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
/*
* This function injects the libanswers <script> for libchat into the current document.
* This function is called by src/bootstrap.ts.
*/
/**
* Loads the MIT LibChat widget into the page
* Includes duplicate check to prevent multiple instances
*/
export function loadLibChatWidget(): void {
const LIBCHAT_HASH = '1e8f3119e6cff530e0d23e2cb1f2b2a7';

export function injectLibchat() {
try {
var libchatHash = "1e8f3119e6cff530e0d23e2cb1f2b2a7";
var div = document.createElement("div");
div.id = "libchat_" + libchatHash;
document.getElementsByTagName("body")[0].appendChild(div);
var scr = document.createElement("script");
scr.src = "https://libanswers.mit.edu/load_chat.php?hash=" + libchatHash;
// we added this error handling to the libanswers-provided script for libchat
scr.onerror = (ev) => {
console.error('Libchat script failed to load', scr.src, ev);
};
setTimeout(function () {
document.getElementsByTagName("body")[0].appendChild(scr);
}, 2000);
} catch (e) {
console.error('error injecting libchat', e)
}
// Check if LibChat is already loaded
if (document.getElementById(`libchat_${LIBCHAT_HASH}`)) {
console.log('LibChat widget already loaded');
return;
}

// Create the libchat div
const div = document.createElement('div');
div.id = `libchat_${LIBCHAT_HASH}`;
document.body.appendChild(div);

// Load the libchat script
const script = document.createElement('script');
script.src = `https://libanswers.mit.edu/load_chat.php?hash=${LIBCHAT_HASH}`;
document.body.appendChild(script);
console.log('LibChat widget loaded');
}
32 changes: 0 additions & 32 deletions src/app/utils/matomo.ts

This file was deleted.

19 changes: 6 additions & 13 deletions src/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import "@angular/compiler";
import '@angular/compiler';
import { AppModule } from './app/app.module';
import { bootstrap } from "@angular-architects/module-federation-tools";
import { injectMatomo } from './app/utils/matomo';
import { injectLibchat } from "./app/utils/libchat";
import { bootstrap } from '@angular-architects/module-federation-tools';

export const bootstrapRemoteApp = (bootstrapOptions: any) => {
// Keep bootstrap minimal: always inject Matomo from helper.
injectMatomo();
injectLibchat();

return bootstrap(AppModule(bootstrapOptions), {
production: true,
appType: 'microfrontend'
}).then(r => {
appType: 'microfrontend',
}).then((r) => {
console.log('custom remote app bootstrap success!', r);
return r
return r;
});
}

};
8 changes: 3 additions & 5 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compileOnSave": false,
"compilerOptions": {
"types": ["jasmine"],
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
Expand All @@ -18,15 +19,12 @@
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
"lib": ["ES2022", "dom"]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
}