Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #86: 快速切片按钮不加载 #88

Merged
merged 5 commits into from
Oct 25, 2024
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"@types/react-dom": "18.2.15",
"@types/semver": "^7.5.8",
"@webgpu/types": "^0.1.49",
"async-mutex": "^0.5.0",
"dotenv": "^16.4.5",
"esbuild": "^0.20.2",
"gify-parse": "^1.0.7",
Expand Down
35 changes: 20 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions src/features/jimaku/components/ButtonArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { toast } from "sonner/dist";
import { sendMessager } from "~utils/messaging";
import { sendForward } from "~background/forwards";
import { sleep } from "~utils/misc";
import { useQuerySelector } from "~hooks/dom";

export type ButtonAreaProps = {
clearJimaku: VoidFunction
Expand Down Expand Up @@ -50,6 +51,11 @@ function ButtonArea({ clearJimaku, jimakus }: ButtonAreaProps): JSX.Element {
sendForward('pages', 'jimaku-summarize', { roomId: info.room, jimakus: jimakus.map(j => j.text) })
}

const upperHeaderAreaElement = useQuerySelector(upperHeaderArea)
if (info.isTheme && upperHeaderAreaElement === null) {
console.warn(`找不到上方标题界面元素 ${upperHeaderArea},可能无法插入切換按鈕列表的按钮`)
}

return (
<Fragment>
{show && (
Expand Down Expand Up @@ -80,11 +86,11 @@ function ButtonArea({ clearJimaku, jimakus }: ButtonAreaProps): JSX.Element {
)}
</div>
)}
{info.isTheme && document.querySelector(upperHeaderArea) !== null && createPortal(
{info.isTheme && upperHeaderAreaElement !== null && createPortal(
<TailwindScope>
<ButtonSwitchList switched={show} onClick={() => setShow(!show)} />
</TailwindScope>,
document.querySelector(upperHeaderArea)
upperHeaderAreaElement
)}
</Fragment>
)
Expand Down
2 changes: 1 addition & 1 deletion src/features/jimaku/components/JimakuList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type React from "react";
import { Item, Menu, useContextMenu, type ItemParams } from 'react-contexify';
import { toast } from 'sonner/dist';
import { useKeepBottom } from '~hooks/keep-bottom';
import { useKeepBottom } from '~hooks/dom';
import { useScrollOptimizer } from '~hooks/optimizer';
import { getSettingStorage, setSettingStorage } from '~utils/storage';

Expand Down
3 changes: 2 additions & 1 deletion src/features/jimaku/components/JimakuVisibleButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createPortal } from 'react-dom'
import styled from '@emotion/styled'
import type { SettingSchema } from "~options/fragments/developer"
import { useQuerySelector } from "~hooks/dom"

export type JimakuVisibleButtonProps = {
toggle: VoidFunction
Expand All @@ -21,7 +22,7 @@ const Div = styled.div`

function JimakuVisibleButton({ toggle, visible, dev }: JimakuVisibleButtonProps): JSX.Element {

const element = document.querySelector(dev.elements.upperInputArea)
const element = useQuerySelector(dev.elements.upperInputArea)
if (!element) {
console.warn(`找不到元素 ${dev.elements.upperInputArea},部分功能可能无法正常工作`)
return null
Expand Down
3 changes: 2 additions & 1 deletion src/features/jimaku/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { FeatureHookRender } from "..";
import JimakuCaptureLayer from './components/JimakuCaptureLayer';
import JimakuAreaSkeleton from './components/JimakuAreaSkeleton';
import JimakuAreaSkeletonError from './components/JimakuAreaSkeletonError';
import { useQuerySelector } from "~hooks/dom";



Expand All @@ -35,7 +36,7 @@ export function App(): JSX.Element {

const dev = settings['settings.developer']

const danmakuArea = document.querySelector(dev.elements.danmakuArea)
const danmakuArea = useQuerySelector(dev.elements.danmakuArea)
if (!danmakuArea) {
toast.warning(`找不到弹幕区域 ${dev.elements.danmakuArea},部分功能可能无法正常工作`)
}
Expand Down
3 changes: 2 additions & 1 deletion src/features/recorder/components/RecorderButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useContext, useState, type MutableRefObject } from "react"
import TailwindScope from "~components/TailwindScope"
import ContentContext from "~contexts/ContentContexts"
import RecorderFeatureContext from "~contexts/RecorderFeatureContext"
import { useQuerySelector } from "~hooks/dom"
import { useForceRender } from "~hooks/force-update"
import { useComputedStyle, useContrast } from "~hooks/styles"
import type { Recorder } from "~types/media"
Expand All @@ -25,7 +26,7 @@ function RecorderButton(props: RecorderButtonProps): JSX.Element {
const [recording, setRecording] = useState(false)
const update = useForceRender()
const { headInfoArea } = settings['settings.developer'].elements
const { backgroundImage } = useComputedStyle(document.querySelector(headInfoArea))
const { backgroundImage } = useComputedStyle(useQuerySelector(headInfoArea))

useInterval(() => {
if (!recorder.current) return
Expand Down
9 changes: 7 additions & 2 deletions src/features/recorder/components/RecorderLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { randomString } from '~utils/misc'
import createRecorder from "../recorders"
import ProgressText from "./ProgressText"
import RecorderButton from "./RecorderButton"
import { useQuerySelector } from "~hooks/dom"

export type RecorderLayerProps = {
urls: StreamUrls
Expand Down Expand Up @@ -202,7 +203,11 @@ function RecorderLayer(props: RecorderLayerProps): JSX.Element {
screenshot()
})

if (hiddenUI || document.querySelector(upperHeaderArea) === null) {
const upperHeaderAreaElement = useQuerySelector(upperHeaderArea)
if (hiddenUI || upperHeaderAreaElement === null) {
if (!hiddenUI) {
console.warn(upperHeaderArea, 'is not attached yet')
}
return null
}

Expand All @@ -212,7 +217,7 @@ function RecorderLayer(props: RecorderLayerProps): JSX.Element {
record={clipRecord}
screenshot={screenshot}
/>,
document.querySelector(upperHeaderArea)
upperHeaderAreaElement
)

}
Expand Down
40 changes: 40 additions & 0 deletions src/hooks/keep-bottom.ts → src/hooks/dom.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useInterval } from "@react-hooks-library/core"
import { useCallback, useEffect, useRef, useState } from 'react'

/**
Expand Down Expand Up @@ -57,4 +58,43 @@ export function useKeepBottom<E extends HTMLElement>(enabled: boolean, calculate
}, [])

return { ref: refCallback, element: ref, keepBottom }
}




/**
* Custom hook that queries the DOM for an element matching the given selector.
* Optionally, it can remount and re-query the DOM at a specified interval.
*
* @param {string} selector - The CSS selector to query the DOM.
* @param {boolean} [remount=false] - If true, the hook will re-query the DOM at the specified interval.
* @returns {Element | null} - The DOM element matching the selector, or null if no element is found.
*
* @example
* // Usage in a React component
* const MyComponent = () => {
* const element = useQuerySelector('#my-element', true);
*
* useEffect(() => {
* if (element) {
* console.log('Element found:', element);
* }
* }, [element]);
*
* return <div>Check the console for the element.</div>;
* };
*/
export function useQuerySelector<E extends Element>(selector: string, remount: boolean = false): E | null {

const [element, setElement] = useState<Element | null>(document.querySelector(selector))

useInterval(() => {
const el = document.querySelector(selector)
if (el) {
setElement(el)
}
}, 500, { paused: !remount && !!element, immediate: true })

return element as E
}
2 changes: 1 addition & 1 deletion src/hooks/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useMemo } from "react";


export function useComputedStyle(element: Element): CSSStyleDeclaration {
return useMemo(() => window.getComputedStyle(element), [element]);
return useMemo(() => element ? window.getComputedStyle(element) : {} as CSSStyleDeclaration, [element]);
}

export function useContrast(background: Element) {
Expand Down
6 changes: 4 additions & 2 deletions src/options/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ function SettingPage(): JSX.Element {
if (!(settings instanceof Object)) {
throw new Error('导入的设定文件格式错误。')
}
if (Object.keys(settings).length === 0) {
throw new Error('导入的设定文件格式错误。')
}
if (!Object.keys(settings).every(key => (fragmentKeys as string[]).includes(key))) {
throw new Error('导入的设定文件格式错误。')
}
Expand All @@ -117,8 +120,7 @@ function SettingPage(): JSX.Element {
})();
toast.promise(importing, {
loading: '正在导入设定...',
success: '设定已经导入成功。',
error: err => '导入设定失败: ' + err.message
success: '设定已经导入成功。'
})
await importing
if (!processing) {
Expand Down
17 changes: 12 additions & 5 deletions tests/helpers/bilibili-api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { request, type APIRequestContext } from "@playwright/test";
import { sendInternal } from "~background/messages";
import { Mutex } from 'async-mutex';
import type { StreamUrls } from "~background/messages/get-stream-urls";
import type { V1Response, StreamUrlResponse } from "~types/bilibili";
import type { StreamUrlResponse, V1Response } from "~types/bilibili";
import logger from "./logger";

export interface LiveRoomInfo {
Expand Down Expand Up @@ -35,6 +35,8 @@ export default class BilbiliApi {
return new BilbiliApi(context)
}

private readonly mutex = new Mutex()

/**
* 构造BilbiliApi的新实例。
* @param context - API请求的上下文。
Expand All @@ -48,9 +50,14 @@ export default class BilbiliApi {
* @throws 如果获取操作失败,则抛出错误。
*/
private async fetch<T = any>(path: string): Promise<T> {
const res = await this.context.get(path)
if (!res.ok()) throw new Error(`获取bilibili API失败:${res.statusText()}`)
return await res.json()
const release = await this.mutex.acquire()
try {
const res = await this.context.get(path)
if (!res.ok()) throw new Error(`获取bilibili API失败:${res.statusText()}`)
return await res.json()
} finally {
release()
}
}

/**
Expand Down
Loading
Loading