@vnejs/plugins.views.scenario.interface 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ export const SUBSCRIBE_EVENTS = {
2
+ SHOW: "vne:interface:show",
3
+ HIDE: "vne:interface:hide",
4
+
5
+ SHOW_CHANGED: "vne:interface:show_changed",
6
+
7
+ VIEW_SHOW: "vne:interface:view_show",
8
+ VIEW_HIDE: "vne:interface:view_hide",
9
+ VIEW_UPDATE: "vne:interface:view_update",
10
+
11
+ TEXT: "vne:interface:text",
12
+ TEXT_HIDE: "vne:interface:text_hide",
13
+ TEXT_CHANGED: "vne:interface:text_changed",
14
+
15
+ SPEAKER_CHANGED: "vne:interface:speaker_changed",
16
+
17
+ VISIBLE: "vne:interface:visible",
18
+ VISIBLE_CHANGED: "vne:interface:visible_changed",
19
+
20
+ VIEW: "vne:interface:view",
21
+ VIEW_CHANGED: "vne:interface:view_changed",
22
+
23
+ HISTORY: "vne:interface:history",
24
+ HISTORY_BACK: "vne:interface:history_back",
25
+
26
+ GAMEMENU: "vne:interface:gamemenu",
27
+
28
+ INTERACT: "vne:interface:interact",
29
+
30
+ STATE_CHANGED: "vne:interface:state_changed",
31
+ };
@@ -0,0 +1,37 @@
1
+ import { getVneLength } from "@vnejs/uis.utils";
2
+
3
+ export const DEFAULT_VIEW = "line";
4
+
5
+ export const LOC_LABEL = "interface";
6
+
7
+ export const TRANSITION = 500;
8
+ export const ZINDEX = 1000;
9
+
10
+ const createBackdropProps = (opacity) => ({ type: "bottom", size: 600, transition: TRANSITION, opacity });
11
+ const createFlatBackdropPosition = (margin, opacity) => ({ top: margin, left: margin, right: margin, bottom: margin, transition: TRANSITION, opacity });
12
+ const createFlatBackdropStyle = (borderRadius) => ({ background: "rgba(0, 0, 0, 0.6)", borderRadius, transition: `${TRANSITION}ms` });
13
+
14
+ export const VIEW_PROPS = {
15
+ adv: {
16
+ backdrop: { props: createBackdropProps(1), flatPosition: createFlatBackdropPosition(0, 0), flatStyle: createFlatBackdropStyle(0) },
17
+ position: { bottom: 144, left: 600, right: 600 },
18
+ flex: { direction: "column", gap: 24 },
19
+ speaker: { size: 72, weight: "bold" },
20
+ text: { size: 60, height: 300 },
21
+ },
22
+ wall: {
23
+ backdrop: { props: createBackdropProps(0), flatPosition: createFlatBackdropPosition(120, 1), flatStyle: createFlatBackdropStyle(getVneLength(48)) },
24
+ position: { top: 180, left: 180, right: 180, bottom: 264 },
25
+ flex: { direction: "column", gap: 24 },
26
+ scrollBar: { height: 1680, scrollToEnd: true, hideScrolbars: true, disableScroll: true },
27
+ speaker: { size: 72, weight: "bold" },
28
+ text: { size: 60 },
29
+ },
30
+ line: {
31
+ backdrop: { props: createBackdropProps(0), flatPosition: createFlatBackdropPosition(0, 1), flatStyle: createFlatBackdropStyle(0) },
32
+ position: { isCentered: true },
33
+ flex: { direction: "column", gap: 60 },
34
+ speaker: { size: 72, weight: "bold" },
35
+ text: { isCentered: true, size: 60, width: 3120 },
36
+ },
37
+ };
package/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import { regPlugin } from "@vnejs/shared";
2
+
3
+ import { SUBSCRIBE_EVENTS } from "./const/events";
4
+ import * as params from "./const/params";
5
+
6
+ import { InterfaceController } from "./modules/controller";
7
+ import { Interface } from "./modules/interface";
8
+ import { InterfaceView } from "./modules/view";
9
+
10
+ regPlugin("INTERFACE", { events: SUBSCRIBE_EVENTS, params }, [InterfaceController, Interface, InterfaceView]);
@@ -0,0 +1,74 @@
1
+ import { ModuleController } from "@vnejs/module.components";
2
+
3
+ export class InterfaceController extends ModuleController {
4
+ name = "interface.controller";
5
+
6
+ emitInteract = () => this.emit(this.EVENTS.INTERFACE.INTERACT);
7
+
8
+ updateEvent = this.EVENTS.INTERFACE.VIEW_UPDATE;
9
+ controls = {
10
+ [this.CONST.CONTROLS.BUTTONS.INTERACT]: this.emitInteract,
11
+ [this.CONST.CONTROLS.BUTTONS.ACCEPT]: this.emitInteract,
12
+ };
13
+ controlsUnvisible = {};
14
+ controlsIndex = this.PARAMS.INTERFACE.ZINDEX;
15
+
16
+ subscribe = () => {
17
+ this.on(this.EVENTS.INTERFACE.VIEW_SHOW, this.onShow);
18
+ this.on(this.EVENTS.INTERFACE.VIEW_HIDE, this.onHide);
19
+
20
+ this.on(this.EVENTS.INTERFACE.SHOW_CHANGED, this.onShowChanged);
21
+ this.on(this.EVENTS.INTERFACE.VIEW_CHANGED, this.onViewChanged);
22
+ this.on(this.EVENTS.INTERFACE.TEXT_CHANGED, this.onTextChanged);
23
+ this.on(this.EVENTS.INTERFACE.SPEAKER_CHANGED, this.onSpeakerChanged);
24
+ this.on(this.EVENTS.INTERFACE.STATE_CHANGED, this.onStateChanged);
25
+ this.on(this.EVENTS.INTERFACE.VISIBLE_CHANGED, this.onVisibleChanged);
26
+ };
27
+
28
+ init = () => this.emit(this.EVENTS.INTERFACE.VIEW, { view: this.PARAMS.INTERFACE.DEFAULT_VIEW, isForce: true });
29
+
30
+ onShowChanged = ({ isForce = false } = {}) => {
31
+ const { isShow = false } = this.globalState.interface;
32
+ const event = isShow ? this.EVENTS.INTERFACE.VIEW_SHOW : this.EVENTS.INTERFACE.VIEW_HIDE;
33
+
34
+ if (this.state.isShow !== isShow) return this.emit(event, { isForce });
35
+ };
36
+ onViewChanged = ({ isForce = false } = {}) => {
37
+ const { view = "" } = this.globalState.interface;
38
+
39
+ if (this.state.view !== view) return this.updateStateAndView({ view }, isForce, !isForce);
40
+ };
41
+ onTextChanged = async () => {
42
+ const { tokens = [], wall = [], uid = "", tokensVisible = 0 } = this.globalState.interface;
43
+
44
+ while (this.isStateChangeInProcess) await this.waitRerender();
45
+
46
+ return this.updateStateAndViewFast({ tokens, wall, uid, tokensVisible });
47
+ };
48
+ onSpeakerChanged = async () => {
49
+ const { speaker = "", meet = 0 } = this.globalState.interface;
50
+
51
+ while (this.isStateChangeInProcess) await this.waitRerender();
52
+
53
+ return this.updateStateAndViewFast({ speaker, meet });
54
+ };
55
+ onVisibleChanged = async ({ isForce = false } = {}) => {
56
+ const { isVisible = false } = this.globalState.interface;
57
+
58
+ if (this.state.isVisible === isVisible) return;
59
+
60
+ const args = { key: `${this.name}-unvisible`, controls: this.controlsUnvisible, index: this.controlsIndex + 500 };
61
+ await this.emit(isVisible ? this.EVENTS.CONTROLS.POP : this.EVENTS.CONTROLS.PUSH, args);
62
+ await this.updateStateAndView({ isVisible }, isForce, !isForce);
63
+ };
64
+
65
+ onStateChanged = async () => {
66
+ const { view = "", isShow = false, isVisible = false } = this.globalState.interface;
67
+
68
+ this.isStateChangeInProcess = true;
69
+ await this.updateStateAndViewForce({ view, isShow, isVisible });
70
+ this.isStateChangeInProcess = false;
71
+ };
72
+
73
+ getDefaultState = () => ({ tokens: [], wall: [], uid: "", tokensVisible: 0, speaker: "", meet: 0, isShow: false, isVisible: false });
74
+ }
@@ -0,0 +1,87 @@
1
+ import { Module } from "@vnejs/module";
2
+
3
+ import { tokenizeExecLine } from "@vnejs/helpers";
4
+
5
+ export class Interface extends Module {
6
+ name = "interface";
7
+
8
+ subscribe = () => {
9
+ this.on(this.EVENTS.INTERFACE.INTERACT, this.onInteract);
10
+ this.on(this.EVENTS.INTERFACE.SHOW, this.onShow);
11
+ this.on(this.EVENTS.INTERFACE.HIDE, this.onHide);
12
+ this.on(this.EVENTS.INTERFACE.VISIBLE, this.onVisible);
13
+ this.on(this.EVENTS.INTERFACE.TEXT_HIDE, this.onTextHide);
14
+ this.on(this.EVENTS.INTERFACE.VIEW, this.onView);
15
+
16
+ this.on(this.EVENTS.TEXT.CHANGED, this.onTextChanged);
17
+ this.on(this.EVENTS.TEXT.SPEAKER_CHANGED, this.onTextSpeakerChanged);
18
+ this.on(this.EVENTS.TEXT.EMIT_BEFORE, this.onTextEmitBefore);
19
+
20
+ this.on(this.EVENTS.STATE.SET, this.onStateSet);
21
+ this.on(this.EVENTS.STATE.CLEAR, this.onStateClear);
22
+ };
23
+ init = () => this.emit(this.EVENTS.SCENARIO.LINE_EXEC_REG, { module: this.name, handler: this.onLineExec });
24
+
25
+ onLineExec = async ({ line = "" } = {}) => {
26
+ const isForce = Boolean(this.shared.viewForceAnimationSources.length);
27
+
28
+ const [action = "", execArg = ""] = tokenizeExecLine(line);
29
+
30
+ if (action === "show") await this.emit(this.EVENTS.INTERFACE.SHOW, { isForce });
31
+ if (action === "hide") await this.emit(this.EVENTS.INTERFACE.HIDE, { isForce });
32
+ if (action === "view") await this.emit(this.EVENTS.INTERFACE.VIEW, { view: execArg, isForce });
33
+
34
+ this.emit(this.EVENTS.SCENARIO.NEXT, { module: this.name });
35
+ };
36
+
37
+ onInteract = () => this.emit(this.state.isVisible ? this.EVENTS.INTERACT.EMIT : this.EVENTS.INTERFACE.VISIBLE);
38
+ onShow = ({ isForce = false } = {}) => !this.state.isShow && this.setShow(true, isForce);
39
+ onHide = ({ isForce = false } = {}) => this.state.isShow && this.setShow(false, isForce);
40
+ onVisible = ({ isForce = false } = {}) => this.setVisible(!this.state.isVisible, isForce);
41
+ onTextChanged = ({ tokens, wall, uid, tokensVisible } = {}) => this.setText(tokens, wall, uid, tokensVisible);
42
+ onTextSpeakerChanged = ({ speaker, meet } = {}) => this.setSpeaker(speaker, meet);
43
+ onTextEmitBefore = () => this.emit(this.EVENTS.INTERFACE.SHOW);
44
+ onTextHide = () => this.setText([], [], "", 0);
45
+ onView = ({ view, isForce = false } = {}) => this.setView(view, isForce);
46
+ onStateSet = ({ [this.name]: state } = {}) => this.setNewState(state.view, state.isVisible, state.isShow);
47
+ onStateClear = () => this.setNewState(this.PARAMS.INTERFACE.DEFAULT_VIEW, false, false);
48
+
49
+ setView = (view, isForce = false) => {
50
+ this.state.view = view;
51
+
52
+ return this.emit(this.EVENTS.INTERFACE.VIEW_CHANGED, { isForce });
53
+ };
54
+ setShow = async (isShow, isForce = false) => {
55
+ this.state.isShow = isShow;
56
+
57
+ if (isShow && !this.state.isVisible) await this.setVisible(true, isForce);
58
+
59
+ return this.emit(this.EVENTS.INTERFACE.SHOW_CHANGED, { isForce });
60
+ };
61
+ setVisible = (isVisible, isForce = false) => {
62
+ this.state.isVisible = isVisible;
63
+
64
+ return this.emit(this.EVENTS.INTERFACE.VISIBLE_CHANGED, { isForce });
65
+ };
66
+ setText = (tokens = [], wall = [], uid = "", tokensVisible) => {
67
+ Object.assign(this.state, { tokens, wall, uid, tokensVisible });
68
+
69
+ return this.emit(this.EVENTS.INTERFACE.TEXT_CHANGED);
70
+ };
71
+ setSpeaker = (speaker = "", meet = 0) => {
72
+ Object.assign(this.state, { speaker, meet });
73
+
74
+ return this.emit(this.EVENTS.INTERFACE.SPEAKER_CHANGED);
75
+ };
76
+ setNewState = (view, isVisible, isShow) => {
77
+ Object.assign(this.state, { isShow, view, isVisible });
78
+
79
+ return this.emit(this.EVENTS.INTERFACE.STATE_CHANGED);
80
+ };
81
+
82
+ getDefaultState = () => {
83
+ const result = { tokens: [], view: this.PARAMS.INTERFACE.DEFAULT_VIEW, uid: "", speaker: "" };
84
+
85
+ return { ...result, meet: 0, tokensVisible: 0, isShow: false, isVisible: false };
86
+ };
87
+ }
@@ -0,0 +1,14 @@
1
+ import { ModuleView } from "@vnejs/module.components";
2
+
3
+ import { render } from "../view";
4
+
5
+ export class InterfaceView extends ModuleView {
6
+ name = "interface.view";
7
+
8
+ locLabel = this.PARAMS.INTERFACE.LOC_LABEL;
9
+ animationTime = this.PARAMS.INTERFACE.TRANSITION;
10
+ updateEvent = this.EVENTS.INTERFACE.VIEW_UPDATE;
11
+
12
+ renderFunc = render;
13
+ updateHandler = this.onUpdateStoreComponent;
14
+ }
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@vnejs/plugins.views.scenario.interface",
3
+ "version": "0.1.12",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1",
7
+ "publish:major:plugin": "npm run publish:major",
8
+ "publish:minor:plugin": "npm run publish:minor",
9
+ "publish:patch:plugin": "npm run publish:patch",
10
+ "publish:major": "npm version major && npm publish --access public",
11
+ "publish:minor": "npm version minor && npm publish --access public",
12
+ "publish:patch": "npm version patch && npm publish --access public"
13
+ },
14
+ "author": "",
15
+ "license": "ISC",
16
+ "description": ""
17
+ }
@@ -0,0 +1,22 @@
1
+ import { useMemo } from "react";
2
+
3
+ import { Backdrop, PositionBox } from "@vnejs/uis.react";
4
+
5
+ export const InterfaceBackdrop = ({ view = "", transition = 0, ...props }) => {
6
+ const propsByView = useMemo(() => props.PARAMS.INTERFACE.VIEW_PROPS[view]?.backdrop || {}, [view]);
7
+
8
+ const propsBackdrop = useMemo(() => ({ ...propsByView.props, transition }), [propsByView, transition]);
9
+ const propsPosition = useMemo(() => ({ ...propsByView.flatPosition, transition }), [propsByView, transition]);
10
+
11
+ const styleFlatBackdrop = useMemo(() => ({ ...propsByView.flatStyle, width: "100%", height: "100%", transition: `${transition}ms` }), [propsByView]);
12
+
13
+ return (
14
+ <>
15
+ <Backdrop {...propsBackdrop} />
16
+
17
+ <PositionBox {...propsPosition}>
18
+ <div style={styleFlatBackdrop} />
19
+ </PositionBox>
20
+ </>
21
+ );
22
+ };
@@ -0,0 +1,42 @@
1
+ import { useMemo } from "react";
2
+
3
+ import { Flex, PositionBox, SmoothText, Text } from "@vnejs/uis.react";
4
+ import { getVneLength } from "@vnejs/uis.utils";
5
+ import { useTokensHook } from "./tokens.hook";
6
+
7
+ export const InterfaceViewDialog = (props = {}) => {
8
+ const { isShow = false, transition, tokensVisible = 0, tokens, speakerText, speakerName, speakerColor, opacity, color, uid = "" } = props;
9
+
10
+ const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.adv;
11
+
12
+ const propsPosition = useMemo(() => ({ ...propsByView.position, opacity: isShow ? 1 : 0, transition }), [isShow, transition, propsByView.position]);
13
+
14
+ const speakerOpacity = speakerName ? 1 : 0;
15
+ const speakerTransition = speakerName ? transition : 0;
16
+
17
+ const backgroundSep = useMemo(() => `linear-gradient(to right, ${speakerColor} 0%, transparent 100%)`, [speakerColor]);
18
+ const styleSepBg = useMemo(() => ({ height: getVneLength(6), width: "calc(200% / 3)", background: backgroundSep }), [backgroundSep]);
19
+ const styleSepColor = useMemo(() => ({ opacity: speakerOpacity, transition: `${speakerTransition}ms` || "none" }), [speakerOpacity, speakerTransition]);
20
+ const styleSep = useMemo(() => ({ ...styleSepBg, ...styleSepColor }), [styleSepBg, styleSepColor]);
21
+
22
+ const { realTokens, realTokensVisible } = useTokensHook(isShow, tokens, tokensVisible, uid);
23
+
24
+ const propsSpeaker = useMemo(
25
+ () => ({ ...propsByView.speaker, text: speakerText, color: speakerColor, opacity: speakerOpacity, transition: speakerTransition }),
26
+ [speakerText, speakerColor, speakerOpacity, speakerTransition],
27
+ );
28
+ const propsText = useMemo(
29
+ () => ({ ...propsByView.text, opacity, color, tokens: realTokens, tokensVisibleForce: realTokensVisible, transition }),
30
+ [opacity, color, realTokens, realTokensVisible, transition],
31
+ );
32
+
33
+ return (
34
+ <PositionBox {...propsPosition}>
35
+ <Flex {...propsByView.flex}>
36
+ <Text {...propsSpeaker} />
37
+ <div style={styleSep} />
38
+ <SmoothText {...propsText} />
39
+ </Flex>
40
+ </PositionBox>
41
+ );
42
+ };
@@ -0,0 +1,25 @@
1
+ import { useMemo } from "react";
2
+
3
+ import { Flex, PositionBox, SmoothText } from "@vnejs/uis.react";
4
+
5
+ import { useTokensHook } from "./tokens.hook";
6
+
7
+ export const InterfaceViewLine = (props = {}) => {
8
+ const { isShow = false, transition = 0, tokensVisible = 0, tokens = [], opacity, color, uid = "" } = props;
9
+
10
+ const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.line;
11
+
12
+ const { realTokens, realTokensVisible } = useTokensHook(isShow, tokens, tokensVisible, uid);
13
+
14
+ const propsTextTokens = useMemo(() => ({ tokens: realTokens, tokensVisibleForce: realTokensVisible }), [realTokens, realTokensVisible]);
15
+ const propsText = useMemo(() => ({ ...propsByView.text, opacity, color, transition, ...propsTextTokens }), [opacity, color, transition, propsTextTokens]);
16
+ const propsPosition = useMemo(() => ({ ...propsByView.position, opacity: isShow ? 1 : 0, transition }), [isShow, transition]);
17
+
18
+ return (
19
+ <PositionBox {...propsPosition}>
20
+ <Flex {...propsByView.flex}>
21
+ <SmoothText {...propsText} />
22
+ </Flex>
23
+ </PositionBox>
24
+ );
25
+ };
@@ -0,0 +1,65 @@
1
+ import { useMemo } from "react";
2
+
3
+ import { Flex, PositionBox, Scrollbar, ScrollContent, SmoothText, Text } from "@vnejs/uis.react";
4
+
5
+ import { useTokensHook } from "./tokens.hook";
6
+
7
+ const renderOneText = (wallElement) => (
8
+ <div key={wallElement.uid}>
9
+ {wallElement.speakerName && (
10
+ <Text
11
+ {...wallElement.propsByView.text}
12
+ isInlineBlock={true}
13
+ color={wallElement.speakerColor}
14
+ text={`${wallElement.speakerName}:`}
15
+ marginRight={12}
16
+ />
17
+ )}
18
+ <SmoothText
19
+ {...wallElement.propsByView.text}
20
+ isNoScrollBar={true}
21
+ color={wallElement.color}
22
+ opacity={wallElement.opacity}
23
+ transition={wallElement.transition}
24
+ tokens={wallElement.tokens}
25
+ tokensVisibleForce={wallElement.tokensVisible}
26
+ />
27
+ </div>
28
+ );
29
+
30
+ const mapWall = ({ uid = "", tokens = [], speaker = "", meet = 0, transition = 0, props } = {}) => {
31
+ const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.wall;
32
+ const speakerInfo = props.PARAMS.TEXT.SPEAKERS_INFO[speaker] || props.PARAMS.TEXT.SPEAKERS_INFO.default || {};
33
+ const { color, opacity = 1, names = {}, speakerColor = "white" } = speakerInfo;
34
+
35
+ const langNames = names[props.shared.lang] || names.default || [];
36
+ const speakerName = langNames[meet] || langNames[langNames.length - 1];
37
+
38
+ return { speakerName, speakerColor, uid, color, opacity, tokens, tokensVisible: tokens.length, transition, propsByView };
39
+ };
40
+
41
+ export const InterfaceViewWall = (props = {}) => {
42
+ const { isShow = false, transition = 0, tokensVisible = 0, tokens = [], wall = [], uid = "", speakerName, speakerColor, color, opacity } = props;
43
+
44
+ const propsByView = props.PARAMS.INTERFACE.VIEW_PROPS.wall;
45
+
46
+ const propsPosition = useMemo(() => ({ ...propsByView.position, opacity: isShow ? 1 : 0, transition }), [isShow, transition]);
47
+ const wallWithProps = useMemo(() => wall.map((item) => ({ ...item, transition, props })), [wall, transition]);
48
+
49
+ const { realTokens, realTokensVisible } = useTokensHook(isShow, tokens, tokensVisible, uid);
50
+
51
+ const wallTexts = useMemo(() => wallWithProps.map(mapWall), [wallWithProps]);
52
+ const curText = useMemo(
53
+ () => ({ speakerName, speakerColor, uid, color, opacity, tokens: realTokens, tokensVisible: realTokensVisible, transition, propsByView }),
54
+ [uid, realTokens, realTokensVisible, transition],
55
+ );
56
+ const texts = useMemo(() => [...wallTexts, curText], [wallTexts, curText]);
57
+
58
+ return (
59
+ <PositionBox {...propsPosition}>
60
+ <Scrollbar {...propsByView.scrollBar}>
61
+ <Flex {...propsByView.flex}>{texts.map(renderOneText)}</Flex>
62
+ </Scrollbar>
63
+ </PositionBox>
64
+ );
65
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./InterfaceBackdrop";
2
+ export * from "./InterfaceViewDialog";
3
+ export * from "./InterfaceViewLine";
4
+ export * from "./InterfaceViewWall";
@@ -0,0 +1,34 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import { flushSync } from "react-dom";
3
+
4
+ const EMPTY_ARR = [];
5
+
6
+ export const useTokensHook = (isShow, tokens, tokensVisible, uid) => {
7
+ const [savedUid, setSavedUid] = useState("");
8
+ const [tokensSync, setTokensSync] = useState(EMPTY_ARR);
9
+ const [tokensWrong, setTokensWrong] = useState(EMPTY_ARR);
10
+
11
+ useEffect(() => {
12
+ if (isShow) setTokensWrong(tokens);
13
+ }, [isShow]);
14
+ useEffect(() => {
15
+ setImmediate(() => {
16
+ if (savedUid === uid) {
17
+ if (!isShow) setTokensWrong(tokens);
18
+ setTokensSync(tokens);
19
+ return;
20
+ }
21
+ flushSync(() => {
22
+ setSavedUid(uid);
23
+ if (!isShow) setTokensWrong(tokens);
24
+ setTokensSync(tokens);
25
+ });
26
+ });
27
+ }, [tokens]);
28
+
29
+ const isShowTokens = useMemo(() => tokensSync !== tokensWrong, [tokensSync, tokensWrong]);
30
+ const realTokensVisible = useMemo(() => (isShowTokens ? tokensVisible : 0), [isShowTokens, tokensVisible]);
31
+ const realTokens = useMemo(() => (isShowTokens ? tokensSync : EMPTY_ARR), [isShowTokens, tokensSync]);
32
+
33
+ return { isShowTokens, realTokensVisible, realTokens };
34
+ };
package/view/index.jsx ADDED
@@ -0,0 +1,62 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+
4
+ import { Screen, useIsForceHook } from "@vnejs/uis.react";
5
+
6
+ import { InterfaceBackdrop, InterfaceViewDialog, InterfaceViewLine, InterfaceViewWall } from "./components";
7
+
8
+ const EMPTY_ARRAY = [];
9
+
10
+ const Interface = ({ store, onMount, ...props } = {}) => {
11
+ const [state, setState] = useState({});
12
+
13
+ const { isShow = false, isVisible = false, isForce = false, tokensVisible = 0, view = "", locs = {} } = state;
14
+ const { speaker, meet = 0, tokens = EMPTY_ARRAY, wall = [], postfix = "", prefix = "", uid = "" } = state;
15
+
16
+ const onClick = useCallback(() => props.emit(props.EVENTS.INTERFACE.INTERACT), []);
17
+
18
+ useEffect(() => store.subscribe(setState), []);
19
+ useEffect(() => void onMount(), []);
20
+
21
+ const isRealForce = useIsForceHook(isForce);
22
+
23
+ const speakerName = locs[`speaker.${speaker}.${meet ?? 0}`];
24
+ const speakerText = speakerName ? `${`${locs[`prefix.${prefix}`] ?? ""} `}${speakerName}${` ${locs[`postfix.${postfix}`] ?? ""}`}` : ".";
25
+ const transition = isRealForce ? 0 : props.PARAMS.INTERFACE.TRANSITION;
26
+ const speakerInfo = props.PARAMS.TEXT.SPEAKERS_INFO[speaker] || props.PARAMS.TEXT.SPEAKERS_INFO[props.PARAMS.TEXT.DEFAULT_SPEAKER_NAME];
27
+ const { color, opacity = 1, speakerColor = "white" } = speakerInfo;
28
+
29
+ const propsCommon = useMemo(
30
+ () => ({ isForce: isRealForce, transition, tokensVisible, tokens, wall, uid }),
31
+ [isRealForce, transition, tokensVisible, tokens, wall, uid],
32
+ );
33
+ const propsSpeaker = useMemo(() => ({ speakerText, speakerName, speakerColor }), [speakerText, speakerName, speakerColor]);
34
+ const propsText = useMemo(() => ({ opacity, color }), [opacity, color]);
35
+ const propsView = useMemo(() => ({ ...propsCommon, ...propsSpeaker, ...propsText }), [propsCommon, propsSpeaker, propsText]);
36
+
37
+ const propsBackdrop = useMemo(() => ({ view, transition, ...props }), [view, transition]);
38
+ const propsDialog = useMemo(() => ({ isShow: view === "adv", ...propsView, ...props }), [view, propsView]);
39
+ const propsLine = useMemo(() => ({ isShow: view === "line", ...propsView, ...props }), [view, propsView]);
40
+ const propsWall = useMemo(() => ({ isShow: view === "wall", ...propsView, ...props }), [view, propsView]);
41
+
42
+ return (
43
+ <Screen
44
+ isShow={isShow}
45
+ isForce={isRealForce}
46
+ isHidden={!isVisible}
47
+ isDisableAutoread={false}
48
+ isAllowAutoread={Boolean(isShow && isVisible)}
49
+ isIgnoreOnScreenshot={false}
50
+ transition={transition}
51
+ zIndex={props.PARAMS.INTERFACE.ZINDEX}
52
+ onClick={onClick}
53
+ >
54
+ <InterfaceBackdrop {...propsBackdrop} />
55
+ <InterfaceViewDialog {...propsDialog} />
56
+ <InterfaceViewLine {...propsLine} />
57
+ <InterfaceViewWall {...propsWall} />
58
+ </Screen>
59
+ );
60
+ };
61
+
62
+ export const render = (props, root) => createRoot(root).render(<Interface {...props} />);