diff --git a/src/content.tsx b/src/content.tsx
index f65dfa7..f723e02 100644
--- a/src/content.tsx
+++ b/src/content.tsx
@@ -1,40 +1,66 @@
import { Gitako } from 'components/Gitako'
+import { IN_PRODUCTION_MODE } from 'env'
import React, { useCallback } from 'react'
import { createRoot } from 'react-dom/client'
import { insertMountPoint, insertSideBarMountPoint } from 'utils/DOMHelper'
import { useAfterRedirect } from 'utils/hooks/useFastRedirect'
+import { waitForNext } from 'utils/waitForNextEvent'
import './content.scss'
-if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', init)
-} else {
- init()
-}
-
-async function init() {
- await injectStyles(browser.runtime.getURL('content.css'))
+const renderReact = () => {
const mountPoint = insertSideBarMountPoint()
const MountPointWatcher = () => {
useAfterRedirect(useCallback(() => insertMountPoint(() => mountPoint), []))
return null
}
-
- createRoot(mountPoint).render(
+ const root = createRoot(mountPoint)
+ root.render(
<>
>,
)
+
+ return () => {
+ root.unmount()
+ }
}
// injects a copy of stylesheets so that other extensions(e.g. dark reader) could read
// resolves when style is loaded to prevent render without proper styles
-async function injectStyles(url: string) {
- return new Promise(resolve => {
+const injectStyles = (url: string) =>
+ new Promise<() => void>(resolve => {
const linkElement = document.createElement('link')
linkElement.setAttribute('rel', 'stylesheet')
linkElement.setAttribute('href', url)
- linkElement.onload = () => resolve()
+ const unload = () => {
+ linkElement.remove()
+ }
+ linkElement.onload = () => resolve(unload)
document.head.appendChild(linkElement)
})
-}
+
+const GitakoExclusiveEventType = 'GITAKO_EXCLUSIVE_EVENT'
+const GitakoMountedEventType = 'GITAKO_MOUNTED_EVENT'
+
+Promise.resolve()
+ .then(() =>
+ document.readyState === 'loading' ? waitForNext.documentEvent('DOMContentLoaded') : null,
+ )
+ .then(() =>
+ Promise.all([injectStyles(browser.runtime.getURL('content.css')), renderReact()]).then(
+ ([unmountStyles, unmountReact]) =>
+ () =>
+ Promise.all([unmountStyles(), unmountReact()]),
+ ),
+ )
+ .then(unmount => {
+ document.dispatchEvent(new CustomEvent(GitakoMountedEventType))
+ if (IN_PRODUCTION_MODE) {
+ waitForNext.documentEvent(GitakoExclusiveEventType).then(unmount)
+ } else {
+ waitForNext
+ .documentEvent(GitakoMountedEventType)
+ .then(() => document.dispatchEvent(new CustomEvent(GitakoExclusiveEventType)))
+ }
+ })
diff --git a/src/utils/waitForNextEvent.ts b/src/utils/waitForNextEvent.ts
new file mode 100644
index 0000000..43c7823
--- /dev/null
+++ b/src/utils/waitForNextEvent.ts
@@ -0,0 +1,28 @@
+export const waitForNextDocumentEvent = (
+ type: EnumString,
+ options?: boolean | AddEventListenerOptions,
+) =>
+ new Promise(resolve => {
+ const listener = (ev: DocumentEventMap[K] | Event) => {
+ document.removeEventListener(type, listener, options)
+ resolve(ev)
+ }
+ document.addEventListener(type, listener, options)
+ })
+
+export const waitForNextWindowEvent = (
+ type: EnumString,
+ options?: boolean | AddEventListenerOptions,
+) =>
+ new Promise(resolve => {
+ const listener = (ev: WindowEventMap[K] | Event) => {
+ window.removeEventListener(type, listener, options)
+ resolve(ev)
+ }
+ window.addEventListener(type, listener, options)
+ })
+
+export const waitForNext = {
+ documentEvent: waitForNextDocumentEvent,
+ windowEvent: waitForNextWindowEvent,
+}