mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
refactor(schema): simplify validation and OpenAPI generation (#2709)
This commit is contained in:
1 parent
4bc5593f9f
commit
de1fd95a99
510 files changed
+29494
-36131
No files matched your search
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {StickerAnimationOptions} from '@fluxer/constants/src/UserConstants';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type StickerAnimation = number;
|
||||
export type AnimatedMediaKind = 'emoji' | 'gif' | 'sticker';
|
||||
@@ -388,7 +388,7 @@ export type MotionPreferencesSnapshot = SnapshotFrom<typeof motionPreferencesMac
|
||||
export type MotionPreferencesStateValue = 'full' | 'reduced';
|
||||
|
||||
export function createMotionPreferencesSnapshot(input: MotionPreferencesInput = {}): MotionPreferencesSnapshot {
|
||||
return getInitialSnapshot(motionPreferencesMachine, input);
|
||||
return initialTransition(motionPreferencesMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMotionPreferencesSnapshot(
|
||||
|
||||
@@ -99,7 +99,7 @@ const RootComponent = React.forwardRef<HTMLDivElement, ModalProps>(
|
||||
if (typeof ref === 'function') {
|
||||
ref(node);
|
||||
} else if (ref) {
|
||||
(ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
||||
ref.current = node;
|
||||
}
|
||||
},
|
||||
[ref],
|
||||
|
||||
@@ -129,7 +129,7 @@ export const SettingsModalDesktopScroll: React.FC<SettingsModalDesktopScrollProp
|
||||
if (typeof scrollerRef === 'function') {
|
||||
scrollerRef(node);
|
||||
} else {
|
||||
(scrollerRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
||||
scrollerRef.current = node;
|
||||
}
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -423,11 +423,11 @@ function resolvePinnedCallChannel(directMessagesDisabled: boolean): Channel | nu
|
||||
|
||||
interface DMChannelVisibilityControllerState {
|
||||
readonly removalDelayMs: number;
|
||||
readonly removalTimers: React.MutableRefObject<Map<string, number>>;
|
||||
readonly removalTimers: React.RefObject<Map<string, number>>;
|
||||
readonly orderedUnreadChannels: ReadonlyArray<Channel>;
|
||||
readonly setVisibleChannels: React.Dispatch<React.SetStateAction<ReadonlyArray<Channel>>>;
|
||||
readonly unreadChannels: ReadonlyArray<Channel>;
|
||||
readonly unreadIdsRef: React.MutableRefObject<ReadonlySet<string>>;
|
||||
readonly unreadIdsRef: React.RefObject<ReadonlySet<string>>;
|
||||
}
|
||||
|
||||
interface ProjectVisibleDMChannelsRequest {
|
||||
@@ -437,11 +437,11 @@ interface ProjectVisibleDMChannelsRequest {
|
||||
|
||||
class DMChannelVisibilityController {
|
||||
private readonly removalDelayMs: number;
|
||||
private readonly removalTimers: React.MutableRefObject<Map<string, number>>;
|
||||
private readonly removalTimers: React.RefObject<Map<string, number>>;
|
||||
private readonly orderedUnreadChannels: ReadonlyArray<Channel>;
|
||||
private readonly setVisibleChannels: React.Dispatch<React.SetStateAction<ReadonlyArray<Channel>>>;
|
||||
private readonly unreadChannels: ReadonlyArray<Channel>;
|
||||
private readonly unreadIdsRef: React.MutableRefObject<ReadonlySet<string>>;
|
||||
private readonly unreadIdsRef: React.RefObject<ReadonlySet<string>>;
|
||||
|
||||
public constructor({
|
||||
removalDelayMs,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type ScrollIndicatorSeverity = 'mention' | 'unread';
|
||||
export type ScrollIndicatorDirection = 'top' | 'bottom';
|
||||
@@ -238,7 +238,7 @@ export type ScrollIndicatorMachineSnapshot = SnapshotFrom<typeof scrollIndicator
|
||||
export type ScrollIndicatorStateValue = 'hidden' | 'visible';
|
||||
|
||||
export function createScrollIndicatorSnapshot(input: ScrollIndicatorMachineInput = {}): ScrollIndicatorMachineSnapshot {
|
||||
return getInitialSnapshot(scrollIndicatorStateMachine, input);
|
||||
return initialTransition(scrollIndicatorStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionScrollIndicatorSnapshot(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {type DragItem, DragItemType, type DropResult} from '@app/features/app/components/layout/types/DndTypes';
|
||||
import {getVerticalDropTargetHeight, resolveVerticalDropEdge} from '@app/features/ui/dnd/DropGeometry';
|
||||
import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface ChannelReorderPoint {
|
||||
x: number;
|
||||
@@ -244,7 +244,7 @@ export type ChannelReorderSnapshot = SnapshotFrom<typeof channelReorderStateMach
|
||||
export type ChannelReorderStateValue = 'idle' | 'resolving' | 'targeting' | 'voiceParticipantTransfer' | 'blocked';
|
||||
|
||||
export function createChannelReorderSnapshot(): ChannelReorderSnapshot {
|
||||
return getInitialSnapshot(channelReorderStateMachine);
|
||||
return initialTransition(channelReorderStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionChannelReorderSnapshot(
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import {RelativePosition} from '@app/features/ui/RelativePosition';
|
||||
import {
|
||||
assign,
|
||||
getInitialSnapshot,
|
||||
initialTransition,
|
||||
type MachineSnapshot,
|
||||
type MetaObject,
|
||||
type NonReducibleUnknown,
|
||||
@@ -380,7 +380,7 @@ export type GuildReorderSnapshot = MachineSnapshot<
|
||||
>;
|
||||
|
||||
export function createGuildReorderSnapshot(): GuildReorderSnapshot {
|
||||
return getInitialSnapshot(guildReorderStateMachine);
|
||||
return initialTransition(guildReorderStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionGuildReorderSnapshot({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {MessageNotifications} from '@fluxer/constants/src/NotificationConstants';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import type {ChannelUnreadState, ChannelUnreadStateInput} from './ChannelUnreadState';
|
||||
|
||||
interface ChannelUnreadMachineContext extends ChannelUnreadStateInput {}
|
||||
@@ -74,7 +74,7 @@ export const channelUnreadStateMachine = setup({
|
||||
export type ChannelUnreadSnapshot = SnapshotFrom<typeof channelUnreadStateMachine>;
|
||||
|
||||
export function createChannelUnreadSnapshot(input: ChannelUnreadStateInput): ChannelUnreadSnapshot {
|
||||
return getInitialSnapshot(channelUnreadStateMachine, input);
|
||||
return initialTransition(channelUnreadStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionChannelUnreadSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type WizardStep =
|
||||
| 'welcome'
|
||||
@@ -157,7 +157,7 @@ export const setupWizardStateMachine = setup({
|
||||
export type SetupWizardSnapshot = SnapshotFrom<typeof setupWizardStateMachine>;
|
||||
|
||||
export function createSetupWizardSnapshot(): SetupWizardSnapshot {
|
||||
return getInitialSnapshot(setupWizardStateMachine);
|
||||
return initialTransition(setupWizardStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionSetupWizardSnapshot(
|
||||
|
||||
@@ -127,8 +127,7 @@ export const FriendSelector: React.FC<FriendSelectorProps> = observer(
|
||||
const isMaxed = maxSelections !== undefined && selectedUserIds.length >= maxSelections;
|
||||
const isMutableRefObject = (
|
||||
ref: React.Ref<HTMLInputElement> | undefined,
|
||||
): ref is React.MutableRefObject<HTMLInputElement | null> =>
|
||||
typeof ref === 'object' && ref !== null && 'current' in ref;
|
||||
): ref is React.RefObject<HTMLInputElement | null> => typeof ref === 'object' && ref !== null && 'current' in ref;
|
||||
const renderSearchInput = ({inputProps, inputClassName, ref: forwardedRef}: RenderInputArgs) => {
|
||||
const handleRef = (node: HTMLInputElement | null) => {
|
||||
inputRef.current = node;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type HoverStateValue = 'idle' | 'hovering';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const hoverStateMachine = setup({
|
||||
export type HoverStateSnapshot = SnapshotFrom<typeof hoverStateMachine>;
|
||||
|
||||
export function createHoverStateSnapshot(): HoverStateSnapshot {
|
||||
return getInitialSnapshot(hoverStateMachine);
|
||||
return initialTransition(hoverStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionHoverStateSnapshot(snapshot: HoverStateSnapshot, event: HoverStateEvent): HoverStateSnapshot {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {FormEvent} from 'react';
|
||||
import type {SubmitEvent} from 'react';
|
||||
import {useCallback, useLayoutEffect, useState} from 'react';
|
||||
|
||||
interface FormField {
|
||||
@@ -33,7 +33,7 @@ export interface UseFormReturn {
|
||||
setErrors: (errors: ReadonlyMap<string, string>) => void;
|
||||
getValue: (fieldName: string) => string;
|
||||
getError: (fieldName: string) => string | undefined;
|
||||
handleSubmit: (event?: FormEvent) => Promise<void>;
|
||||
handleSubmit: (event?: SubmitEvent<HTMLFormElement>) => Promise<void>;
|
||||
isSubmitting: boolean;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export function useForm({initialValues, onSubmit}: UseFormOptions): UseFormRetur
|
||||
const getValue = useCallback((fieldName: string): string => fields.get(fieldName)?.value ?? '', [fields]);
|
||||
const getError = useCallback((fieldName: string): string | undefined => fields.get(fieldName)?.error, [fields]);
|
||||
const handleSubmit = useCallback(
|
||||
async (event?: FormEvent) => {
|
||||
async (event?: SubmitEvent<HTMLFormElement>) => {
|
||||
event?.preventDefault();
|
||||
if (lifecycle.activeSubmission != null) {
|
||||
return;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {type LegacyRef, type MutableRefObject, type RefCallback, useCallback, useRef} from 'react';
|
||||
import {type Ref, type RefCallback, useCallback, useRef} from 'react';
|
||||
|
||||
export function useMergeRefs<T>(
|
||||
refs: Array<MutableRefObject<T | null> | LegacyRef<T> | undefined | null>,
|
||||
): RefCallback<T> {
|
||||
export function useMergeRefs<T>(refs: Array<Ref<T> | undefined>): RefCallback<T> {
|
||||
const latestRefs = useRef(refs);
|
||||
latestRefs.current = refs;
|
||||
return useCallback((value: T | null) => {
|
||||
@@ -12,7 +10,7 @@ export function useMergeRefs<T>(
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref != null) {
|
||||
(ref as MutableRefObject<T | null>).current = value;
|
||||
ref.current = value;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type ReviewStep = 'account' | 'scopes' | 'community' | 'permissions';
|
||||
export type AuthorizePhase =
|
||||
@@ -80,7 +80,7 @@ export type AuthorizeMachineSnapshot = SnapshotFrom<typeof authorizeStateMachine
|
||||
export type AuthorizeMachineStateValue = 'loading' | 'session_expired' | 'invalid_request' | 'review' | 'success';
|
||||
|
||||
export function createAuthorizeSnapshot(): AuthorizeMachineSnapshot {
|
||||
return getInitialSnapshot(authorizeStateMachine);
|
||||
return initialTransition(authorizeStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionAuthorizeSnapshot(
|
||||
|
||||
@@ -10,7 +10,7 @@ import {useId} from 'react';
|
||||
type FieldErrors = ReadonlyMap<string, string> | null | undefined;
|
||||
|
||||
export interface AuthFormControllerLike {
|
||||
handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
||||
handleSubmit: (event: React.SubmitEvent<HTMLFormElement>) => void;
|
||||
getValue: (name: string) => string;
|
||||
setValue: (name: string, value: string) => void;
|
||||
getError: (name: string) => string | null | undefined;
|
||||
|
||||
@@ -25,7 +25,7 @@ export const AutocompleteChannel = observer(
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}) => {
|
||||
const channels = options.filter(isChannel);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {type AutocompleteOption, isCommand} from '@app/features/channel/components/Autocomplete';
|
||||
import {AutocompleteItem} from '@app/features/channel/components/AutocompleteItem';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import type {MutableRefObject} from 'react';
|
||||
import type {RefObject} from 'react';
|
||||
|
||||
interface Props {
|
||||
onSelect: (option: AutocompleteOption) => void;
|
||||
@@ -12,7 +12,7 @@ interface Props {
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ interface AutocompleteCommandChoiceProps {
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ interface AutocompleteCommandOptionalAddProps {
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export const AutocompleteEmoji = observer(
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}) => {
|
||||
const {i18n} = useLingui();
|
||||
|
||||
@@ -85,7 +85,7 @@ export const AutocompleteGif = observer(
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}) => {
|
||||
const {i18n} = useLingui();
|
||||
|
||||
@@ -24,7 +24,7 @@ export const AutocompleteMeme = observer(
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}) => {
|
||||
const memes = options.filter(isMeme);
|
||||
|
||||
@@ -37,7 +37,7 @@ const NOTIFY_USERS_WITH_THIS_ROLE_WHO_HAVE_PERMISSION_DESCRIPTOR = msg({
|
||||
comment: 'Description text in the channel and chat autocomplete mention. Keep the tone plain and specific.',
|
||||
});
|
||||
|
||||
type RowRef = React.MutableRefObject<Array<HTMLButtonElement | null>> | undefined;
|
||||
type RowRef = React.RefObject<Array<HTMLButtonElement | null>> | undefined;
|
||||
type OptionIdGetter = ((index: number) => string) | undefined;
|
||||
|
||||
function resolveOptionId(getOptionId: OptionIdGetter, index: number): string | undefined {
|
||||
@@ -76,7 +76,7 @@ export const AutocompleteMention = observer(function AutocompleteMention({
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}) {
|
||||
const {i18n} = useLingui();
|
||||
|
||||
@@ -43,7 +43,7 @@ export const AutocompleteSticker = observer(
|
||||
options: Array<AutocompleteOption>;
|
||||
onMouseEnter: (index: number) => void;
|
||||
onMouseLeave: () => void;
|
||||
rowRefs?: React.MutableRefObject<Array<HTMLButtonElement | null>>;
|
||||
rowRefs?: React.RefObject<Array<HTMLButtonElement | null>>;
|
||||
getOptionId?: (index: number) => string;
|
||||
}) => {
|
||||
const stickers = options.filter(isSticker);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type {Channel} from '@app/features/channel/models/Channel';
|
||||
import type {Message} from '@app/features/messaging/models/MessagingMessage';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface SearchMachineStateIdle {
|
||||
status: 'idle';
|
||||
@@ -132,7 +132,7 @@ export const searchStateMachine = setup({
|
||||
export type SearchMachineSnapshot = SnapshotFrom<typeof searchStateMachine>;
|
||||
|
||||
export function createSearchMachineSnapshot(): SearchMachineSnapshot {
|
||||
return getInitialSnapshot(searchStateMachine);
|
||||
return initialTransition(searchStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionSearchMachineSnapshot(
|
||||
|
||||
@@ -123,7 +123,7 @@ export const AddFriendForm: React.FC<AddFriendFormProps> = observer(({onSuccess}
|
||||
}
|
||||
return getSendFriendRequestErrorMessage(i18n, errorCode, null);
|
||||
};
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const [username, discriminator] = parseInput(input['trim']());
|
||||
if (!username || !discriminator || !/^\d{4}$/.test(discriminator)) {
|
||||
|
||||
@@ -34,7 +34,7 @@ interface VirtualRowRendererProps {
|
||||
selectedColumnInRow: number;
|
||||
emojiRowIndex: number;
|
||||
shouldScrollOnSelection?: boolean;
|
||||
emojiRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
|
||||
emojiRefs: React.RefObject<Map<string, HTMLButtonElement>>;
|
||||
}
|
||||
|
||||
const VirtualRowRenderer: React.FC<VirtualRowRendererProps> = React.memo(
|
||||
@@ -208,7 +208,7 @@ interface VirtualizedRowProps {
|
||||
selectedColumn: number;
|
||||
emojiRowIndex: number;
|
||||
shouldScrollOnSelection?: boolean;
|
||||
emojiRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
|
||||
emojiRefs: React.RefObject<Map<string, HTMLButtonElement>>;
|
||||
}
|
||||
|
||||
export const VirtualizedRow: React.FC<VirtualizedRowProps> = observer(
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface MembersPaginationProps {
|
||||
isSearching: boolean;
|
||||
activeEllipsis: PaginationEllipsisSide | null;
|
||||
pageJumpValue: string;
|
||||
ellipsisInputRef: React.MutableRefObject<HTMLInputElement | null>;
|
||||
ellipsisInputRef: React.RefObject<HTMLInputElement | null>;
|
||||
onPageSelect: (page: number) => void;
|
||||
setActiveEllipsis: React.Dispatch<React.SetStateAction<PaginationEllipsisSide | null>>;
|
||||
setPageJumpValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
@@ -57,7 +57,7 @@ export function MembersPagination({
|
||||
setPageJumpValue('');
|
||||
}, [setActiveEllipsis, setPageJumpValue]);
|
||||
const handleJumpSubmit = useCallback(
|
||||
(event: React.FormEvent<HTMLFormElement>) => {
|
||||
(event: React.SubmitEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const nextPage = parseInt(pageJumpValue, 10);
|
||||
if (!Number.isNaN(nextPage) && nextPage >= 1 && nextPage <= totalPages) {
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ export interface MembersTableFooterProps {
|
||||
isSearching: boolean;
|
||||
activeEllipsis: PaginationEllipsisSide | null;
|
||||
pageJumpValue: string;
|
||||
ellipsisInputRef: React.MutableRefObject<HTMLInputElement | null>;
|
||||
ellipsisInputRef: React.RefObject<HTMLInputElement | null>;
|
||||
onPageSelect: (page: number) => void;
|
||||
setActiveEllipsis: React.Dispatch<React.SetStateAction<PaginationEllipsisSide | null>>;
|
||||
setPageJumpValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
|
||||
+1
-1
@@ -254,7 +254,7 @@ export function assignRef<T>(ref: React.Ref<T> | undefined, value: T | null): vo
|
||||
ref(value);
|
||||
return;
|
||||
}
|
||||
(ref as React.MutableRefObject<T | null>).current = value;
|
||||
ref.current = value;
|
||||
}
|
||||
|
||||
export function normalizeFilterKey(filterKey: string): string {
|
||||
|
||||
+1
-4
@@ -90,10 +90,7 @@ const UNNAMED_DESCRIPTOR = msg({
|
||||
comment: 'Short label in the channel and chat use message search autocomplete. Keep it concise.',
|
||||
});
|
||||
|
||||
function resolveInputCursorPosition(
|
||||
inputRef: React.MutableRefObject<LexicalSearchInputHandle | null>,
|
||||
value: string,
|
||||
): number {
|
||||
function resolveInputCursorPosition(inputRef: React.RefObject<LexicalSearchInputHandle | null>, value: string): number {
|
||||
const input = inputRef.current;
|
||||
if (input == null) {
|
||||
return value.length;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {View} from '@app/features/channel/components/pickers/gif/GifPickerTypes';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
declare const gifPickerRequestIdBrand: unique symbol;
|
||||
|
||||
@@ -576,7 +576,7 @@ export const gifPickerStateMachine = setup({
|
||||
});
|
||||
|
||||
export function createGifPickerSnapshot(): GifPickerMachineSnapshot {
|
||||
return getInitialSnapshot(gifPickerStateMachine);
|
||||
return initialTransition(gifPickerStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionGifPickerSnapshot(
|
||||
|
||||
@@ -59,7 +59,7 @@ const assignRef = <T,>(ref: React.Ref<T> | null | undefined, value: T | null) =>
|
||||
ref(value);
|
||||
return;
|
||||
}
|
||||
(ref as React.MutableRefObject<T | null>).current = value;
|
||||
ref.current = value;
|
||||
};
|
||||
export const PickerSearchInput = React.forwardRef<HTMLInputElement, PickerSearchInputProps>(
|
||||
(
|
||||
|
||||
@@ -32,7 +32,7 @@ interface VirtualRowRendererProps {
|
||||
selectedColumn: number;
|
||||
stickerRowIndex: number;
|
||||
shouldScrollOnSelection?: boolean;
|
||||
stickerRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
|
||||
stickerRefs: React.RefObject<Map<string, HTMLButtonElement>>;
|
||||
channel?: Channel | null;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ interface StickerButtonProps {
|
||||
sticker: GuildSticker;
|
||||
stickerKey: string;
|
||||
isSelected: boolean;
|
||||
stickerRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
|
||||
stickerRefs: React.RefObject<Map<string, HTMLButtonElement>>;
|
||||
shouldScrollOnSelection?: boolean;
|
||||
handleStickerClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
handleContextMenu: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
@@ -296,7 +296,7 @@ interface VirtualRowWrapperProps {
|
||||
selectedColumn: number;
|
||||
stickerRowIndex: number;
|
||||
shouldScrollOnSelection?: boolean;
|
||||
stickerRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
|
||||
stickerRefs: React.RefObject<Map<string, HTMLButtonElement>>;
|
||||
channel?: Channel | null;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {hasVisibleMessageContent} from '@app/features/messaging/utils/VisibleMessageContent';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type MobileTextareaVisibleButton = 'send' | 'voice';
|
||||
export type MobileTextareaButtonMode = 'voice' | 'sendReady' | 'sendBlocked';
|
||||
@@ -144,7 +144,7 @@ export const mobileTextareaButtonStateMachine = setup({
|
||||
export type MobileTextareaButtonSnapshot = SnapshotFrom<typeof mobileTextareaButtonStateMachine>;
|
||||
|
||||
export function createMobileTextareaButtonSnapshot(): MobileTextareaButtonSnapshot {
|
||||
return getInitialSnapshot(mobileTextareaButtonStateMachine);
|
||||
return initialTransition(mobileTextareaButtonStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionMobileTextareaButtonSnapshot(
|
||||
|
||||
@@ -36,7 +36,7 @@ interface TextareaInputFieldProps {
|
||||
placeholder: string;
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
scrollerRef?: React.RefObject<ScrollerHandle | null>;
|
||||
shouldStickToBottomRef?: React.MutableRefObject<boolean>;
|
||||
shouldStickToBottomRef?: React.RefObject<boolean>;
|
||||
isFocused?: boolean;
|
||||
isAutocompleteAttached: boolean;
|
||||
autocompleteListId?: string;
|
||||
|
||||
@@ -16,7 +16,7 @@ import {useLingui} from '@lingui/react/macro';
|
||||
import {ArrowLeftIcon, MagnifyingGlassIcon, XIcon} from '@phosphor-icons/react';
|
||||
import {clsx} from 'clsx';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import {type FormEvent, type KeyboardEvent, useCallback, useEffect, useRef, useState} from 'react';
|
||||
import {type KeyboardEvent, type SubmitEvent, useCallback, useEffect, useRef, useState} from 'react';
|
||||
|
||||
const ALL_DESCRIPTOR = msg({
|
||||
message: 'All',
|
||||
@@ -155,7 +155,7 @@ export const DiscoveryNavbar = observer(function DiscoveryNavbar({
|
||||
[onCategorySelect],
|
||||
);
|
||||
const handleSubmit = useCallback(
|
||||
(event: FormEvent<HTMLFormElement>) => {
|
||||
(event: SubmitEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const trimmedQuery = draftQuery.trim();
|
||||
setDraftQuery(trimmedQuery);
|
||||
|
||||
@@ -47,29 +47,6 @@ const CANARY_ENVELOPE: ExperimentAssignmentsResponse = {
|
||||
assignments: {voice_noise_suppression: CANARY_ASSIGNMENT},
|
||||
};
|
||||
|
||||
interface ScheduleRecorder {
|
||||
delays: Array<number>;
|
||||
restore: () => void;
|
||||
}
|
||||
|
||||
function recordScheduledDelays(): ScheduleRecorder {
|
||||
const delays: Array<number> = [];
|
||||
const original = globalThis.setTimeout;
|
||||
const patched = (...args: Array<unknown>) => {
|
||||
const timeout = args[1];
|
||||
delays.push(typeof timeout === 'number' ? timeout : 0);
|
||||
return (original as unknown as (...callArgs: Array<unknown>) => NodeJS.Timeout)(...args);
|
||||
};
|
||||
globalThis.setTimeout = patched as unknown as typeof globalThis.setTimeout;
|
||||
return {
|
||||
delays,
|
||||
restore: () => {
|
||||
globalThis.setTimeout = original;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let recorder: ScheduleRecorder;
|
||||
let visibility: DocumentVisibilityState = 'visible';
|
||||
const unsubscribes: Array<() => void> = [];
|
||||
|
||||
@@ -82,7 +59,9 @@ function requestHeaders(index: number): Record<string, string> {
|
||||
}
|
||||
|
||||
function lastScheduledDelayMs(): number {
|
||||
return recorder.delays[recorder.delays.length - 1] ?? Number.NaN;
|
||||
const call = vi.mocked(setTimeout).mock.calls.at(-1);
|
||||
expect(call).toBeDefined();
|
||||
return call![1]!;
|
||||
}
|
||||
|
||||
function setVisibility(next: DocumentVisibilityState): void {
|
||||
@@ -91,9 +70,7 @@ function setVisibility(next: DocumentVisibilityState): void {
|
||||
}
|
||||
|
||||
async function settle(): Promise<void> {
|
||||
for (let index = 0; index < 4; index++) {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
}
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
}
|
||||
|
||||
function subscribe(listener: () => void): () => void {
|
||||
@@ -107,16 +84,12 @@ function voiceConfigVersion(): number {
|
||||
}
|
||||
|
||||
function deferredReply(): {resolve: (response: RestResponse<unknown>) => void} {
|
||||
let resolve: (response: RestResponse<unknown>) => void = () => {};
|
||||
const pending = new Promise<RestResponse<unknown>>((settleWith) => {
|
||||
resolve = settleWith;
|
||||
let resolve!: (response: RestResponse<unknown>) => void;
|
||||
const pending = new Promise<RestResponse<unknown>>((complete) => {
|
||||
resolve = complete;
|
||||
});
|
||||
vi.mocked(http.get).mockReturnValue(pending);
|
||||
return {
|
||||
resolve: (response) => {
|
||||
resolve(response);
|
||||
},
|
||||
};
|
||||
vi.mocked(http.get).mockReturnValueOnce(pending);
|
||||
return {resolve};
|
||||
}
|
||||
|
||||
async function adopt(response: ExperimentAssignmentsResponse): Promise<void> {
|
||||
@@ -129,7 +102,8 @@ beforeEach(() => {
|
||||
visibility = 'visible';
|
||||
Object.defineProperty(document, 'visibilityState', {configurable: true, get: () => visibility});
|
||||
vi.useFakeTimers();
|
||||
recorder = recordScheduledDelays();
|
||||
vi.spyOn(globalThis, 'setTimeout');
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.5);
|
||||
vi.mocked(http.get).mockReset();
|
||||
});
|
||||
|
||||
@@ -138,9 +112,8 @@ afterEach(() => {
|
||||
unsubscribe();
|
||||
}
|
||||
ExperimentAssignments.reset();
|
||||
recorder.restore();
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('ExperimentAssignments cold start', () => {
|
||||
@@ -217,40 +190,20 @@ describe('ExperimentAssignments response handling', () => {
|
||||
});
|
||||
|
||||
describe('ExperimentAssignments scheduling', () => {
|
||||
it('takes the poll interval from the envelope and jitters it by fifteen percent', async () => {
|
||||
const observed: Array<number> = [];
|
||||
for (let run = 0; run < 64; run++) {
|
||||
ExperimentAssignments.reset();
|
||||
await adopt({...CANARY_ENVELOPE, poll_interval_seconds: 600});
|
||||
observed.push(lastScheduledDelayMs());
|
||||
}
|
||||
for (const delay of observed) {
|
||||
expect(delay).toBeGreaterThanOrEqual(510_000);
|
||||
expect(delay).toBeLessThanOrEqual(690_000);
|
||||
}
|
||||
expect(new Set(observed).size).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it('takes the jitter ratio from the envelope', async () => {
|
||||
const observed: Array<number> = [];
|
||||
for (let run = 0; run < 64; run++) {
|
||||
ExperimentAssignments.reset();
|
||||
await adopt({...CANARY_ENVELOPE, poll_interval_seconds: 600, poll_jitter_percent: 50});
|
||||
observed.push(lastScheduledDelayMs());
|
||||
}
|
||||
for (const delay of observed) {
|
||||
expect(delay).toBeGreaterThanOrEqual(300_000);
|
||||
expect(delay).toBeLessThanOrEqual(900_000);
|
||||
}
|
||||
expect(observed.some((delay) => delay < 510_000 || delay > 690_000)).toBe(true);
|
||||
});
|
||||
|
||||
it('schedules an exact interval when the server disables jitter', async () => {
|
||||
for (let run = 0; run < 8; run++) {
|
||||
ExperimentAssignments.reset();
|
||||
await adopt({...CANARY_ENVELOPE, poll_interval_seconds: 600, poll_jitter_percent: 0});
|
||||
expect(lastScheduledDelayMs()).toBe(600_000);
|
||||
}
|
||||
it.each([
|
||||
{jitter: 15, random: 0, delay: 510_000},
|
||||
{jitter: 15, random: 0.5, delay: 600_000},
|
||||
{jitter: 15, random: 0.999999, delay: 690_000},
|
||||
{jitter: 50, random: 0, delay: 300_000},
|
||||
{jitter: 50, random: 0.5, delay: 600_000},
|
||||
{jitter: 50, random: 0.999999, delay: 899_999},
|
||||
{jitter: 0, random: 0, delay: 600_000},
|
||||
{jitter: 0, random: 0.5, delay: 600_000},
|
||||
{jitter: 0, random: 0.999999, delay: 600_000},
|
||||
])('schedules $delay ms for $jitter% jitter and random value $random', async ({jitter, random, delay}) => {
|
||||
vi.mocked(Math.random).mockReturnValue(random);
|
||||
await adopt({...CANARY_ENVELOPE, poll_interval_seconds: 600, poll_jitter_percent: jitter});
|
||||
expect(lastScheduledDelayMs()).toBe(delay);
|
||||
});
|
||||
|
||||
it('never schedules a poll below half the minimum interval', async () => {
|
||||
@@ -379,6 +332,58 @@ describe('ExperimentAssignments visibility', () => {
|
||||
});
|
||||
|
||||
describe('ExperimentAssignments lifecycle', () => {
|
||||
it.each([
|
||||
'stop',
|
||||
'reset',
|
||||
] as const)('aborts an active request on %s and starts a fresh request immediately', async (method) => {
|
||||
const stale = deferredReply();
|
||||
ExperimentAssignments.start();
|
||||
await settle();
|
||||
const signal = vi.mocked(http.get).mock.calls[0]?.[1]?.signal;
|
||||
expect(signal).toBeDefined();
|
||||
expect(signal!.aborted).toBe(false);
|
||||
|
||||
ExperimentAssignments[method]();
|
||||
expect(signal!.aborted).toBe(true);
|
||||
vi.mocked(http.get).mockResolvedValue(reply(200, CANARY_ENVELOPE, {etag: 'W/"fresh"'}));
|
||||
ExperimentAssignments.start();
|
||||
await settle();
|
||||
expect(http.get).toHaveBeenCalledTimes(2);
|
||||
expect(ExperimentAssignments.response).toEqual(CANARY_ENVELOPE);
|
||||
expect(vi.getTimerCount()).toBe(1);
|
||||
|
||||
stale.resolve(reply(200, INERT_EXPERIMENT_ASSIGNMENTS_RESPONSE, {etag: 'W/"stale"'}));
|
||||
await settle();
|
||||
expect(ExperimentAssignments.response).toEqual(CANARY_ENVELOPE);
|
||||
expect(vi.getTimerCount()).toBe(1);
|
||||
});
|
||||
|
||||
it('keeps ownership of the replacement request when the stale request finishes first', async () => {
|
||||
const stale = deferredReply();
|
||||
ExperimentAssignments.start();
|
||||
ExperimentAssignments.stop();
|
||||
const replacement = deferredReply();
|
||||
ExperimentAssignments.start();
|
||||
const signal = vi.mocked(http.get).mock.calls[1]?.[1]?.signal;
|
||||
expect(signal).toBeDefined();
|
||||
|
||||
stale.resolve(reply(200, CANARY_ENVELOPE));
|
||||
await settle();
|
||||
await vi.advanceTimersByTimeAsync(31_000);
|
||||
setVisibility('hidden');
|
||||
setVisibility('visible');
|
||||
await settle();
|
||||
expect(http.get).toHaveBeenCalledTimes(2);
|
||||
expect(signal!.aborted).toBe(false);
|
||||
|
||||
ExperimentAssignments.stop();
|
||||
expect(signal!.aborted).toBe(true);
|
||||
replacement.resolve(reply(200, CANARY_ENVELOPE));
|
||||
await settle();
|
||||
expect(ExperimentAssignments.response).toBe(INERT_EXPERIMENT_ASSIGNMENTS_RESPONSE);
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it('discards a response that lands after reset', async () => {
|
||||
const deferred = deferredReply();
|
||||
ExperimentAssignments.start();
|
||||
|
||||
@@ -25,6 +25,7 @@ const MAX_BACKOFF_DOUBLINGS = 2;
|
||||
const EXPERIMENT_ASSIGNMENTS_MIN_POLL_DELAY_MS = (EXPERIMENT_MIN_POLL_INTERVAL_SECONDS * MS_PER_SECOND) / 2;
|
||||
|
||||
type UnobservedField =
|
||||
| 'abortController'
|
||||
| 'etag'
|
||||
| 'failureStreak'
|
||||
| 'generation'
|
||||
@@ -69,6 +70,7 @@ class ExperimentAssignmentsStore {
|
||||
private listening = false;
|
||||
private pollTimerId: NodeJS.Timeout | null = null;
|
||||
private inFlight: Promise<void> | null = null;
|
||||
private abortController: AbortController | null = null;
|
||||
private failureStreak = 0;
|
||||
private notFoundStreak = 0;
|
||||
private pollingDisabled = false;
|
||||
@@ -81,6 +83,7 @@ class ExperimentAssignmentsStore {
|
||||
this,
|
||||
{
|
||||
response: observable.ref,
|
||||
abortController: false,
|
||||
etag: false,
|
||||
failureStreak: false,
|
||||
generation: false,
|
||||
@@ -117,6 +120,10 @@ class ExperimentAssignmentsStore {
|
||||
stop(): void {
|
||||
this.started = false;
|
||||
this.generation += 1;
|
||||
const controller = this.abortController;
|
||||
this.abortController = null;
|
||||
this.inFlight = null;
|
||||
controller?.abort();
|
||||
try {
|
||||
this.removeVisibilityListener();
|
||||
} catch (err) {
|
||||
@@ -128,7 +135,6 @@ class ExperimentAssignmentsStore {
|
||||
reset(): void {
|
||||
this.stop();
|
||||
this.etag = null;
|
||||
this.inFlight = null;
|
||||
this.failureStreak = 0;
|
||||
this.notFoundStreak = 0;
|
||||
this.pollingDisabled = false;
|
||||
@@ -198,16 +204,19 @@ class ExperimentAssignmentsStore {
|
||||
if (existing !== null) {
|
||||
return existing;
|
||||
}
|
||||
const pending = this.fetchAssignments(this.generation).finally(() => {
|
||||
const controller = new AbortController();
|
||||
this.abortController = controller;
|
||||
const pending = this.fetchAssignments(this.generation, controller.signal).finally(() => {
|
||||
if (this.inFlight === pending) {
|
||||
this.inFlight = null;
|
||||
this.abortController = null;
|
||||
}
|
||||
});
|
||||
this.inFlight = pending;
|
||||
return pending;
|
||||
}
|
||||
|
||||
private async fetchAssignments(generation: number): Promise<void> {
|
||||
private async fetchAssignments(generation: number, signal: AbortSignal): Promise<void> {
|
||||
this.lastFetchStartedAt = Date.now();
|
||||
const headers: Record<string, string> = {};
|
||||
if (this.etag !== null) {
|
||||
@@ -218,6 +227,7 @@ class ExperimentAssignmentsStore {
|
||||
headers,
|
||||
mode: 'silent',
|
||||
parse: 'json',
|
||||
signal,
|
||||
});
|
||||
if (generation !== this.generation) {
|
||||
return;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface RoleReorderPoint {
|
||||
x: number;
|
||||
@@ -275,7 +275,7 @@ export type RoleReorderSnapshot = SnapshotFrom<typeof roleReorderStateMachine>;
|
||||
export type RoleReorderStateValue = 'idle' | 'resolving' | 'targeting' | 'blocked';
|
||||
|
||||
export function createRoleReorderSnapshot(): RoleReorderSnapshot {
|
||||
return getInitialSnapshot(roleReorderStateMachine);
|
||||
return initialTransition(roleReorderStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionRoleReorderSnapshot(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {MessageNotifications} from '@fluxer/constants/src/NotificationConstants';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface GuildReadStateContributionInput {
|
||||
isEligibleTextChannel: boolean;
|
||||
@@ -110,7 +110,7 @@ export type GuildReadStateContributionSnapshot = SnapshotFrom<typeof guildReadSt
|
||||
export function createGuildReadStateContributionSnapshot(
|
||||
input: GuildReadStateContributionInput,
|
||||
): GuildReadStateContributionSnapshot {
|
||||
return getInitialSnapshot(guildReadStateContributionMachine, input);
|
||||
return initialTransition(guildReadStateContributionMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionGuildReadStateContributionSnapshot(
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import {registerComposerEnter} from '@app/features/lexical/composer/ComposerEnter';
|
||||
import {$replaceComposerRange, $selectComposerRange} from '@app/features/lexical/composer/composerOffsets';
|
||||
import {ComposerMentionNode} from '@app/features/lexical/composer/nodes/ComposerMentionNode';
|
||||
import {ComposerPlainSegmentNode} from '@app/features/lexical/composer/nodes/ComposerPlainSegmentNode';
|
||||
import {$createSlashSlotNode, SlashSlotNode} from '@app/features/lexical/composer/nodes/SlashSlotNode';
|
||||
import {
|
||||
$createSlashSlotNode,
|
||||
$isSlashSlotNode,
|
||||
SlashSlotNode,
|
||||
} from '@app/features/lexical/composer/nodes/SlashSlotNode';
|
||||
import {SlashSlotPlaceholderNode} from '@app/features/lexical/composer/nodes/SlashSlotPlaceholderNode';
|
||||
import {$getRoot, $setSelection, createEditor, type ElementNode, KEY_ENTER_COMMAND, type LexicalEditor} from 'lexical';
|
||||
import {$getRoot, $isElementNode, $setSelection, createEditor, KEY_ENTER_COMMAND, type LexicalEditor} from 'lexical';
|
||||
import {describe, expect, it, vi} from 'vitest';
|
||||
|
||||
vi.mock('@app/features/lexical/composer/nodes/ComposerMentionPill', () => ({ComposerMentionPill: () => null}));
|
||||
@@ -45,16 +50,22 @@ function typedWithRequiredSlot(text: string, offset: number): LexicalEditor {
|
||||
return createComposer(() => {
|
||||
$replaceComposerRange(0, 0, {kind: 'text', text}, {leading: false, trailing: false});
|
||||
const slot = $createSlashSlotNode('value', 'string', true);
|
||||
$getRoot().getFirstChildOrThrow<ElementNode>().append(slot);
|
||||
const paragraph = $getRoot().getFirstChildOrThrow();
|
||||
assert($isElementNode(paragraph), 'Expected composer paragraph');
|
||||
paragraph.append(slot);
|
||||
slot.ensurePlaceholder();
|
||||
$selectComposerRange(offset, offset);
|
||||
});
|
||||
}
|
||||
|
||||
function slotValidity(editor: LexicalEditor): string {
|
||||
return editor.read(() =>
|
||||
$getRoot().getFirstChildOrThrow<ElementNode>().getLastChildOrThrow<SlashSlotNode>().getValidity(),
|
||||
);
|
||||
return editor.read(() => {
|
||||
const paragraph = $getRoot().getFirstChildOrThrow();
|
||||
assert($isElementNode(paragraph), 'Expected composer paragraph');
|
||||
const slot = paragraph.getLastChildOrThrow();
|
||||
assert($isSlashSlotNode(slot), 'Expected required slash command slot');
|
||||
return slot.getValidity();
|
||||
});
|
||||
}
|
||||
|
||||
function pressEnter(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import {registerComposerMarkdownHighlight} from '@app/features/lexical/composer/ComposerMarkdownHighlight';
|
||||
import {$hydrateComposerFromDraft, $projectComposer} from '@app/features/lexical/composer/ComposerSerialization';
|
||||
import {$getComposerSelectionRange, $selectComposerRange} from '@app/features/lexical/composer/composerOffsets';
|
||||
@@ -24,7 +25,6 @@ import {
|
||||
$isElementNode,
|
||||
$isTextNode,
|
||||
createEditor,
|
||||
type ElementNode,
|
||||
type LexicalEditor,
|
||||
type RangeSelection,
|
||||
} from 'lexical';
|
||||
@@ -282,7 +282,9 @@ describe('registerComposerMarkdownHighlight with silentMessagePrefix', () => {
|
||||
]);
|
||||
expect(readLines(editor)).toEqual([[['@silent', SILENT_STYLE], [' ', ''], 'composer-mention']]);
|
||||
const mention = editor.getEditorState().read(() => {
|
||||
const pill = $getRoot().getFirstChildOrThrow<ElementNode>().getLastChild();
|
||||
const paragraph = $getRoot().getFirstChildOrThrow();
|
||||
assert($isElementNode(paragraph), 'Expected composer paragraph');
|
||||
const pill = paragraph.getLastChild();
|
||||
return $isComposerMentionNode(pill) ? {literal: pill.isLiteral(), presentation: pill.getPresentation()} : null;
|
||||
});
|
||||
expect(mention).toEqual({literal: false, presentation: ComposerMentionPresentation.none});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
$getComposerClipboardSelection,
|
||||
$insertComposerClipboardSlice,
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
$createTextNode,
|
||||
$getRoot,
|
||||
$getSelection,
|
||||
$isElementNode,
|
||||
$isRangeSelection,
|
||||
$setCompositionKey,
|
||||
$setSelection,
|
||||
@@ -142,7 +144,9 @@ function describeNode(node: LexicalNode): string {
|
||||
}
|
||||
|
||||
function $paragraph(): ElementNode {
|
||||
return $getRoot().getFirstChildOrThrow<ElementNode>();
|
||||
const paragraph = $getRoot().getFirstChildOrThrow();
|
||||
assert($isElementNode(paragraph), 'Expected composer paragraph');
|
||||
return paragraph;
|
||||
}
|
||||
|
||||
function children(editor: LexicalEditor): Array<string> {
|
||||
@@ -517,9 +521,13 @@ describe('typed special mentions', () => {
|
||||
'text:@everyone ',
|
||||
'slash-slot:@here ',
|
||||
]);
|
||||
expect(editor.read(() => $paragraph().getLastChildOrThrow<ElementNode>().getChildren().map(describeNode))).toEqual([
|
||||
'text:@here ',
|
||||
]);
|
||||
expect(
|
||||
editor.read(() => {
|
||||
const slot = $paragraph().getLastChildOrThrow();
|
||||
assert($isElementNode(slot), 'Expected slash command slot element');
|
||||
return slot.getChildren().map(describeNode);
|
||||
}),
|
||||
).toEqual(['text:@here ']);
|
||||
});
|
||||
|
||||
it('restores a draft special mention and projects it back unchanged', () => {
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface ComposerTypeaheadPluginProps {
|
||||
listboxId: string;
|
||||
enabled: boolean;
|
||||
onSelect: (option: AutocompleteOption) => void;
|
||||
activeRef: React.MutableRefObject<boolean>;
|
||||
activeRef: React.RefObject<boolean>;
|
||||
}
|
||||
|
||||
export const ComposerTypeaheadPlugin = ({
|
||||
|
||||
@@ -250,7 +250,7 @@ function SelectionToolbarSurface({
|
||||
onFocusRequestHandled: () => void;
|
||||
onDismiss: () => void;
|
||||
toolbarRef: React.RefObject<HTMLElement | null>;
|
||||
savedSelectionRef: React.MutableRefObject<ComposerSelectionOffsets | null>;
|
||||
savedSelectionRef: React.RefObject<ComposerSelectionOffsets | null>;
|
||||
}) {
|
||||
const {i18n} = useLingui();
|
||||
const [focusIndex, setFocusIndex] = useState(0);
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface SlashSlotAutocompletePluginProps {
|
||||
listboxId: string;
|
||||
enabled: boolean;
|
||||
onSelect: (option: AutocompleteOption) => void;
|
||||
activeRef: React.MutableRefObject<boolean>;
|
||||
activeRef: React.RefObject<boolean>;
|
||||
}
|
||||
|
||||
export const SlashSlotAutocompletePlugin = ({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import {registerComposerMarkdownHighlight} from '@app/features/lexical/composer/ComposerMarkdownHighlight';
|
||||
import {type CodeBlockWrapPlan, planCodeBlockWrap} from '@app/features/lexical/composer/codeBlockWrap';
|
||||
import {
|
||||
@@ -25,8 +26,8 @@ import {createEmptyHistoryState, registerHistory} from '@lexical/history';
|
||||
import {
|
||||
$getNodeByKey,
|
||||
$getRoot,
|
||||
$isElementNode,
|
||||
createEditor,
|
||||
type ElementNode,
|
||||
type LexicalEditor,
|
||||
type NodeKey,
|
||||
REDO_COMMAND,
|
||||
@@ -307,13 +308,14 @@ function composerState(editor: LexicalEditor): {
|
||||
}
|
||||
|
||||
function atomicKeys(editor: LexicalEditor): Array<NodeKey> {
|
||||
return editor.read(() =>
|
||||
$getRoot()
|
||||
.getFirstChildOrThrow<ElementNode>()
|
||||
return editor.read(() => {
|
||||
const paragraph = $getRoot().getFirstChildOrThrow();
|
||||
assert($isElementNode(paragraph), 'Expected composer paragraph');
|
||||
return paragraph
|
||||
.getChildren()
|
||||
.filter((node) => $isComposerMentionNode(node) || $isComposerCustomEmojiNode(node))
|
||||
.map((node) => node.getKey()),
|
||||
);
|
||||
.map((node) => node.getKey());
|
||||
});
|
||||
}
|
||||
|
||||
function atomicState(editor: LexicalEditor, keys: Array<NodeKey>): Array<{attached: boolean; literal: boolean} | null> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {normalizeMemberListRanges} from '@app/features/member/utils/MemberListRangeUtils';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface MemberListViewportMachineInput {
|
||||
hasReceivedInitialPayload?: boolean;
|
||||
@@ -127,7 +127,7 @@ export const memberListViewportStateMachine = setup({
|
||||
export function createMemberListViewportSnapshot(
|
||||
input: MemberListViewportMachineInput = {},
|
||||
): MemberListViewportMachineSnapshot {
|
||||
return getInitialSnapshot(memberListViewportStateMachine, input);
|
||||
return initialTransition(memberListViewportStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMemberListViewportSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type MessageFetchCacheHit = 'jump' | 'before' | 'after';
|
||||
|
||||
@@ -157,7 +157,7 @@ export type MessageFetchPreflightSnapshot = SnapshotFrom<typeof messageFetchPref
|
||||
export type MessageFetchExecutionSnapshot = SnapshotFrom<typeof messageFetchExecutionMachine>;
|
||||
|
||||
export function createMessageFetchPreflightSnapshot(input: MessageFetchPreflightInput): MessageFetchPreflightSnapshot {
|
||||
return getInitialSnapshot(messageFetchPreflightMachine, input);
|
||||
return initialTransition(messageFetchPreflightMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessageFetchPreflightSnapshot(
|
||||
@@ -178,7 +178,7 @@ export function resolveMessageFetchPreflightDecision(input: MessageFetchPrefligh
|
||||
}
|
||||
|
||||
export function createMessageFetchExecutionSnapshot(input: MessageFetchExecutionInput): MessageFetchExecutionSnapshot {
|
||||
return getInitialSnapshot(messageFetchExecutionMachine, input);
|
||||
return initialTransition(messageFetchExecutionMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessageFetchExecutionSnapshot(
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
calculateAroundPaginationState,
|
||||
} from '@app/features/messaging/utils/MessagePaginationUtils';
|
||||
import {compare as compareSnowflakes} from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface MessagePageStateInput {
|
||||
before: string | null;
|
||||
@@ -158,7 +158,7 @@ export const messagePageStateMachine = setup({
|
||||
export type MessagePageStateSnapshot = SnapshotFrom<typeof messagePageStateMachine>;
|
||||
|
||||
export function createMessagePageStateSnapshot(input: MessagePageStateInput): MessagePageStateSnapshot {
|
||||
return getInitialSnapshot(messagePageStateMachine, input);
|
||||
return initialTransition(messagePageStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessagePageStateSnapshot(
|
||||
|
||||
@@ -184,7 +184,7 @@ export const useMarkdownFormattingShortcut = ({
|
||||
value: string;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
handleTextChange: (newValue: string, oldValue: string) => void;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
}): ((event: KeyboardEvent<HTMLTextAreaElement>) => void) => {
|
||||
return useCallback(
|
||||
(event: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
|
||||
@@ -103,8 +103,8 @@ interface UseTextareaAutocompleteParams {
|
||||
value: string;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
segmentManagerRef: React.MutableRefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
segmentManagerRef: React.RefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
prepareTextChange: PrepareTextareaTextChange;
|
||||
allowedTriggers?: Array<TriggerType>;
|
||||
maxActualLength?: number;
|
||||
|
||||
@@ -12,8 +12,8 @@ interface UseTextareaDraftAndTypingOptions {
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
draft: string | null;
|
||||
draftSegments?: ReadonlyArray<MentionSegment> | null;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
segmentManagerRef?: React.MutableRefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
segmentManagerRef?: React.RefObject<TextareaSegmentManager>;
|
||||
isAutocompleteAttached: boolean;
|
||||
enabled: boolean;
|
||||
typingEnabled?: boolean;
|
||||
|
||||
@@ -13,7 +13,7 @@ interface UseTextareaEditingOptions {
|
||||
value: string;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
}
|
||||
|
||||
export const useTextareaEditing = ({
|
||||
|
||||
@@ -18,8 +18,8 @@ interface UseTextareaEmojiPickerReturn {
|
||||
interface UseTextareaEmojiPickerParams {
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
segmentManagerRef: React.MutableRefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
segmentManagerRef: React.RefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
prepareTextChange: PrepareTextareaTextChange;
|
||||
channelId?: string;
|
||||
maxActualLength?: number;
|
||||
|
||||
@@ -31,9 +31,9 @@ interface UseTextareaExpressionHandlersOptions {
|
||||
newText: string;
|
||||
newSegments: Array<MentionSegment>;
|
||||
};
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
prepareTextChange: PrepareTextareaTextChange;
|
||||
segmentManagerRef: React.MutableRefObject<TextareaSegmentManager>;
|
||||
segmentManagerRef: React.RefObject<TextareaSegmentManager>;
|
||||
sendOptimisticMessage: (
|
||||
messageData: {
|
||||
content: string;
|
||||
|
||||
@@ -21,7 +21,7 @@ interface UseTextareaKeyboardOptions {
|
||||
value: string;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
handleTextChange: (newValue: string, previousValue: string) => void;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
clearSegments: () => void;
|
||||
replyingMessage: {
|
||||
messageId: string;
|
||||
|
||||
@@ -21,9 +21,9 @@ import {useCallback, useEffect} from 'react';
|
||||
interface UseTextareaPasteParams {
|
||||
channel?: Channel | null;
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
segmentManagerRef: React.MutableRefObject<TextareaSegmentManager>;
|
||||
segmentManagerRef: React.RefObject<TextareaSegmentManager>;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
prepareTextChange: PrepareTextareaTextChange;
|
||||
maxMessageLength?: number;
|
||||
onPasteExceedsLimit?: (pastedText: string) => void;
|
||||
|
||||
@@ -5,8 +5,8 @@ import {TextareaSegmentManager} from '@app/features/messaging/utils/TextareaSegm
|
||||
import {useCallback, useRef} from 'react';
|
||||
|
||||
interface UseTextareaSegmentsReturn {
|
||||
segmentManagerRef: React.MutableRefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
segmentManagerRef: React.RefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
displayToActual: (displayText: string) => string;
|
||||
rememberSegmentsForValue: (value: string, segments?: ReadonlyArray<MentionSegment>) => void;
|
||||
prepareTextChange: (nextValue: string, nextSegments: ReadonlyArray<MentionSegment>) => void;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface ChannelIncomingMessageInput {
|
||||
hasNonceMatch: boolean;
|
||||
@@ -111,7 +111,7 @@ export type ChannelIncomingMessageSnapshot = SnapshotFrom<typeof channelIncoming
|
||||
export function createChannelIncomingMessageSnapshot(
|
||||
input: ChannelIncomingMessageInput,
|
||||
): ChannelIncomingMessageSnapshot {
|
||||
return getInitialSnapshot(channelIncomingMessageMachine, input);
|
||||
return initialTransition(channelIncomingMessageMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionChannelIncomingMessageSnapshot(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {compare as compareSnowflakes} from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface ChannelMessagesLoadInput {
|
||||
isBefore: boolean;
|
||||
@@ -124,7 +124,7 @@ export const channelMessagesLoadMachine = setup({
|
||||
export type ChannelMessagesLoadSnapshot = SnapshotFrom<typeof channelMessagesLoadMachine>;
|
||||
|
||||
export function createChannelMessagesLoadSnapshot(input: ChannelMessagesLoadInput): ChannelMessagesLoadSnapshot {
|
||||
return getInitialSnapshot(channelMessagesLoadMachine, input);
|
||||
return initialTransition(channelMessagesLoadMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionChannelMessagesLoadSnapshot(
|
||||
@@ -247,7 +247,7 @@ export const channelMessagesWindowMachine = setup({
|
||||
export type ChannelMessagesWindowSnapshot = SnapshotFrom<typeof channelMessagesWindowMachine>;
|
||||
|
||||
export function createChannelMessagesWindowSnapshot(input: ChannelMessagesWindowInput): ChannelMessagesWindowSnapshot {
|
||||
return getInitialSnapshot(channelMessagesWindowMachine, input);
|
||||
return initialTransition(channelMessagesWindowMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionChannelMessagesWindowSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type MentionType = '@everyone' | '@here' | 'role';
|
||||
|
||||
@@ -121,7 +121,7 @@ export const mentionConfirmationStateMachine = setup({
|
||||
});
|
||||
|
||||
export function createMentionConfirmationSnapshot(): MentionConfirmationSnapshot {
|
||||
return getInitialSnapshot(mentionConfirmationStateMachine);
|
||||
return initialTransition(mentionConfirmationStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionMentionConfirmationSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface MessageQueuePayloadRouteInput {
|
||||
payloadType?: string;
|
||||
@@ -275,7 +275,7 @@ export type MessageLocalSendRateLimitSnapshot = SnapshotFrom<typeof messageLocal
|
||||
export function createMessageQueuePayloadRouteSnapshot(
|
||||
input: MessageQueuePayloadRouteInput,
|
||||
): MessageQueuePayloadRouteSnapshot {
|
||||
return getInitialSnapshot(messageQueuePayloadRouteMachine, input);
|
||||
return initialTransition(messageQueuePayloadRouteMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessageQueuePayloadRouteSnapshot(
|
||||
@@ -307,7 +307,7 @@ export function resolveMessageQueuePayloadRouteDecision(
|
||||
export function createMessageQueueSendExecutionSnapshot(
|
||||
input: MessageQueueSendExecutionInput,
|
||||
): MessageQueueSendExecutionSnapshot {
|
||||
return getInitialSnapshot(messageQueueSendExecutionMachine, input);
|
||||
return initialTransition(messageQueueSendExecutionMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessageQueueSendExecutionSnapshot(
|
||||
@@ -337,7 +337,7 @@ export function resolveMessageQueueSendExecutionDecision(
|
||||
export function createMessageQueueRequestOutcomeSnapshot(
|
||||
input: MessageQueueRequestOutcomeInput,
|
||||
): MessageQueueRequestOutcomeSnapshot {
|
||||
return getInitialSnapshot(messageQueueRequestOutcomeMachine, input);
|
||||
return initialTransition(messageQueueRequestOutcomeMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessageQueueRequestOutcomeSnapshot(
|
||||
@@ -369,7 +369,7 @@ export function resolveMessageQueueRequestOutcomeDecision(
|
||||
export function createMessageLocalSendRateLimitSnapshot(
|
||||
input: MessageLocalSendRateLimitInput,
|
||||
): MessageLocalSendRateLimitSnapshot {
|
||||
return getInitialSnapshot(messageLocalSendRateLimitMachine, input);
|
||||
return initialTransition(messageLocalSendRateLimitMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionMessageLocalSendRateLimitSnapshot(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type {ReactionEmoji} from '@app/features/messaging/utils/ReactionUtils';
|
||||
import type {MessageReaction} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface ReactionRecord {
|
||||
emoji: ReactionEmoji;
|
||||
@@ -366,7 +366,7 @@ export function createReactionMachineSnapshot(
|
||||
map: ReactionMap = emptyMap(),
|
||||
currentUserId?: string | null,
|
||||
): ReactionMachineSnapshot {
|
||||
return getInitialSnapshot(reactionStateMachine, {map, currentUserId});
|
||||
return initialTransition(reactionStateMachine, {map, currentUserId})[0];
|
||||
}
|
||||
|
||||
export function transitionReactionSnapshot(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import {User} from '@app/features/user/models/User';
|
||||
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type FetchStatus = 'idle' | 'pending' | 'success' | 'error';
|
||||
|
||||
@@ -336,7 +336,7 @@ export const reactionUsersStateMachine = setup({
|
||||
export type ReactionUsersMachineSnapshot = SnapshotFrom<typeof reactionUsersStateMachine>;
|
||||
|
||||
export function createReactionUsersSnapshot(requestSerial = 0): ReactionUsersMachineSnapshot {
|
||||
return getInitialSnapshot(reactionUsersStateMachine, {requestSerial});
|
||||
return initialTransition(reactionUsersStateMachine, {requestSerial})[0];
|
||||
}
|
||||
|
||||
export function transitionReactionUsersSnapshot(
|
||||
|
||||
@@ -9,8 +9,8 @@ export type PrepareTextareaTextChange = (nextValue: string, nextSegments: Readon
|
||||
export interface ApplyTextareaTextChangeOptions {
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement | null>;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
segmentManagerRef: React.MutableRefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.MutableRefObject<string>;
|
||||
segmentManagerRef: React.RefObject<TextareaSegmentManager>;
|
||||
previousValueRef: React.RefObject<string>;
|
||||
prepareTextChange: PrepareTextareaTextChange;
|
||||
nextValue: string;
|
||||
nextSegments?: ReadonlyArray<MentionSegment>;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
INITIAL_FORM_VALUES,
|
||||
type State,
|
||||
} from '@app/features/moderation/components/report/ReportTypes';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
type ReportMachineContext = Omit<State, 'flowStep'>;
|
||||
|
||||
@@ -162,7 +162,7 @@ export const reportStateMachine = setup({
|
||||
export type ReportMachineSnapshot = SnapshotFrom<typeof reportStateMachine>;
|
||||
|
||||
export function createReportSnapshot(): ReportMachineSnapshot {
|
||||
return getInitialSnapshot(reportStateMachine);
|
||||
return initialTransition(reportStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionReportSnapshot(snapshot: ReportMachineSnapshot, event: Action): ReportMachineSnapshot {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface AutoAckWindowConditions {
|
||||
channelId: string | null;
|
||||
@@ -111,7 +111,7 @@ export const autoAckWindowStateMachine = setup({
|
||||
export type AutoAckWindowSnapshot = SnapshotFrom<typeof autoAckWindowStateMachine>;
|
||||
|
||||
export function createAutoAckWindowSnapshot(input: AutoAckWindowInput = {}): AutoAckWindowSnapshot {
|
||||
return getInitialSnapshot(autoAckWindowStateMachine, input);
|
||||
return initialTransition(autoAckWindowStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionAutoAckWindowSnapshot(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type {RuntimeConfigSnapshot} from '@app/features/app/state/RuntimeConfig';
|
||||
import type {AccountPresenceIntent, UserData} from '@app/features/auth/state/AccountStorage';
|
||||
import type {ValueOf} from '@fluxer/constants/src/ValueOf';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export const SessionState = {
|
||||
Idle: 'idle',
|
||||
@@ -285,7 +285,7 @@ export const authSessionStateMachine = setup({
|
||||
export type AuthSessionSnapshot = SnapshotFrom<typeof authSessionStateMachine>;
|
||||
|
||||
export function createAuthSessionSnapshot(): AuthSessionSnapshot {
|
||||
return getInitialSnapshot(authSessionStateMachine);
|
||||
return initialTransition(authSessionStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionAuthSessionSnapshot(
|
||||
|
||||
@@ -123,7 +123,7 @@ export const TextareaAutosize = React.forwardRef<HTMLTextAreaElement, TextareaAu
|
||||
(node: HTMLTextAreaElement | null) => {
|
||||
elRef.current = node;
|
||||
if (typeof forwardedRef === 'function') forwardedRef(node);
|
||||
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
||||
else if (forwardedRef) forwardedRef.current = node;
|
||||
},
|
||||
[forwardedRef],
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {compareMessageIds} from './shared';
|
||||
|
||||
export interface ReadStateAckInput {
|
||||
@@ -128,7 +128,7 @@ export const readStateAckMachine = setup({
|
||||
export type ReadStateAckSnapshot = SnapshotFrom<typeof readStateAckMachine>;
|
||||
|
||||
export function createReadStateAckSnapshot(input: ReadStateAckInput): ReadStateAckSnapshot {
|
||||
return getInitialSnapshot(readStateAckMachine, input);
|
||||
return initialTransition(readStateAckMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionReadStateAckSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {compareMessageIds} from './shared';
|
||||
|
||||
export interface ReadStateEntryStatusInput {
|
||||
@@ -127,7 +127,7 @@ export const readStateEntryStatusMachine = setup({
|
||||
export type ReadStateEntryStatusSnapshot = SnapshotFrom<typeof readStateEntryStatusMachine>;
|
||||
|
||||
export function createReadStateEntryStatusSnapshot(input: ReadStateEntryStatusInput): ReadStateEntryStatusSnapshot {
|
||||
return getInitialSnapshot(readStateEntryStatusMachine, input);
|
||||
return initialTransition(readStateEntryStatusMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionReadStateEntryStatusSnapshot(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {compareMessageIds} from './shared';
|
||||
|
||||
export interface ReadStateIncomingMessageInput {
|
||||
@@ -136,7 +136,7 @@ export type ReadStateIncomingMessageSnapshot = SnapshotFrom<typeof readStateInco
|
||||
export function createReadStateIncomingMessageSnapshot(
|
||||
input: ReadStateIncomingMessageInput,
|
||||
): ReadStateIncomingMessageSnapshot {
|
||||
return getInitialSnapshot(readStateIncomingMessageMachine, input);
|
||||
return initialTransition(readStateIncomingMessageMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionReadStateIncomingMessageSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface ReadStateMentionInput {
|
||||
authorBlocked: boolean;
|
||||
@@ -98,7 +98,7 @@ export const readStateMentionMachine = setup({
|
||||
export type ReadStateMentionSnapshot = SnapshotFrom<typeof readStateMentionMachine>;
|
||||
|
||||
export function createReadStateMentionSnapshot(input: ReadStateMentionInput): ReadStateMentionSnapshot {
|
||||
return getInitialSnapshot(readStateMentionMachine, input);
|
||||
return initialTransition(readStateMentionMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionReadStateMentionSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {compareMessageIds, compareReadStateVersions} from './shared';
|
||||
|
||||
export interface ReadStateServerAckInput {
|
||||
@@ -107,7 +107,7 @@ export const readStateServerAckMachine = setup({
|
||||
export type ReadStateServerAckSnapshot = SnapshotFrom<typeof readStateServerAckMachine>;
|
||||
|
||||
export function createReadStateServerAckSnapshot(input: ReadStateServerAckInput): ReadStateServerAckSnapshot {
|
||||
return getInitialSnapshot(readStateServerAckMachine, input);
|
||||
return initialTransition(readStateServerAckMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionReadStateServerAckSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export const LOCAL_TYPING_REMOTE_SEND_DELAY_MS = 1500;
|
||||
export const LOCAL_TYPING_REMOTE_REFRESH_MS = 8000;
|
||||
@@ -198,7 +198,7 @@ export const localTypingStateMachine = setup({
|
||||
});
|
||||
|
||||
export function createLocalTypingSnapshot(input: LocalTypingMachineInput = {}): LocalTypingSnapshot {
|
||||
return getInitialSnapshot(localTypingStateMachine, input);
|
||||
return initialTransition(localTypingStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionLocalTypingSnapshot(
|
||||
|
||||
@@ -82,7 +82,7 @@ export const Checkbox: React.FC<CheckboxProps> = observer(
|
||||
'aria-describedby': ariaDescribedBy,
|
||||
'aria-hidden': ariaHidden,
|
||||
}) => {
|
||||
const rootRef = useRef<React.ElementRef<typeof CheckboxPrimitive.Root>>(null);
|
||||
const rootRef = useRef<React.ComponentRef<typeof CheckboxPrimitive.Root>>(null);
|
||||
const checkboxRef = useRef<HTMLLabelElement>(null);
|
||||
const labelRef = useRef<HTMLDivElement>(null);
|
||||
const actualSize = size === 'small' ? 18 : size;
|
||||
|
||||
@@ -113,7 +113,7 @@ export const InlineEdit: React.FC<InlineEditProps> = observer((props) => {
|
||||
setMode('editing');
|
||||
}
|
||||
};
|
||||
const handleEditableInput: React.FormEventHandler<HTMLDivElement> = (e) => {
|
||||
const handleEditableInput: React.InputEventHandler<HTMLDivElement> = (e) => {
|
||||
const el = e.currentTarget;
|
||||
const raw = el.textContent ?? '';
|
||||
let next = sanitizeDraft(raw);
|
||||
|
||||
@@ -73,7 +73,7 @@ const assignRef = <T,>(ref: React.Ref<T> | undefined, value: T | null): void =>
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref && typeof ref === 'object') {
|
||||
(ref as React.MutableRefObject<T | null>).current = value;
|
||||
ref.current = value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export const Switch = observer(
|
||||
);
|
||||
const resolvedLabelledBy = hasLabel ? labelId : ariaLabelledBy;
|
||||
const resolvedDescribedBy = [hasDescription ? descriptionId : null, ariaDescribedBy].filter(Boolean).join(' ');
|
||||
const rootRef = useRef<React.ElementRef<typeof SwitchPrimitive.Root>>(null);
|
||||
const rootRef = useRef<React.ComponentRef<typeof SwitchPrimitive.Root>>(null);
|
||||
const valueChange = useCallback(
|
||||
(next: boolean) => {
|
||||
if (disabled) return;
|
||||
|
||||
@@ -67,7 +67,7 @@ function keepCurrentArrayIfEqual<T>(currentItems: ReadonlyArray<T>, nextItems: R
|
||||
interface ResolveHoverDeferredItemsRequest<T> {
|
||||
readonly currentItems: ReadonlyArray<T>;
|
||||
readonly getKey: (item: T) => string;
|
||||
readonly hasDeferredOrderRef: React.MutableRefObject<boolean>;
|
||||
readonly hasDeferredOrderRef: React.RefObject<boolean>;
|
||||
readonly isHoveringDynamicItem: boolean;
|
||||
readonly items: ReadonlyArray<T>;
|
||||
readonly sourceChanged: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type SteppedCarouselHeight = number | 'auto';
|
||||
|
||||
@@ -136,7 +136,7 @@ export type SteppedCarouselSnapshot = SnapshotFrom<typeof steppedCarouselStateMa
|
||||
export type SteppedCarouselStateValue = 'unmeasured' | 'ready';
|
||||
|
||||
export function createSteppedCarouselSnapshot(input: SteppedCarouselMachineInput): SteppedCarouselSnapshot {
|
||||
return getInitialSnapshot(steppedCarouselStateMachine, input);
|
||||
return initialTransition(steppedCarouselStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionSteppedCarouselSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface TooltipEnvironment {
|
||||
disabled: boolean;
|
||||
@@ -243,7 +243,7 @@ export const tooltipStateMachine = setup({
|
||||
export type TooltipSnapshot = SnapshotFrom<typeof tooltipStateMachine>;
|
||||
|
||||
export function createTooltipSnapshot(input: TooltipMachineInput = {}): TooltipSnapshot {
|
||||
return getInitialSnapshot(tooltipStateMachine, input);
|
||||
return initialTransition(tooltipStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionTooltipSnapshot(snapshot: TooltipSnapshot, event: TooltipMachineEvent): TooltipSnapshot {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {UpdaterDownloadOption} from '@app/features/platform/types/Electron';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type UpdaterState = 'idle' | 'checking' | 'available';
|
||||
export type UpdateType = 'native' | 'web' | 'both' | null;
|
||||
@@ -389,7 +389,7 @@ export const updaterStateMachine = setup({
|
||||
export type UpdaterMachineSnapshot = SnapshotFrom<typeof updaterStateMachine>;
|
||||
|
||||
export function createUpdaterMachineSnapshot(): UpdaterMachineSnapshot {
|
||||
return getInitialSnapshot(updaterStateMachine);
|
||||
return initialTransition(updaterStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionUpdaterMachineSnapshot(
|
||||
|
||||
@@ -207,7 +207,7 @@ const EmbedDebuggerTab: React.FC = observer(() => {
|
||||
() => (previewEmbeds ? createPreviewMessage(submittedUrl, previewEmbeds) : null),
|
||||
[previewEmbeds, submittedUrl],
|
||||
);
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleSubmit = async (event: React.SubmitEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
if (!trimmedUrl || isSubmitting) return;
|
||||
setIsSubmitting(true);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {TransientUploadFieldValue} from '@app/lib/forms/TransientUploadFields';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type ProfileAssetMode = 'inherit' | 'custom' | 'unset';
|
||||
|
||||
@@ -244,7 +244,7 @@ export const profileAssetCustomizationStateMachine = setup({
|
||||
export type ProfileAssetCustomizationSnapshot = SnapshotFrom<typeof profileAssetCustomizationStateMachine>;
|
||||
|
||||
export function createProfileAssetCustomizationSnapshot(): ProfileAssetCustomizationSnapshot {
|
||||
return getInitialSnapshot(profileAssetCustomizationStateMachine);
|
||||
return initialTransition(profileAssetCustomizationStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionProfileAssetCustomizationSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type SyncedFieldFailureReason =
|
||||
| 'build-local'
|
||||
@@ -242,7 +242,7 @@ export const syncedFieldStateMachine = setup({
|
||||
});
|
||||
|
||||
export function createSyncedFieldMachineSnapshot(): SyncedFieldMachineSnapshot {
|
||||
return getInitialSnapshot(syncedFieldStateMachine);
|
||||
return initialTransition(syncedFieldStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionSyncedFieldMachineSnapshot(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {areOrderedStringArraysEqual} from '@app/features/voice/utils/StringArrayUtils';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface IncomingCallManagerSignals {
|
||||
incomingCallIds: Array<string>;
|
||||
@@ -85,7 +85,7 @@ export const incomingCallManagerStateMachine = setup({
|
||||
export type IncomingCallManagerSnapshot = SnapshotFrom<typeof incomingCallManagerStateMachine>;
|
||||
|
||||
export function createIncomingCallManagerSnapshot(): IncomingCallManagerSnapshot {
|
||||
return getInitialSnapshot(incomingCallManagerStateMachine);
|
||||
return initialTransition(incomingCallManagerStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionIncomingCallManagerSnapshot(
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
type WindowShareAudioScope,
|
||||
} from '@app/features/voice/utils/StreamSettingsUpdatePolicy';
|
||||
import type {NativeAudioAvailability} from '@app/types/electron.d';
|
||||
import {getInitialSnapshot, setup, transition} from 'xstate';
|
||||
import {initialTransition, setup, transition} from 'xstate';
|
||||
|
||||
export type StreamSettingsAudioControlStateValue =
|
||||
| 'hidden'
|
||||
@@ -146,7 +146,7 @@ export function selectStreamSettingsAudioControlState(
|
||||
): StreamSettingsAudioControlStateValue {
|
||||
const [snapshot] = transition(
|
||||
streamSettingsAudioControlStateMachine,
|
||||
getInitialSnapshot(streamSettingsAudioControlStateMachine),
|
||||
initialTransition(streamSettingsAudioControlStateMachine)[0],
|
||||
{
|
||||
type: 'audio.evaluate',
|
||||
signals,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type VoiceCallLayoutPresentationMode = 'grid' | 'focus';
|
||||
|
||||
@@ -185,7 +185,7 @@ export type VoiceCallLayoutPresentationMachineSnapshot = SnapshotFrom<typeof voi
|
||||
export function createVoiceCallLayoutPresentationSnapshot(
|
||||
input: VoiceCallLayoutPresentationInput,
|
||||
): VoiceCallLayoutPresentationMachineSnapshot {
|
||||
return getInitialSnapshot(voiceCallLayoutPresentationStateMachine, input);
|
||||
return initialTransition(voiceCallLayoutPresentationStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionVoiceCallLayoutPresentationSnapshot(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, setup, transition} from 'xstate';
|
||||
|
||||
export type VoiceControlBarMuteLabel =
|
||||
| 'moderatorDeafened'
|
||||
@@ -152,7 +152,7 @@ export const voiceControlBarStateMachine = setup({
|
||||
});
|
||||
|
||||
export function transitionVoiceControlBarState(signals: VoiceControlBarSignals): VoiceControlBarState {
|
||||
const [snapshot] = transition(voiceControlBarStateMachine, getInitialSnapshot(voiceControlBarStateMachine), {
|
||||
const [snapshot] = transition(voiceControlBarStateMachine, initialTransition(voiceControlBarStateMachine)[0], {
|
||||
type: 'controls.evaluate',
|
||||
signals,
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type {VoiceParticipantTilePresentation} from '@app/features/voice/components/voice_participant_tile/shared';
|
||||
import type {VoiceMediaGraphStreamTileState} from '@app/features/voice/engine/VoiceMediaGraphTileState';
|
||||
import {assign, getInitialSnapshot, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, setup, transition} from 'xstate';
|
||||
|
||||
export type VoiceParticipantTileScreenShareStateValue =
|
||||
| 'idle'
|
||||
@@ -221,7 +221,7 @@ export function selectVoiceParticipantTileScreenShareState(
|
||||
): VoiceParticipantTileScreenShareStateValue {
|
||||
const [snapshot] = transition(
|
||||
voiceParticipantTileStateMachine,
|
||||
getInitialSnapshot(voiceParticipantTileStateMachine),
|
||||
initialTransition(voiceParticipantTileStateMachine)[0],
|
||||
{
|
||||
type: 'tile.evaluateScreenShare',
|
||||
signals,
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface CompactVoiceCallPresentationInput {
|
||||
readonly audioOnly?: boolean;
|
||||
@@ -113,7 +113,7 @@ export type CompactVoiceCallPresentationMachineSnapshot = SnapshotFrom<typeof co
|
||||
export function createCompactVoiceCallPresentationSnapshot(
|
||||
input: CompactVoiceCallPresentationInput,
|
||||
): CompactVoiceCallPresentationMachineSnapshot {
|
||||
return getInitialSnapshot(compactVoiceCallPresentationStateMachine, input);
|
||||
return initialTransition(compactVoiceCallPresentationStateMachine, input)[0];
|
||||
}
|
||||
|
||||
export function transitionCompactVoiceCallPresentationSnapshot(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export interface MediaControlsVisibilitySignals {
|
||||
disabled: boolean;
|
||||
@@ -58,7 +58,7 @@ export const mediaControlsVisibilityStateMachine = setup({
|
||||
export type MediaControlsVisibilitySnapshot = SnapshotFrom<typeof mediaControlsVisibilityStateMachine>;
|
||||
|
||||
export function createMediaControlsVisibilitySnapshot(): MediaControlsVisibilitySnapshot {
|
||||
return getInitialSnapshot(mediaControlsVisibilityStateMachine);
|
||||
return initialTransition(mediaControlsVisibilityStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionMediaControlsVisibilitySnapshot(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
export type VideoPlayerRenderState = 'poster' | 'playing' | 'paused' | 'ended' | 'error';
|
||||
export type VideoPlayerPlayPauseIndicator = 'play' | 'pause';
|
||||
@@ -127,7 +127,7 @@ export const videoPlayerRenderStateMachine = setup({
|
||||
export type VideoPlayerRenderSnapshot = SnapshotFrom<typeof videoPlayerRenderStateMachine>;
|
||||
|
||||
export function createVideoPlayerRenderSnapshot(): VideoPlayerRenderSnapshot {
|
||||
return getInitialSnapshot(videoPlayerRenderStateMachine);
|
||||
return initialTransition(videoPlayerRenderStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionVideoPlayerRenderSnapshot(
|
||||
|
||||
@@ -256,8 +256,8 @@ interface CameraPreviewTrackSetupArgs {
|
||||
cameraResolution: 'low' | 'medium' | 'high';
|
||||
videoFrameRate: number;
|
||||
isCurrentInitialization: () => boolean;
|
||||
trackRef: React.MutableRefObject<LocalVideoTrack | null>;
|
||||
processorRef: React.MutableRefObject<CameraPreviewProcessor | null>;
|
||||
trackRef: React.RefObject<LocalVideoTrack | null>;
|
||||
processorRef: React.RefObject<CameraPreviewProcessor | null>;
|
||||
onResolutionNegotiated: (resolution: {width: number; height: number} | null) => void;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {NativePermissionResult} from '@app/features/permissions/system/utils/NativePermissions';
|
||||
import {assign, getInitialSnapshot, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
import {assign, initialTransition, type SnapshotFrom, setup, transition} from 'xstate';
|
||||
|
||||
interface ScreenSharePickerDisplayPermissionContext {
|
||||
settingsOpened: boolean;
|
||||
@@ -107,7 +107,7 @@ export type ScreenSharePickerDisplayPermissionSnapshot = SnapshotFrom<
|
||||
>;
|
||||
|
||||
export function createScreenSharePickerDisplayPermissionSnapshot(): ScreenSharePickerDisplayPermissionSnapshot {
|
||||
return getInitialSnapshot(screenSharePickerDisplayPermissionStateMachine);
|
||||
return initialTransition(screenSharePickerDisplayPermissionStateMachine)[0];
|
||||
}
|
||||
|
||||
export function transitionScreenSharePickerDisplayPermissionSnapshot(
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {getInitialSnapshot, setup, transition} from 'xstate';
|
||||
import {initialTransition, setup, transition} from 'xstate';
|
||||
|
||||
export type LocalParticipantMediaControl = 'camera' | 'screenShare';
|
||||
export type LocalParticipantMediaControlStateValue =
|
||||
@@ -120,7 +120,7 @@ export function selectLocalParticipantMediaControlState(
|
||||
): LocalParticipantMediaControlStateValue {
|
||||
const [snapshot] = transition(
|
||||
localParticipantMediaControlStateMachine,
|
||||
getInitialSnapshot(localParticipantMediaControlStateMachine),
|
||||
initialTransition(localParticipantMediaControlStateMachine)[0],
|
||||
{
|
||||
type: 'controls.evaluate',
|
||||
control,
|
||||
|
||||
@@ -174,7 +174,7 @@ export function useTileContextMenuActive(tileElRef: React.RefObject<HTMLElement
|
||||
}
|
||||
|
||||
function unsubscribeManagedVideoPublication(
|
||||
managedPublicationRef: React.MutableRefObject<RemoteTrackPublication | null>,
|
||||
managedPublicationRef: React.RefObject<RemoteTrackPublication | null>,
|
||||
publication: RemoteTrackPublication | null,
|
||||
): void {
|
||||
if (!publication) return;
|
||||
|
||||
Loaded 100 of 510 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user