feat: auto-unmount prod version when dev version is enabled
Some checks failed
Tests / build (push) Has been cancelled
Tests / e2e-test (push) Has been cancelled
Tests / unit-test (push) Has been cancelled

This commit is contained in:
EnixCoda 2025-01-30 19:53:00 +08:00
parent 0c9acf7a18
commit dfc6311020
2 changed files with 68 additions and 14 deletions

View file

@ -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(
<>
<MountPointWatcher />
<Gitako />
</>,
)
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<void>(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)))
}
})

View file

@ -0,0 +1,28 @@
export const waitForNextDocumentEvent = <K extends keyof DocumentEventMap>(
type: EnumString<K>,
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 = <K extends keyof WindowEventMap>(
type: EnumString<K>,
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,
}