Teact: Implement createContext and useContextSignal (#4619)
This commit is contained in:
parent
9d2a928968
commit
a4bfdad768
@ -4,9 +4,9 @@ import React, { memo, useMemo } from '../../lib/teact/teact';
|
|||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import renderText from './helpers/renderText';
|
import renderText from './helpers/renderText';
|
||||||
|
|
||||||
|
import useSelectorSignal from '../../hooks/data/useSelectorSignal';
|
||||||
import useDerivedState from '../../hooks/useDerivedState';
|
import useDerivedState from '../../hooks/useDerivedState';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
import useSelectorSignal from '../../hooks/useSelectorSignal';
|
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import ListItem from '../ui/ListItem';
|
import ListItem from '../ui/ListItem';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import React, {
|
import React, {
|
||||||
memo, useEffect, useMemo, useRef, useState,
|
memo, useEffect, useMemo, useRef, useSignal, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
@ -118,7 +118,6 @@ import usePrevious from '../../hooks/usePrevious';
|
|||||||
import useSchedule from '../../hooks/useSchedule';
|
import useSchedule from '../../hooks/useSchedule';
|
||||||
import useSendMessageAction from '../../hooks/useSendMessageAction';
|
import useSendMessageAction from '../../hooks/useSendMessageAction';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
import useSignal from '../../hooks/useSignal';
|
|
||||||
import { useStateRef } from '../../hooks/useStateRef';
|
import { useStateRef } from '../../hooks/useStateRef';
|
||||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useAttachmentModal from '../middle/composer/hooks/useAttachmentModal';
|
import useAttachmentModal from '../middle/composer/hooks/useAttachmentModal';
|
||||||
|
|||||||
@ -12,13 +12,13 @@ import {
|
|||||||
} from '../../../util/dates/workHours';
|
} from '../../../util/dates/workHours';
|
||||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||||
|
|
||||||
|
import useSelectorSignal from '../../../hooks/data/useSelectorSignal';
|
||||||
import useInterval from '../../../hooks/schedulers/useInterval';
|
import useInterval from '../../../hooks/schedulers/useInterval';
|
||||||
import useDerivedState from '../../../hooks/useDerivedState';
|
import useDerivedState from '../../../hooks/useDerivedState';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
import useSelectorSignal from '../../../hooks/useSelectorSignal';
|
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Transition, { ACTIVE_SLIDE_CLASS_NAME, TO_SLIDE_CLASS_NAME } from '../../ui/Transition';
|
import Transition, { ACTIVE_SLIDE_CLASS_NAME, TO_SLIDE_CLASS_NAME } from '../../ui/Transition';
|
||||||
|
|||||||
@ -50,13 +50,13 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import { createLocationHash } from '../../../util/routing';
|
import { createLocationHash } from '../../../util/routing';
|
||||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/windowEnvironment';
|
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/windowEnvironment';
|
||||||
|
|
||||||
|
import useSelectorSignal from '../../../hooks/data/useSelectorSignal';
|
||||||
import useAppLayout from '../../../hooks/useAppLayout';
|
import useAppLayout from '../../../hooks/useAppLayout';
|
||||||
import useChatContextActions from '../../../hooks/useChatContextActions';
|
import useChatContextActions from '../../../hooks/useChatContextActions';
|
||||||
import useEnsureMessage from '../../../hooks/useEnsureMessage';
|
import useEnsureMessage from '../../../hooks/useEnsureMessage';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useSelectorSignal from '../../../hooks/useSelectorSignal';
|
|
||||||
import useShowTransition from '../../../hooks/useShowTransition';
|
import useShowTransition from '../../../hooks/useShowTransition';
|
||||||
import useChatListEntry from './hooks/useChatListEntry';
|
import useChatListEntry from './hooks/useChatListEntry';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import React, {
|
import React, {
|
||||||
memo, useEffect, useLayoutEffect, useRef, useState,
|
memo, useEffect, useLayoutEffect, useRef, useSignal, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { MediaViewerOrigin, ThreadId } from '../../types';
|
import type { MediaViewerOrigin, ThreadId } from '../../types';
|
||||||
@ -24,7 +24,6 @@ import useDerivedState from '../../hooks/useDerivedState';
|
|||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
import useSignal from '../../hooks/useSignal';
|
|
||||||
import { useSignalRef } from '../../hooks/useSignalRef';
|
import { useSignalRef } from '../../hooks/useSignalRef';
|
||||||
import { useFullscreenStatus } from '../../hooks/window/useFullscreen';
|
import { useFullscreenStatus } from '../../hooks/window/useFullscreen';
|
||||||
import useWindowSize from '../../hooks/window/useWindowSize';
|
import useWindowSize from '../../hooks/window/useWindowSize';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import React, {
|
import React, {
|
||||||
memo, useEffect, useLayoutEffect,
|
memo, useEffect, useLayoutEffect,
|
||||||
useMemo, useRef, useState,
|
useMemo, useRef, useSignal, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiDimensions } from '../../api/types';
|
import type { ApiDimensions } from '../../api/types';
|
||||||
@ -18,7 +18,6 @@ import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
|||||||
import { useThrottledSignal } from '../../hooks/useAsyncResolvers';
|
import { useThrottledSignal } from '../../hooks/useAsyncResolvers';
|
||||||
import useCurrentTimeSignal from '../../hooks/useCurrentTimeSignal';
|
import useCurrentTimeSignal from '../../hooks/useCurrentTimeSignal';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useSignal from '../../hooks/useSignal';
|
|
||||||
import useVideoWaitingSignal from './hooks/useVideoWaitingSignal';
|
import useVideoWaitingSignal from './hooks/useVideoWaitingSignal';
|
||||||
|
|
||||||
import ShowTransition from '../ui/ShowTransition';
|
import ShowTransition from '../ui/ShowTransition';
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { FC } from '../../lib/teact/teact';
|
|||||||
import React, {
|
import React, {
|
||||||
memo, useEffect, useLayoutEffect,
|
memo, useEffect, useLayoutEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useSignal,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiDimensions } from '../../api/types';
|
import type { ApiDimensions } from '../../api/types';
|
||||||
@ -18,7 +19,6 @@ import useDerivedState from '../../hooks/useDerivedState';
|
|||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
import useSignal from '../../hooks/useSignal';
|
|
||||||
import useControlsSignal from './hooks/useControlsSignal';
|
import useControlsSignal from './hooks/useControlsSignal';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import type { RefObject } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import { useEffect, useRef } from '../../../lib/teact/teact';
|
import { useEffect, useRef, useSignal } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useResizeObserver from '../../../hooks/useResizeObserver';
|
import useResizeObserver from '../../../hooks/useResizeObserver';
|
||||||
import useSignal from '../../../hooks/useSignal';
|
|
||||||
|
|
||||||
export default function useContainerHeight(containerRef: RefObject<HTMLDivElement>, isComposerVisible: boolean) {
|
export default function useContainerHeight(containerRef: RefObject<HTMLDivElement>, isComposerVisible: boolean) {
|
||||||
const [getContainerHeight, setContainerHeight] = useSignal<number | undefined>();
|
const [getContainerHeight, setContainerHeight] = useSignal<number | undefined>();
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef } from '../../../lib/teact/teact';
|
import { useEffect, useRef, useSignal } from '../../../lib/teact/teact';
|
||||||
import { getGlobal } from '../../../global';
|
import { getGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ThreadId } from '../../../types';
|
import type { ThreadId } from '../../../types';
|
||||||
@ -13,7 +13,6 @@ import { unique } from '../../../util/iteratees';
|
|||||||
import { clamp } from '../../../util/math';
|
import { clamp } from '../../../util/math';
|
||||||
|
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useSignal from '../../../hooks/useSignal';
|
|
||||||
|
|
||||||
type PinnedIntersectionChangedParams = {
|
type PinnedIntersectionChangedParams = {
|
||||||
viewportPinnedIdsToAdd?: number[];
|
viewportPinnedIdsToAdd?: number[];
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
useRef,
|
useRef,
|
||||||
|
useSignal,
|
||||||
useState,
|
useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions } from '../../../global';
|
import { getActions } from '../../../global';
|
||||||
@ -28,7 +29,6 @@ import useMediaTransition from '../../../hooks/useMediaTransition';
|
|||||||
import useMediaWithLoadProgress from '../../../hooks/useMediaWithLoadProgress';
|
import useMediaWithLoadProgress from '../../../hooks/useMediaWithLoadProgress';
|
||||||
import usePrevious from '../../../hooks/usePrevious';
|
import usePrevious from '../../../hooks/usePrevious';
|
||||||
import useShowTransition from '../../../hooks/useShowTransition';
|
import useShowTransition from '../../../hooks/useShowTransition';
|
||||||
import useSignal from '../../../hooks/useSignal';
|
|
||||||
import useBlurredMediaThumbRef from './hooks/useBlurredMediaThumbRef';
|
import useBlurredMediaThumbRef from './hooks/useBlurredMediaThumbRef';
|
||||||
|
|
||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
|
|||||||
@ -15,9 +15,9 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import { formatDateTimeToString } from '../../../util/dates/dateFormat';
|
import { formatDateTimeToString } from '../../../util/dates/dateFormat';
|
||||||
import { CUSTOM_PEER_PREMIUM } from '../../../util/objects/customPeer';
|
import { CUSTOM_PEER_PREMIUM } from '../../../util/objects/customPeer';
|
||||||
|
|
||||||
|
import useSelector from '../../../hooks/data/useSelector';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
import useSelector from '../../../hooks/useSelector';
|
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React, {
|
import React, {
|
||||||
memo, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
memo, useEffect, useLayoutEffect, useMemo, useRef, useSignal, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
@ -33,7 +33,6 @@ import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
|||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import useSignal from '../../hooks/useSignal';
|
|
||||||
import useWindowSize from '../../hooks/window/useWindowSize';
|
import useWindowSize from '../../hooks/window/useWindowSize';
|
||||||
import useSlideSizes from './hooks/useSlideSizes';
|
import useSlideSizes from './hooks/useSlideSizes';
|
||||||
|
|
||||||
|
|||||||
79
src/components/test/TestContext.tsx
Normal file
79
src/components/test/TestContext.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import React, {
|
||||||
|
createContext, memo, useState,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import useContext from '../../hooks/data/useContext';
|
||||||
|
|
||||||
|
const TestingContext = createContext('default value');
|
||||||
|
|
||||||
|
const ContextConsumer = ({ children, debugKey }: { children?: any; debugKey?: string }) => {
|
||||||
|
const value = useContext(TestingContext);
|
||||||
|
if (debugKey) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`ContextConsumer ${debugKey}`, value);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div style="color: red">
|
||||||
|
{`Current context value: ${value}`}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MemoizedWrapper = memo(({ children } : { children: any }) => {
|
||||||
|
return <div style="background-color: aquamarine">{children}</div>;
|
||||||
|
});
|
||||||
|
|
||||||
|
const ContextSwapper = ({ value, children } : { value: string; children: any }) => {
|
||||||
|
return (
|
||||||
|
<div style="border: 1px solid red">
|
||||||
|
Swapped {value}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TestContext = () => {
|
||||||
|
const [value, setValue] = useState(Math.random().toString());
|
||||||
|
const [isSwapping, setIsSwapping] = useState(false);
|
||||||
|
|
||||||
|
const Wrapper = isSwapping ? ContextSwapper : TestingContext.Provider;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={() => setValue(Math.random().toString())}>Change context value</button>
|
||||||
|
<button onClick={() => setIsSwapping((prev) => !prev)}>Swap context wrapper</button>
|
||||||
|
<ContextConsumer />
|
||||||
|
<Wrapper value={value}>
|
||||||
|
<div>
|
||||||
|
<ContextConsumer debugKey="A">
|
||||||
|
<ContextConsumer debugKey="B">
|
||||||
|
<ContextConsumer debugKey="C" />
|
||||||
|
</ContextConsumer>
|
||||||
|
</ContextConsumer>
|
||||||
|
</div>
|
||||||
|
<TestingContext.Provider value={`sibling ${value}`}>
|
||||||
|
<ContextConsumer />
|
||||||
|
</TestingContext.Provider>
|
||||||
|
<MemoizedWrapper>
|
||||||
|
<ContextConsumer />
|
||||||
|
</MemoizedWrapper>
|
||||||
|
<>
|
||||||
|
<ContextConsumer />
|
||||||
|
<ContextConsumer />
|
||||||
|
</>
|
||||||
|
<TestingContext.Provider value={`fastlist ${value}`}>
|
||||||
|
<div style="background-color: antiquewhite" teactFastList>
|
||||||
|
<ContextConsumer key="first" />
|
||||||
|
{!isSwapping && <div key="second">Fast list item</div>}
|
||||||
|
<ContextConsumer key="third" />
|
||||||
|
</div>
|
||||||
|
</TestingContext.Provider>
|
||||||
|
<TestingContext.Provider value="overriden value">
|
||||||
|
<ContextConsumer />
|
||||||
|
</TestingContext.Provider>
|
||||||
|
</Wrapper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TestContext;
|
||||||
8
src/hooks/data/useContext.ts
Normal file
8
src/hooks/data/useContext.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { type Context, useContextSignal } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import useDerivedState from '../useDerivedState';
|
||||||
|
|
||||||
|
export default function useContext<T>(context: Context<T>) {
|
||||||
|
const signal = useContextSignal(context);
|
||||||
|
return useDerivedState(signal);
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { GlobalState } from '../global/types';
|
import type { GlobalState } from '../../global/types';
|
||||||
|
|
||||||
import useDerivedState from './useDerivedState';
|
import useDerivedState from '../useDerivedState';
|
||||||
import useSelectorSignal from './useSelectorSignal';
|
import useSelectorSignal from './useSelectorSignal';
|
||||||
|
|
||||||
type Selector<T extends unknown> = (global: GlobalState) => T;
|
type Selector<T extends unknown> = (global: GlobalState) => T;
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import { addCallback } from '../lib/teact/teactn';
|
import { addCallback } from '../../lib/teact/teactn';
|
||||||
import { getGlobal } from '../global';
|
import { getGlobal } from '../../global';
|
||||||
|
|
||||||
import type { GlobalState } from '../global/types';
|
import type { GlobalState } from '../../global/types';
|
||||||
import type { Signal, SignalSetter } from '../util/signals';
|
import type { Signal, SignalSetter } from '../../util/signals';
|
||||||
|
|
||||||
import { createSignal } from '../util/signals';
|
import { createSignal } from '../../util/signals';
|
||||||
import useEffectOnce from './useEffectOnce';
|
import useEffectOnce from '../useEffectOnce';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This hook is a more performant variation of the standard React `useSelector` hook. It allows to:
|
This hook is a more performant variation of the standard React `useSelector` hook. It allows to:
|
||||||
@ -1,6 +1,7 @@
|
|||||||
|
import { useSignal } from '../lib/teact/teact';
|
||||||
|
|
||||||
import type { Signal } from '../util/signals';
|
import type { Signal } from '../util/signals';
|
||||||
|
|
||||||
import useSignal from './useSignal';
|
|
||||||
import { useSignalEffect } from './useSignalEffect';
|
import { useSignalEffect } from './useSignalEffect';
|
||||||
import { useStateRef } from './useStateRef';
|
import { useStateRef } from './useStateRef';
|
||||||
import useSyncEffect from './useSyncEffect';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import { useEffect } from '../lib/teact/teact';
|
import { useEffect, useSignal } from '../lib/teact/teact';
|
||||||
|
|
||||||
import useSignal from './useSignal';
|
|
||||||
|
|
||||||
export default function useGetSelectionRange(inputSelector: string) {
|
export default function useGetSelectionRange(inputSelector: string) {
|
||||||
const [getRange, setRange] = useSignal<Range | undefined>();
|
const [getRange, setRange] = useSignal<Range | undefined>();
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { ApiMediaFormat } from '../api/types';
|
|||||||
|
|
||||||
import { selectIsSynced } from '../global/selectors';
|
import { selectIsSynced } from '../global/selectors';
|
||||||
import * as mediaLoader from '../util/mediaLoader';
|
import * as mediaLoader from '../util/mediaLoader';
|
||||||
|
import useSelector from './data/useSelector';
|
||||||
import useForceUpdate from './useForceUpdate';
|
import useForceUpdate from './useForceUpdate';
|
||||||
import useSelector from './useSelector';
|
|
||||||
|
|
||||||
const useMedia = (
|
const useMedia = (
|
||||||
mediaHash: string | false | undefined,
|
mediaHash: string | false | undefined,
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import { selectIsSynced } from '../global/selectors';
|
|||||||
import * as mediaLoader from '../util/mediaLoader';
|
import * as mediaLoader from '../util/mediaLoader';
|
||||||
import { throttle } from '../util/schedulers';
|
import { throttle } from '../util/schedulers';
|
||||||
import { IS_PROGRESSIVE_SUPPORTED } from '../util/windowEnvironment';
|
import { IS_PROGRESSIVE_SUPPORTED } from '../util/windowEnvironment';
|
||||||
|
import useSelector from './data/useSelector';
|
||||||
import useForceUpdate from './useForceUpdate';
|
import useForceUpdate from './useForceUpdate';
|
||||||
import useSelector from './useSelector';
|
|
||||||
import useUniqueId from './useUniqueId';
|
import useUniqueId from './useUniqueId';
|
||||||
|
|
||||||
const STREAMING_PROGRESS = 0.75;
|
const STREAMING_PROGRESS = 0.75;
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { useRef } from '../lib/teact/teact';
|
|
||||||
|
|
||||||
import { createSignal } from '../util/signals';
|
|
||||||
|
|
||||||
export default function useSignal<T>(initial?: T) {
|
|
||||||
const signalRef = useRef<ReturnType<typeof createSignal<T>>>();
|
|
||||||
signalRef.current ??= createSignal<T>(initial);
|
|
||||||
return signalRef.current;
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
|
|
||||||
|
import type { Signal } from '../../util/signals';
|
||||||
import type {
|
import type {
|
||||||
VirtualElement,
|
VirtualElement,
|
||||||
VirtualElementChildren,
|
VirtualElementChildren,
|
||||||
@ -34,6 +35,8 @@ interface SelectionState {
|
|||||||
isCaretAtEnd: boolean;
|
isCaretAtEnd: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CurrentContext = Record<string, Signal<unknown>>;
|
||||||
|
|
||||||
type DOMElement = HTMLElement | SVGElement;
|
type DOMElement = HTMLElement | SVGElement;
|
||||||
|
|
||||||
const FILTERED_ATTRIBUTES = new Set(['key', 'ref', 'teactFastList', 'teactOrderKey']);
|
const FILTERED_ATTRIBUTES = new Set(['key', 'ref', 'teactFastList', 'teactOrderKey']);
|
||||||
@ -59,7 +62,7 @@ function render($element: VirtualElement | undefined, parentEl: HTMLElement) {
|
|||||||
|
|
||||||
const runImmediateEffects = captureImmediateEffects();
|
const runImmediateEffects = captureImmediateEffects();
|
||||||
const $head = headsByElement.get(parentEl)!;
|
const $head = headsByElement.get(parentEl)!;
|
||||||
const $renderedChild = renderWithVirtual(parentEl, $head.children[0], $element, $head, 0);
|
const $renderedChild = renderWithVirtual(parentEl, $head.children[0], $element, $head, {}, 0);
|
||||||
runImmediateEffects?.();
|
runImmediateEffects?.();
|
||||||
|
|
||||||
$head.children = $renderedChild ? [$renderedChild] : [];
|
$head.children = $renderedChild ? [$renderedChild] : [];
|
||||||
@ -79,6 +82,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
$current: VirtualElement | undefined,
|
$current: VirtualElement | undefined,
|
||||||
$new: T,
|
$new: T,
|
||||||
$parent: VirtualElementParent | VirtualDomHead,
|
$parent: VirtualElementParent | VirtualDomHead,
|
||||||
|
currentContext: CurrentContext,
|
||||||
index: number,
|
index: number,
|
||||||
options: {
|
options: {
|
||||||
skipComponentUpdate?: boolean;
|
skipComponentUpdate?: boolean;
|
||||||
@ -116,7 +120,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
&& isNewComponent
|
&& isNewComponent
|
||||||
&& ($new as VirtualElementComponent).componentInstance.mountState === MountState.Mounted
|
&& ($new as VirtualElementComponent).componentInstance.mountState === MountState.Mounted
|
||||||
) {
|
) {
|
||||||
setupComponentUpdateListener(parentEl, $new as VirtualElementComponent, $parent, index);
|
setupComponentUpdateListener(parentEl, $new as VirtualElementComponent, $parent, currentContext, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($current === $new) {
|
if ($current === $new) {
|
||||||
@ -133,10 +137,13 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
if (!$current && $new) {
|
if (!$current && $new) {
|
||||||
if (isNewComponent || isNewFragment) {
|
if (isNewComponent || isNewFragment) {
|
||||||
if (isNewComponent) {
|
if (isNewComponent) {
|
||||||
$new = initComponent(parentEl, $new as VirtualElementComponent, $parent, index) as unknown as typeof $new;
|
$new = initComponent(
|
||||||
|
parentEl, $new as VirtualElementComponent, $parent, currentContext, index,
|
||||||
|
) as unknown as typeof $new;
|
||||||
|
currentContext = ($new as VirtualElementComponent).componentInstance.context ?? currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, {
|
mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, currentContext, {
|
||||||
nextSibling, fragment, isSvg,
|
nextSibling, fragment, isSvg,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -150,7 +157,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
parentEl.textContent = $newAsReal.value;
|
parentEl.textContent = $newAsReal.value;
|
||||||
$newAsReal.target = parentEl.firstChild!;
|
$newAsReal.target = parentEl.firstChild!;
|
||||||
} else {
|
} else {
|
||||||
const node = createNode($newAsReal, isSvg);
|
const node = createNode($newAsReal, currentContext, isSvg);
|
||||||
$newAsReal.target = node;
|
$newAsReal.target = node;
|
||||||
insertBefore(fragment || parentEl, node, nextSibling);
|
insertBefore(fragment || parentEl, node, nextSibling);
|
||||||
|
|
||||||
@ -160,7 +167,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ($current && !$new) {
|
} else if ($current && !$new) {
|
||||||
remount(parentEl, $current, undefined);
|
remount(parentEl, $current, currentContext, undefined);
|
||||||
} else if ($current && $new) {
|
} else if ($current && $new) {
|
||||||
if (hasElementChanged($current, $new)) {
|
if (hasElementChanged($current, $new)) {
|
||||||
if (!nextSibling) {
|
if (!nextSibling) {
|
||||||
@ -169,17 +176,20 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
|
|
||||||
if (isNewComponent || isNewFragment) {
|
if (isNewComponent || isNewFragment) {
|
||||||
if (isNewComponent) {
|
if (isNewComponent) {
|
||||||
$new = initComponent(parentEl, $new as VirtualElementComponent, $parent, index) as unknown as typeof $new;
|
$new = initComponent(
|
||||||
|
parentEl, $new as VirtualElementComponent, $parent, currentContext, index,
|
||||||
|
) as unknown as typeof $new;
|
||||||
|
currentContext = ($new as VirtualElementComponent).componentInstance.context ?? currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
remount(parentEl, $current, undefined);
|
remount(parentEl, $current, currentContext, undefined);
|
||||||
mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, {
|
mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, currentContext, {
|
||||||
nextSibling, fragment, isSvg,
|
nextSibling, fragment, isSvg,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const node = createNode($newAsReal, isSvg);
|
const node = createNode($newAsReal, currentContext, isSvg);
|
||||||
$newAsReal.target = node;
|
$newAsReal.target = node;
|
||||||
remount(parentEl, $current, node, nextSibling);
|
remount(parentEl, $current, currentContext, node, nextSibling);
|
||||||
|
|
||||||
if ($newAsReal.type === VirtualType.Tag) {
|
if ($newAsReal.type === VirtualType.Tag) {
|
||||||
setElementRef($newAsReal, node as DOMElement);
|
setElementRef($newAsReal, node as DOMElement);
|
||||||
@ -193,6 +203,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
renderChildren(
|
renderChildren(
|
||||||
$current,
|
$current,
|
||||||
$new as VirtualElementComponent | VirtualElementFragment,
|
$new as VirtualElementComponent | VirtualElementFragment,
|
||||||
|
currentContext,
|
||||||
parentEl,
|
parentEl,
|
||||||
nextSibling,
|
nextSibling,
|
||||||
options.forceMoveToEnd,
|
options.forceMoveToEnd,
|
||||||
@ -216,7 +227,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateAttributes($current, $newAsTag, currentTarget as DOMElement, isSvg);
|
updateAttributes($current, $newAsTag, currentTarget as DOMElement, isSvg);
|
||||||
renderChildren($current, $newAsTag, currentTarget as DOMElement, undefined, undefined, isSvg);
|
renderChildren($current, $newAsTag, currentContext, currentTarget as DOMElement, undefined, undefined, isSvg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,13 +240,16 @@ function initComponent(
|
|||||||
parentEl: DOMElement,
|
parentEl: DOMElement,
|
||||||
$element: VirtualElementComponent,
|
$element: VirtualElementComponent,
|
||||||
$parent: VirtualElementParent | VirtualDomHead,
|
$parent: VirtualElementParent | VirtualDomHead,
|
||||||
|
currentContext: CurrentContext,
|
||||||
index: number,
|
index: number,
|
||||||
) {
|
) {
|
||||||
const { componentInstance } = $element;
|
const { componentInstance } = $element;
|
||||||
|
|
||||||
|
$element.componentInstance.context = currentContext;
|
||||||
|
|
||||||
if (componentInstance.mountState === MountState.New) {
|
if (componentInstance.mountState === MountState.New) {
|
||||||
$element = mountComponent(componentInstance);
|
$element = mountComponent(componentInstance);
|
||||||
setupComponentUpdateListener(parentEl, $element, $parent, index);
|
setupComponentUpdateListener(parentEl, $element, $parent, currentContext, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $element;
|
return $element;
|
||||||
@ -251,6 +265,7 @@ function setupComponentUpdateListener(
|
|||||||
parentEl: DOMElement,
|
parentEl: DOMElement,
|
||||||
$element: VirtualElementComponent,
|
$element: VirtualElementComponent,
|
||||||
$parent: VirtualElementParent | VirtualDomHead,
|
$parent: VirtualElementParent | VirtualDomHead,
|
||||||
|
currentContext: CurrentContext,
|
||||||
index: number,
|
index: number,
|
||||||
) {
|
) {
|
||||||
const { componentInstance } = $element;
|
const { componentInstance } = $element;
|
||||||
@ -261,6 +276,7 @@ function setupComponentUpdateListener(
|
|||||||
$parent.children[index],
|
$parent.children[index],
|
||||||
componentInstance.$element,
|
componentInstance.$element,
|
||||||
$parent,
|
$parent,
|
||||||
|
currentContext,
|
||||||
index,
|
index,
|
||||||
{ skipComponentUpdate: true },
|
{ skipComponentUpdate: true },
|
||||||
);
|
);
|
||||||
@ -270,6 +286,7 @@ function setupComponentUpdateListener(
|
|||||||
function mountChildren(
|
function mountChildren(
|
||||||
parentEl: DOMElement,
|
parentEl: DOMElement,
|
||||||
$element: VirtualElementComponent | VirtualElementFragment,
|
$element: VirtualElementComponent | VirtualElementFragment,
|
||||||
|
currentContext: CurrentContext,
|
||||||
options: {
|
options: {
|
||||||
nextSibling?: ChildNode;
|
nextSibling?: ChildNode;
|
||||||
fragment?: DocumentFragment;
|
fragment?: DocumentFragment;
|
||||||
@ -279,20 +296,22 @@ function mountChildren(
|
|||||||
const { children } = $element;
|
const { children } = $element;
|
||||||
for (let i = 0, l = children.length; i < l; i++) {
|
for (let i = 0, l = children.length; i < l; i++) {
|
||||||
const $child = children[i];
|
const $child = children[i];
|
||||||
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $element, i, options);
|
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $element, currentContext, i, options);
|
||||||
if ($renderedChild !== $child) {
|
if ($renderedChild !== $child) {
|
||||||
children[i] = $renderedChild;
|
children[i] = $renderedChild;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmountChildren(parentEl: DOMElement, $element: VirtualElementComponent | VirtualElementFragment) {
|
function unmountChildren(
|
||||||
|
parentEl: DOMElement, $element: VirtualElementComponent | VirtualElementFragment, currentContext: CurrentContext,
|
||||||
|
) {
|
||||||
for (const $child of $element.children) {
|
for (const $child of $element.children) {
|
||||||
renderWithVirtual(parentEl, $child, undefined, $element, -1);
|
renderWithVirtual(parentEl, $child, undefined, $element, currentContext, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNode($element: VirtualElementReal, isSvg?: true): Node {
|
function createNode($element: VirtualElementReal, currentContext: CurrentContext, isSvg?: true): Node {
|
||||||
if ($element.type === VirtualType.Empty) {
|
if ($element.type === VirtualType.Empty) {
|
||||||
return document.createTextNode('');
|
return document.createTextNode('');
|
||||||
}
|
}
|
||||||
@ -319,7 +338,7 @@ function createNode($element: VirtualElementReal, isSvg?: true): Node {
|
|||||||
|
|
||||||
for (let i = 0, l = children.length; i < l; i++) {
|
for (let i = 0, l = children.length; i < l; i++) {
|
||||||
const $child = children[i];
|
const $child = children[i];
|
||||||
const $renderedChild = renderWithVirtual(element, undefined, $child, $element, i, { isSvg });
|
const $renderedChild = renderWithVirtual(element, undefined, $child, $element, currentContext, i, { isSvg });
|
||||||
if ($renderedChild !== $child) {
|
if ($renderedChild !== $child) {
|
||||||
children[i] = $renderedChild;
|
children[i] = $renderedChild;
|
||||||
}
|
}
|
||||||
@ -331,6 +350,7 @@ function createNode($element: VirtualElementReal, isSvg?: true): Node {
|
|||||||
function remount(
|
function remount(
|
||||||
parentEl: DOMElement,
|
parentEl: DOMElement,
|
||||||
$current: VirtualElement,
|
$current: VirtualElement,
|
||||||
|
currentContext: CurrentContext,
|
||||||
node: Node | undefined,
|
node: Node | undefined,
|
||||||
componentNextSibling?: ChildNode,
|
componentNextSibling?: ChildNode,
|
||||||
) {
|
) {
|
||||||
@ -342,7 +362,7 @@ function remount(
|
|||||||
unmountComponent($current.componentInstance);
|
unmountComponent($current.componentInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
unmountChildren(parentEl, $current);
|
unmountChildren(parentEl, $current, currentContext);
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
insertBefore(parentEl, node, componentNextSibling);
|
insertBefore(parentEl, node, componentNextSibling);
|
||||||
@ -400,6 +420,7 @@ function getNextSibling($current: VirtualElement): ChildNode | undefined {
|
|||||||
function renderChildren(
|
function renderChildren(
|
||||||
$current: VirtualElementParent,
|
$current: VirtualElementParent,
|
||||||
$new: VirtualElementParent,
|
$new: VirtualElementParent,
|
||||||
|
currentContext: CurrentContext,
|
||||||
currentEl: DOMElement,
|
currentEl: DOMElement,
|
||||||
nextSibling?: ChildNode,
|
nextSibling?: ChildNode,
|
||||||
forceMoveToEnd = false,
|
forceMoveToEnd = false,
|
||||||
@ -410,7 +431,7 @@ function renderChildren(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (('props' in $new) && $new.props.teactFastList) {
|
if (('props' in $new) && $new.props.teactFastList) {
|
||||||
renderFastListChildren($current, $new, currentEl);
|
renderFastListChildren($current, $new, currentContext, currentEl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +454,7 @@ function renderChildren(
|
|||||||
currentChildren[i],
|
currentChildren[i],
|
||||||
newChildren[i],
|
newChildren[i],
|
||||||
$new,
|
$new,
|
||||||
|
currentContext,
|
||||||
i,
|
i,
|
||||||
i >= currentChildrenLength ? { fragment, isSvg } : { nextSibling, forceMoveToEnd, isSvg },
|
i >= currentChildrenLength ? { fragment, isSvg } : { nextSibling, forceMoveToEnd, isSvg },
|
||||||
);
|
);
|
||||||
@ -449,7 +471,9 @@ function renderChildren(
|
|||||||
|
|
||||||
// This function allows to prepend/append a bunch of new DOM nodes to the top/bottom of preserved ones.
|
// This function allows to prepend/append a bunch of new DOM nodes to the top/bottom of preserved ones.
|
||||||
// It also allows to selectively move particular preserved nodes within their DOM list.
|
// It also allows to selectively move particular preserved nodes within their DOM list.
|
||||||
function renderFastListChildren($current: VirtualElementParent, $new: VirtualElementParent, currentEl: DOMElement) {
|
function renderFastListChildren(
|
||||||
|
$current: VirtualElementParent, $new: VirtualElementParent, currentContext: CurrentContext, currentEl: DOMElement,
|
||||||
|
) {
|
||||||
const currentChildren = $current.children;
|
const currentChildren = $current.children;
|
||||||
const newChildren = $new.children;
|
const newChildren = $new.children;
|
||||||
|
|
||||||
@ -484,7 +508,7 @@ function renderFastListChildren($current: VirtualElementParent, $new: VirtualEle
|
|||||||
|
|
||||||
// First we process removed children
|
// First we process removed children
|
||||||
if (isKeyPresent && !newKeys.has(key)) {
|
if (isKeyPresent && !newKeys.has(key)) {
|
||||||
renderWithVirtual(currentEl, $currentChild, undefined, $new, -1);
|
renderWithVirtual(currentEl, $currentChild, undefined, $new, currentContext, -1);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else if (!isKeyPresent) {
|
} else if (!isKeyPresent) {
|
||||||
@ -495,7 +519,7 @@ function renderFastListChildren($current: VirtualElementParent, $new: VirtualEle
|
|||||||
key = `${INDEX_KEY_PREFIX}${i}`;
|
key = `${INDEX_KEY_PREFIX}${i}`;
|
||||||
// Otherwise, we just remove it
|
// Otherwise, we just remove it
|
||||||
} else {
|
} else {
|
||||||
renderWithVirtual(currentEl, $currentChild, undefined, $new, -1);
|
renderWithVirtual(currentEl, $currentChild, undefined, $new, currentContext, -1);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -531,7 +555,7 @@ function renderFastListChildren($current: VirtualElementParent, $new: VirtualEle
|
|||||||
|
|
||||||
// This prepends new children to the top
|
// This prepends new children to the top
|
||||||
if (fragmentSize) {
|
if (fragmentSize) {
|
||||||
renderFragment(fragmentIndex!, fragmentSize, currentEl, $new);
|
renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext);
|
||||||
fragmentSize = undefined;
|
fragmentSize = undefined;
|
||||||
fragmentIndex = undefined;
|
fragmentIndex = undefined;
|
||||||
}
|
}
|
||||||
@ -551,7 +575,9 @@ function renderFastListChildren($current: VirtualElementParent, $new: VirtualEle
|
|||||||
const nextSibling = currentEl.childNodes[isMovingDown ? i + 1 : i];
|
const nextSibling = currentEl.childNodes[isMovingDown ? i + 1 : i];
|
||||||
const options = shouldMoveNode ? (nextSibling ? { nextSibling } : { forceMoveToEnd: true }) : undefined;
|
const options = shouldMoveNode ? (nextSibling ? { nextSibling } : { forceMoveToEnd: true }) : undefined;
|
||||||
|
|
||||||
const $renderedChild = renderWithVirtual(currentEl, currentChildInfo.$element, $newChild, $new, i, options);
|
const $renderedChild = renderWithVirtual(
|
||||||
|
currentEl, currentChildInfo.$element, $newChild, $new, currentContext, i, options,
|
||||||
|
);
|
||||||
if ($renderedChild !== $newChild) {
|
if ($renderedChild !== $newChild) {
|
||||||
newChildren[i] = $renderedChild;
|
newChildren[i] = $renderedChild;
|
||||||
}
|
}
|
||||||
@ -559,18 +585,24 @@ function renderFastListChildren($current: VirtualElementParent, $new: VirtualEle
|
|||||||
|
|
||||||
// This appends new children to the bottom
|
// This appends new children to the bottom
|
||||||
if (fragmentSize) {
|
if (fragmentSize) {
|
||||||
renderFragment(fragmentIndex!, fragmentSize, currentEl, $new);
|
renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFragment(
|
function renderFragment(
|
||||||
fragmentIndex: number, fragmentSize: number, parentEl: DOMElement, $parent: VirtualElementParent,
|
fragmentIndex: number,
|
||||||
|
fragmentSize: number,
|
||||||
|
parentEl: DOMElement,
|
||||||
|
$parent: VirtualElementParent,
|
||||||
|
currentContext: CurrentContext,
|
||||||
) {
|
) {
|
||||||
const nextSibling = parentEl.childNodes[fragmentIndex];
|
const nextSibling = parentEl.childNodes[fragmentIndex];
|
||||||
|
|
||||||
if (fragmentSize === 1) {
|
if (fragmentSize === 1) {
|
||||||
const $child = $parent.children[fragmentIndex];
|
const $child = $parent.children[fragmentIndex];
|
||||||
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $parent, fragmentIndex, { nextSibling });
|
const $renderedChild = renderWithVirtual(
|
||||||
|
parentEl, undefined, $child, $parent, currentContext, fragmentIndex, { nextSibling },
|
||||||
|
);
|
||||||
if ($renderedChild !== $child) {
|
if ($renderedChild !== $child) {
|
||||||
$parent.children[fragmentIndex] = $renderedChild;
|
$parent.children[fragmentIndex] = $renderedChild;
|
||||||
}
|
}
|
||||||
@ -582,7 +614,7 @@ function renderFragment(
|
|||||||
|
|
||||||
for (let i = fragmentIndex; i < fragmentIndex + fragmentSize; i++) {
|
for (let i = fragmentIndex; i < fragmentIndex + fragmentSize; i++) {
|
||||||
const $child = $parent.children[i];
|
const $child = $parent.children[i];
|
||||||
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $parent, i, { fragment });
|
const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $parent, currentContext, i, { fragment });
|
||||||
if ($renderedChild !== $child) {
|
if ($renderedChild !== $child) {
|
||||||
$parent.children[i] = $renderedChild;
|
$parent.children[i] = $renderedChild;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { incrementOverlayCounter } from '../../util/debugOverlay';
|
|||||||
import { orderBy } from '../../util/iteratees';
|
import { orderBy } from '../../util/iteratees';
|
||||||
import safeExec from '../../util/safeExec';
|
import safeExec from '../../util/safeExec';
|
||||||
import { throttleWith } from '../../util/schedulers';
|
import { throttleWith } from '../../util/schedulers';
|
||||||
import { isSignal } from '../../util/signals';
|
import { createSignal, isSignal, type Signal } from '../../util/signals';
|
||||||
import { requestMeasure, requestMutation } from '../fasterdom/fasterdom';
|
import { requestMeasure, requestMutation } from '../fasterdom/fasterdom';
|
||||||
|
|
||||||
export type Props = AnyLiteral;
|
export type Props = AnyLiteral;
|
||||||
@ -71,6 +71,7 @@ interface ComponentInstance {
|
|||||||
props: Props;
|
props: Props;
|
||||||
renderedValue?: any;
|
renderedValue?: any;
|
||||||
mountState: MountState;
|
mountState: MountState;
|
||||||
|
context?: Record<string, Signal<unknown>>;
|
||||||
hooks?: {
|
hooks?: {
|
||||||
state?: {
|
state?: {
|
||||||
cursor: number;
|
cursor: number;
|
||||||
@ -132,12 +133,20 @@ export type TeactNode =
|
|||||||
type Effect = () => (NoneToVoidFunction | void);
|
type Effect = () => (NoneToVoidFunction | void);
|
||||||
type EffectCleanup = NoneToVoidFunction;
|
type EffectCleanup = NoneToVoidFunction;
|
||||||
|
|
||||||
|
export type Context<T> = {
|
||||||
|
defaultValue?: T;
|
||||||
|
contextId: string;
|
||||||
|
Provider: FC<{ value: T; children: TeactNode }>;
|
||||||
|
};
|
||||||
|
|
||||||
const Fragment = Symbol('Fragment');
|
const Fragment = Symbol('Fragment');
|
||||||
|
|
||||||
const DEBUG_RENDER_THRESHOLD = 7;
|
const DEBUG_RENDER_THRESHOLD = 7;
|
||||||
const DEBUG_EFFECT_THRESHOLD = 7;
|
const DEBUG_EFFECT_THRESHOLD = 7;
|
||||||
const DEBUG_SILENT_RENDERS_FOR = new Set(['TeactMemoWrapper', 'TeactNContainer', 'Button', 'ListItem', 'MenuItem']);
|
const DEBUG_SILENT_RENDERS_FOR = new Set(['TeactMemoWrapper', 'TeactNContainer', 'Button', 'ListItem', 'MenuItem']);
|
||||||
|
|
||||||
|
let contextCounter = 0;
|
||||||
|
|
||||||
let lastComponentId = 0;
|
let lastComponentId = 0;
|
||||||
let renderingInstance: ComponentInstance;
|
let renderingInstance: ComponentInstance;
|
||||||
|
|
||||||
@ -176,7 +185,7 @@ function createComponentInstance(Component: FC, props: Props, children: any[]):
|
|||||||
}
|
}
|
||||||
|
|
||||||
const componentInstance: ComponentInstance = {
|
const componentInstance: ComponentInstance = {
|
||||||
id: ++lastComponentId,
|
id: -1,
|
||||||
$element: undefined as unknown as VirtualElementComponent,
|
$element: undefined as unknown as VirtualElementComponent,
|
||||||
Component,
|
Component,
|
||||||
name: Component.name,
|
name: Component.name,
|
||||||
@ -476,6 +485,7 @@ export function hasElementChanged($old: VirtualElement, $new: VirtualElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function mountComponent(componentInstance: ComponentInstance) {
|
export function mountComponent(componentInstance: ComponentInstance) {
|
||||||
|
componentInstance.id = ++lastComponentId;
|
||||||
renderComponent(componentInstance);
|
renderComponent(componentInstance);
|
||||||
componentInstance.mountState = MountState.Mounted;
|
componentInstance.mountState = MountState.Mounted;
|
||||||
return componentInstance.$element;
|
return componentInstance.$element;
|
||||||
@ -900,6 +910,42 @@ export function useRef<T>(initial?: T | null) {
|
|||||||
return byCursor[cursor];
|
return byCursor[cursor];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createContext<T>(defaultValue?: T): Context<T> {
|
||||||
|
const contextId = String(contextCounter++);
|
||||||
|
|
||||||
|
function TeactContextProvider(props: { value: T; children: TeactNode }) {
|
||||||
|
const [getValue, setValue] = useSignal(props.value ?? defaultValue);
|
||||||
|
// Create a new object to avoid mutations in the parent context
|
||||||
|
renderingInstance.context = { ...renderingInstance.context };
|
||||||
|
|
||||||
|
renderingInstance.context[contextId] = getValue;
|
||||||
|
setValue(props.value);
|
||||||
|
return props.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeactContextProvider.DEBUG_contentComponentName = contextId;
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
defaultValue,
|
||||||
|
contextId,
|
||||||
|
Provider: TeactContextProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useContextSignal<T>(context: Context<T>) {
|
||||||
|
const [getDefaultValue] = useSignal(context.defaultValue);
|
||||||
|
|
||||||
|
return renderingInstance.context?.[context.contextId] || getDefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSignal<T>(initial?: T) {
|
||||||
|
const signalRef = useRef<ReturnType<typeof createSignal<T>>>();
|
||||||
|
signalRef.current ??= createSignal<T>(initial);
|
||||||
|
return signalRef.current;
|
||||||
|
}
|
||||||
|
|
||||||
export function memo<T extends FC_withDebug>(Component: T, debugKey?: string) {
|
export function memo<T extends FC_withDebug>(Component: T, debugKey?: string) {
|
||||||
function TeactMemoWrapper(props: Props) {
|
function TeactMemoWrapper(props: Props) {
|
||||||
return useMemo(
|
return useMemo(
|
||||||
@ -929,6 +975,10 @@ export function DEBUG_resolveComponentName(Component: FC_withDebug) {
|
|||||||
return `memo>${DEBUG_contentComponentName}`;
|
return `memo>${DEBUG_contentComponentName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name === 'TeactContextProvider') {
|
||||||
|
return `context>id${DEBUG_contentComponentName}`;
|
||||||
|
}
|
||||||
|
|
||||||
return name + (DEBUG_contentComponentName ? `>${DEBUG_contentComponentName}` : '');
|
return name + (DEBUG_contentComponentName ? `>${DEBUG_contentComponentName}` : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user