work on turning pl-hooks into a separate library
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
dc5fa13e64
commit
8a047d7c3a
78 changed files with 340 additions and 318 deletions
|
@ -96,7 +96,7 @@ const baseAccountSchema = z.object({
|
|||
acct: z.string().catch(''),
|
||||
url: z.string().url(),
|
||||
display_name: z.string().catch(''),
|
||||
note: z.string().catch(''),
|
||||
note: z.string().transform(note => note === '<p></p>' ? '' : note).catch(''),
|
||||
avatar: z.string().catch(''),
|
||||
avatar_static: z.string().url().catch(''),
|
||||
header: z.string().url().catch(''),
|
||||
|
|
|
@ -41,7 +41,7 @@ const baseStatusSchema = z.object({
|
|||
uri: z.string().url().catch(''),
|
||||
created_at: dateSchema,
|
||||
account: accountSchema,
|
||||
content: z.string().catch(''),
|
||||
content: z.string().transform(note => note === '<p></p>' ? '' : note).catch(''),
|
||||
visibility: z.string().catch('public'),
|
||||
sensitive: z.coerce.boolean(),
|
||||
spoiler_text: z.string().catch(''),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { PLEROMA, type UpdateNotificationSettingsParams, type Account, type CreateAccountParams, type PaginatedResponse, type Relationship } from 'pl-api';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient, type PlfeResponse } from 'pl-fe/api';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { selectAccount } from 'pl-fe/selectors';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
import type { MinifiedStatus } from 'pl-fe/reducers/statuses';
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { fetchRelationships } from 'pl-fe/actions/accounts';
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { filterBadges, getTagDiff } from 'pl-fe/utils/badges';
|
||||
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import toast from 'pl-fe/toast';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* @see module:pl-fe/actions/security
|
||||
*/
|
||||
import { credentialAccountSchema, PlApiClient, type CreateAccountParams, type Token } from 'pl-api';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { createAccount } from 'pl-fe/actions/accounts';
|
||||
|
@ -17,7 +18,6 @@ import { startOnboarding } from 'pl-fe/actions/onboarding';
|
|||
import { type PlfeResponse, getClient } from 'pl-fe/api';
|
||||
import * as BuildConfig from 'pl-fe/build-config';
|
||||
import { custom } from 'pl-fe/custom';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { selectAccount } from 'pl-fe/selectors';
|
||||
import { unsetSentryAccount } from 'pl-fe/sentry';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import throttle from 'lodash/throttle';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages, IntlShape } from 'react-intl';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { isNativeEmoji } from 'pl-fe/features/emoji';
|
||||
import emojiSearch from 'pl-fe/features/emoji/search';
|
||||
import { Language } from 'pl-fe/features/preferences';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { selectAccount, selectOwnAccount, makeGetAccount } from 'pl-fe/selectors';
|
||||
import { tagHistory } from 'pl-fe/settings';
|
||||
import { useModalsStore, useSettingsStore } from 'pl-fe/stores';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import type { Account, Conversation, PaginatedResponse, Status } from 'pl-api';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import KVStore from 'pl-fe/storage/kv-store';
|
||||
|
||||
import type { Account } from 'pl-fe/pl-hooks/normalizers/normalizeAccount';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
import type { Account } from 'pl-hooks/normalizers/normalizeAccount';
|
||||
|
||||
const DRAFT_STATUSES_FETCH_SUCCESS = 'DRAFT_STATUSES_FETCH_SUCCESS' as const;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import type { Status } from 'pl-api';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { STATUS_FETCH_SOURCE_FAIL, STATUS_FETCH_SOURCE_REQUEST, STATUS_FETCH_SOURCE_SUCCESS } from 'pl-fe/actions/statuses';
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import toast from 'pl-fe/toast';
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import type { Account, PaginatedResponse } from 'pl-api';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import type { StatusEdit } from 'pl-api';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import toast, { type IToastOptions } from 'pl-fe/toast';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { selectAccount } from 'pl-fe/selectors';
|
||||
import toast from 'pl-fe/toast';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { selectAccount } from 'pl-fe/selectors';
|
||||
import { setSentryAccount } from 'pl-fe/sentry';
|
||||
import KVStore from 'pl-fe/storage/kv-store';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import 'intl-pluralrules';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import { FILTER_TYPES, type FilterType } from 'pl-fe/features/notifications';
|
||||
import { getNotificationStatus } from 'pl-fe/features/notifications/components/notification';
|
||||
import { normalizeNotification } from 'pl-fe/normalizers';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
// import { getFilters, regexFromFilters } from 'pl-fe/selectors';
|
||||
import { useSettingsStore } from 'pl-fe/stores';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import type { Status } from 'pl-api';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import type { Poll } from 'pl-api';
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import mapValues from 'lodash/mapValues';
|
||||
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { verifyCredentials } from './auth';
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { useSettingsStore } from 'pl-fe/stores';
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Account } from 'pl-hooks/normalizers/normalizeAccount';
|
||||
import { defineMessage } from 'react-intl';
|
||||
|
||||
import { patchMe } from 'pl-fe/actions/me';
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import messages from 'pl-fe/messages';
|
||||
import { Account } from 'pl-fe/pl-hooks/normalizers/normalizeAccount';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import KVStore from 'pl-fe/storage/kv-store';
|
||||
import { useSettingsStore } from 'pl-fe/stores';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import type { Status as BaseStatus, PaginatedResponse } from 'pl-api';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { useModalsStore, useSettingsStore } from 'pl-fe/stores';
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
import { shouldHaveCard } from 'pl-fe/utils/status';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getLocale } from 'pl-fe/actions/settings';
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { useSettingsStore } from 'pl-fe/stores';
|
||||
import { shouldFilter } from 'pl-fe/utils/timelines';
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { getClient } from 'pl-fe/api';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
|
||||
import { HStack, Icon, Text } from 'pl-fe/components/ui';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IQuotedStatusIndicator {
|
||||
/** The quoted status id. */
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { defineMessages, useIntl, FormattedList, FormattedMessage } from 'react-intl';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
|
@ -12,7 +13,6 @@ import StatusTypeIcon from 'pl-fe/features/status/components/status-type-icon';
|
|||
import QuotedStatus from 'pl-fe/features/status/containers/quoted-status-container';
|
||||
import { HotKeys } from 'pl-fe/features/ui/components/hotkeys';
|
||||
import { useAppDispatch, useSettings } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { textForScreenReader } from 'pl-fe/utils/status';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
|
||||
import Status, { IStatus } from 'pl-fe/components/status';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IStatusContainer extends Omit<IStatus, 'status'> {
|
||||
id: string;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Link from 'pl-fe/components/link';
|
||||
import { Text } from 'pl-fe/components/ui';
|
||||
import { useCompose } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IReplyGroupIndicator {
|
||||
composeId: string;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
import { FormattedList, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { useCompose, useFeatures } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
|
||||
interface IReplyMentions {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
|
||||
import { cancelQuoteCompose } from 'pl-fe/actions/compose';
|
||||
import QuotedStatus from 'pl-fe/components/quoted-status';
|
||||
import { useAppDispatch, useCompose } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IQuotedStatusContainer {
|
||||
composeId: string;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
|
||||
import { cancelReplyCompose } from 'pl-fe/actions/compose';
|
||||
import { useAppDispatch, useCompose } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import ReplyIndicator from '../components/reply-indicator';
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
|
@ -7,7 +8,6 @@ import Status from 'pl-fe/components/status';
|
|||
import { Spinner } from 'pl-fe/components/ui';
|
||||
import { useLogo } from 'pl-fe/hooks';
|
||||
import { iframeId } from 'pl-fe/iframe';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IEmbeddedStatus {
|
||||
params: {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
|
@ -11,7 +12,6 @@ import { Stack } from 'pl-fe/components/ui';
|
|||
import PlaceholderStatus from 'pl-fe/features/placeholder/components/placeholder-status';
|
||||
import PendingStatus from 'pl-fe/features/ui/components/pending-status';
|
||||
import { useAppDispatch, useAppSelector } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import ComposeForm from '../compose/components/compose-form';
|
||||
import { getDescendantsIds } from '../status/components/thread';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useCallback } from 'react';
|
||||
import { FormattedDate, FormattedMessage } from 'react-intl';
|
||||
|
||||
|
@ -8,7 +9,6 @@ import TranslateButton from 'pl-fe/components/translate-button';
|
|||
import { HStack, Icon, Stack, Text } from 'pl-fe/components/ui';
|
||||
import QuotedStatus from 'pl-fe/features/status/containers/quoted-status-container';
|
||||
import { usePlFeConfig } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
|
||||
type RouteParams = { statusId: string };
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
|
||||
import EventPreview from 'pl-fe/components/event-preview';
|
||||
import { Card, Icon } from 'pl-fe/components/ui';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import PlaceholderEventPreview from '../../placeholder/components/placeholder-event-preview';
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useNotification } from 'pl-hooks/hooks/notifications/useNotification';
|
||||
import React, { useCallback } from 'react';
|
||||
import { defineMessages, useIntl, FormattedList, FormattedMessage, IntlShape, MessageDescriptor } from 'react-intl';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
|
@ -13,7 +14,6 @@ import AccountContainer from 'pl-fe/containers/account-container';
|
|||
import StatusContainer from 'pl-fe/containers/status-container';
|
||||
import { HotKeys } from 'pl-fe/features/ui/components/hotkeys';
|
||||
import { useAppDispatch, useInstance, useLoggedIn } from 'pl-fe/hooks';
|
||||
import { useNotification } from 'pl-fe/pl-hooks/hooks/notifications/useNotification';
|
||||
import { useModalsStore, useSettingsStore } from 'pl-fe/stores';
|
||||
import { NotificationType } from 'pl-fe/utils/notification';
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useMarker } from 'pl-hooks/hooks/markers/useMarkers';
|
||||
import { useUpdateMarkerMutation } from 'pl-hooks/hooks/markers/useUpdateMarkerMutation';
|
||||
import { useNotificationList } from 'pl-hooks/hooks/notifications/useNotificationList';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
|
@ -10,9 +13,6 @@ import ScrollableList from 'pl-fe/components/scrollable-list';
|
|||
import { Column, Portal } from 'pl-fe/components/ui';
|
||||
import PlaceholderNotification from 'pl-fe/features/placeholder/components/placeholder-notification';
|
||||
import { useAppDispatch, useAppSelector, useSettings } from 'pl-fe/hooks';
|
||||
import { useMarker } from 'pl-fe/pl-hooks/hooks/markers/useMarkers';
|
||||
import { useUpdateMarkerMutation } from 'pl-fe/pl-hooks/hooks/markers/useUpdateMarkerMutation';
|
||||
import { useNotificationList } from 'pl-fe/pl-hooks/hooks/notifications/useNotificationList';
|
||||
import { compareId } from 'pl-fe/utils/comparators';
|
||||
import { NotificationType } from 'pl-fe/utils/notification';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
|
||||
import QuotedStatus from 'pl-fe/components/quoted-status';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IQuotedStatusContainer {
|
||||
/** Status ID to the quoted status. */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
@ -8,7 +9,6 @@ import PullToRefresh from 'pl-fe/components/pull-to-refresh';
|
|||
import { Column, Stack } from 'pl-fe/components/ui';
|
||||
import PlaceholderStatus from 'pl-fe/features/placeholder/components/placeholder-status';
|
||||
import { useAppDispatch, useLoggedIn } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import Thread from './components/thread';
|
||||
import ThreadLoginCta from './components/thread-login-cta';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { importEntities } from 'pl-hooks/importer';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { expandTimelineSuccess } from 'pl-fe/actions/timelines';
|
||||
import { useAppDispatch, useTheme } from 'pl-fe/hooks';
|
||||
import { useIsMobile } from 'pl-fe/hooks/useIsMobile';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
import { Column } from '../../components/ui';
|
||||
import Timeline from '../ui/components/timeline';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import Icon from 'pl-fe/components/icon';
|
||||
import { Modal, Stack, Text } from 'pl-fe/components/ui';
|
||||
import ReplyIndicator from 'pl-fe/features/compose/components/reply-indicator';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import type { BaseModalProps } from '../modal-root';
|
||||
import type { Status as StatusEntity } from 'pl-fe/normalizers';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import L from 'leaflet';
|
||||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button, Modal, Stack } from 'pl-fe/components/ui';
|
||||
import { usePlFeConfig } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import type { BaseModalProps } from '../modal-root';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
@ -15,7 +16,6 @@ import Thread from 'pl-fe/features/status/components/thread';
|
|||
import Video from 'pl-fe/features/video';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { userTouching } from 'pl-fe/is-mobile';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import ImageLoader from '../image-loader';
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
|
@ -6,7 +7,6 @@ import ScrollableList from 'pl-fe/components/scrollable-list';
|
|||
import { Modal, Spinner } from 'pl-fe/components/ui';
|
||||
import AccountContainer from 'pl-fe/containers/account-container';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import type { BaseModalProps } from '../modal-root';
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Modal } from 'pl-fe/components/ui';
|
||||
import Account from 'pl-fe/features/reply-mentions/account';
|
||||
import { useCompose, useOwnAccount } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
import { statusToMentionsAccountIdsArray } from 'pl-fe/reducers/compose';
|
||||
|
||||
import type { BaseModalProps } from '../modal-root';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
|
@ -7,7 +8,6 @@ import { RadioGroup, RadioItem } from 'pl-fe/components/radio';
|
|||
import { Emoji, HStack, Icon, Modal, Spinner, Stack } from 'pl-fe/components/ui';
|
||||
import NewFolderForm from 'pl-fe/features/bookmark-folders/components/new-folder-form';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import type { BaseModalProps } from '../modal-root';
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Video from 'pl-fe/features/video';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
import type { BaseModalProps } from '../modal-root';
|
||||
import type { MediaAttachment } from 'pl-api';
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import { prefetchMarker } from 'pl-hooks/hooks/markers/useMarkers';
|
||||
import { prefetchNotifications } from 'pl-hooks/hooks/notifications/useNotificationList';
|
||||
import React, { Suspense, lazy, useEffect, useRef } from 'react';
|
||||
import { Switch, useHistory, useLocation, Redirect } from 'react-router-dom';
|
||||
|
||||
|
@ -33,8 +35,6 @@ import ProfileLayout from 'pl-fe/layouts/profile-layout';
|
|||
import RemoteInstanceLayout from 'pl-fe/layouts/remote-instance-layout';
|
||||
import SearchLayout from 'pl-fe/layouts/search-layout';
|
||||
import StatusLayout from 'pl-fe/layouts/status-layout';
|
||||
import { prefetchMarker } from 'pl-fe/pl-hooks/hooks/markers/useMarkers';
|
||||
import { prefetchNotifications } from 'pl-fe/pl-hooks/hooks/notifications/useNotificationList';
|
||||
import { useUiStore } from 'pl-fe/stores';
|
||||
import { getVapidKey } from 'pl-fe/utils/auth';
|
||||
import { isStandalone } from 'pl-fe/utils/state';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useStatus } from 'pl-hooks/hooks/statuses/useStatus';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
WhoToFollowPanel,
|
||||
} from 'pl-fe/features/ui/util/async-components';
|
||||
import { useAppSelector, useFeatures } from 'pl-fe/hooks';
|
||||
import { useStatus } from 'pl-fe/pl-hooks/hooks/statuses/useStatus';
|
||||
|
||||
interface IEventLayout {
|
||||
params?: {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { InfiniteData, keepPreviousData, useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
||||
import sumBy from 'lodash/sumBy';
|
||||
import { type Chat, type ChatMessage as BaseChatMessage, type PaginatedResponse, chatMessageSchema, type Relationship } from 'pl-api';
|
||||
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context';
|
||||
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useAppSelector, useClient, useFeatures, useLoggedIn, useOwnAccount } from 'pl-fe/hooks';
|
||||
import { type ChatMessage, normalizeChatMessage } from 'pl-fe/normalizers';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { reOrderChatListItems } from 'pl-fe/utils/chats';
|
||||
import { flattenPages, updatePageItem } from 'pl-fe/utils/queries';
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { useMutation, keepPreviousData, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { fetchRelationships } from 'pl-fe/actions/accounts';
|
||||
import { useAppDispatch, useClient } from 'pl-fe/hooks';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
|
||||
import { removePageItem } from '../utils/queries';
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"pl-fe/*": ["src/*"],
|
||||
"pl-hooks/*": ["src/pl-hooks/*"],
|
||||
},
|
||||
"typeRoots": [
|
||||
"./src/types",
|
||||
|
|
|
@ -154,7 +154,6 @@ const config = defineConfig(({ command }) => ({
|
|||
resolve: {
|
||||
alias: [
|
||||
{ find: 'pl-fe', replacement: fileURLToPath(new URL('./src', import.meta.url)) },
|
||||
{ find: 'pl-hooks', replacement: fileURLToPath(new URL('./src/pl-hooks', import.meta.url)) },
|
||||
],
|
||||
},
|
||||
test: {
|
||||
|
|
14
packages/pl-hooks/lib/client.ts
Normal file
14
packages/pl-hooks/lib/client.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 60000, // 1 minute
|
||||
gcTime: Infinity,
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { queryClient };
|
|
@ -1,9 +1,9 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useRelationship } from 'pl-fe/api/hooks/accounts/useRelationship';
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { normalizeAccount } from 'pl-fe/pl-hooks/normalizers/normalizeAccount';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
import { normalizeAccount } from 'pl-hooks/normalizers/normalizeAccount';
|
||||
|
||||
interface UseAccountOpts {
|
||||
withRelationship?: boolean;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
|
||||
import type { PlApiClient } from 'pl-api';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
|
||||
import type { Timeline } from './useMarkers';
|
||||
import type { Marker } from 'pl-api';
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
|
||||
import { useAppSelector, useClient } from 'pl-fe/hooks';
|
||||
import { normalizeNotification, type Notification } from 'pl-fe/normalizers';
|
||||
import { type MinifiedNotification, minifyNotification } from 'pl-fe/pl-hooks/minifiers/minifyNotification';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { selectAccount, selectAccounts } from 'pl-fe/selectors';
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
import { type NormalizedNotification, normalizeNotification } from 'pl-hooks/normalizers/normalizeNotifications';
|
||||
|
||||
import { useAccount } from '../accounts/useAccount';
|
||||
import { useStatus } from '../statuses/useStatus';
|
||||
|
||||
type Account = ReturnType<typeof selectAccount>;
|
||||
|
||||
const importNotification = (notification: MinifiedNotification) => {
|
||||
queryClient.setQueryData<MinifiedNotification>(
|
||||
const importNotification = (notification: NormalizedNotification) => {
|
||||
queryClient.setQueryData<NormalizedNotification>(
|
||||
['notifications', 'entities', notification.id],
|
||||
existingNotification => existingNotification?.duplicate ? existingNotification : notification,
|
||||
);
|
||||
|
@ -22,25 +22,24 @@ const useNotification = (notificationId: string) => {
|
|||
const notificationQuery = useQuery({
|
||||
queryKey: ['notifications', 'entities', notificationId],
|
||||
queryFn: () => client.notifications.getNotification(notificationId)
|
||||
.then(normalizeNotification)
|
||||
.then(minifyNotification),
|
||||
.then(normalizeNotification),
|
||||
});
|
||||
|
||||
const notification = notificationQuery.data;
|
||||
|
||||
const accountQuery = useAccount(notification?.account_id);
|
||||
const moveTargetAccountQuery = useAccount(notification?.target_id);
|
||||
const statusQuery = useStatus(notification?.status_id);
|
||||
|
||||
const data: Notification | null = useAppSelector((state) => {
|
||||
const notification = notificationQuery.data;
|
||||
if (!notification) return null;
|
||||
const account = selectAccount(state, notification.account_id)!;
|
||||
// @ts-ignore
|
||||
const target = selectAccount(state, notification.target_id)!;
|
||||
// @ts-ignore
|
||||
const status = state.statuses.get(notification.status_id)!;
|
||||
const accounts = selectAccounts(state, notification.account_ids).filter((account): account is Account => account !== undefined);
|
||||
|
||||
return {
|
||||
...notification,
|
||||
account,
|
||||
target,
|
||||
status,
|
||||
account: accountQuery.data,
|
||||
target: moveTargetAccountQuery.data,
|
||||
status: statusQuery.data,
|
||||
accounts,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useClient } from 'pl-fe/hooks';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { deduplicateNotifications } from 'pl-fe/pl-hooks/normalizers/deduplicateNotifications';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { flattenPages } from 'pl-fe/utils/queries';
|
||||
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
import { deduplicateNotifications } from 'pl-hooks/normalizers/deduplicateNotifications';
|
||||
import { flattenPages } from 'pl-hooks/utils/queries';
|
||||
|
||||
import type { Notification as BaseNotification, PaginatedResponse, PlApiClient } from 'pl-api';
|
||||
import type { NotificationType } from 'pl-fe/utils/notification';
|
||||
|
||||
type UseNotificationParams = {
|
||||
types?: Array<NotificationType>;
|
||||
excludeTypes?: Array<NotificationType>;
|
||||
types?: Array<BaseNotification['type']>;
|
||||
excludeTypes?: Array<BaseNotification['type']>;
|
||||
}
|
||||
|
||||
const getQueryKey = (params: UseNotificationParams) => [
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAppSelector, useClient } from 'pl-fe/hooks';
|
||||
import { selectAccount, selectAccounts } from 'pl-fe/selectors';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks';
|
||||
import { useAppSelector, useClient } from 'pl-fe/hooks';
|
||||
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { selectAccount, selectAccounts } from 'pl-fe/selectors';
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
import { useAccount } from 'pl-hooks/hooks/accounts/useAccount';
|
||||
import { importEntities } from 'pl-hooks/importer';
|
||||
|
||||
import { normalizeStatus, type Status } from '../../normalizers/normalizeStatus';
|
||||
|
||||
|
@ -106,7 +106,7 @@ const useStatus = (statusId?: string) => {
|
|||
|
||||
const status = statusQuery.data;
|
||||
|
||||
const { account } = useAccount(status?.account_id || undefined);
|
||||
const { data: account } = useAccount(status?.account_id || undefined);
|
||||
|
||||
// : (Status & {
|
||||
// account: Account;
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { importAccounts, importGroups, importPolls, importStatuses } from 'pl-fe/actions/importer';
|
||||
import { importEntities as importEntityStoreEntities } from 'pl-fe/entity-store/actions';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { queryClient } from 'pl-hooks/client';
|
||||
|
||||
import { minifyNotification, type MinifiedNotification } from './minifiers/minifyNotification';
|
||||
import { DeduplicatedNotification } from './normalizers/deduplicateNotifications';
|
||||
import { type MinifiedNotification, minifyNotification } from './minifiers/minifyNotification';
|
||||
import { DeduplicatedNotification } from './normalizers/normalizeNotifications';
|
||||
import { normalizeStatus, type Status } from './normalizers/normalizeStatus';
|
||||
|
||||
import type {
|
||||
|
@ -14,30 +11,34 @@ import type {
|
|||
Relationship as BaseRelationship,
|
||||
Status as BaseStatus,
|
||||
} from 'pl-api';
|
||||
import type { AppDispatch } from 'pl-fe/store';
|
||||
|
||||
let dispatch: AppDispatch;
|
||||
const importAccount = (account: BaseAccount) => queryClient.setQueryData<BaseAccount>(
|
||||
['accounts', 'entities', account.id], account,
|
||||
);
|
||||
|
||||
import('pl-fe/store').then(value => dispatch = value.store.dispatch).catch(() => {});
|
||||
const importGroup = (group: BaseGroup) => queryClient.setQueryData<BaseGroup>(
|
||||
['groups', 'entities', group.id], group,
|
||||
);
|
||||
|
||||
const importNotification = (notification: DeduplicatedNotification) => {
|
||||
queryClient.setQueryData<MinifiedNotification>(
|
||||
['notifications', 'entities', notification.id],
|
||||
existingNotification => existingNotification?.duplicate ? existingNotification : minifyNotification(notification),
|
||||
);
|
||||
};
|
||||
const importNotification = (notification: DeduplicatedNotification) => queryClient.setQueryData<MinifiedNotification>(
|
||||
['notifications', 'entities', notification.id],
|
||||
existingNotification => existingNotification?.duplicate ? existingNotification : minifyNotification(notification),
|
||||
);
|
||||
|
||||
const importStatus = (status: BaseStatus) => {
|
||||
queryClient.setQueryData<Status>(
|
||||
['statuses', 'entities', status.id],
|
||||
_ => normalizeStatus(status),
|
||||
);
|
||||
};
|
||||
const importPoll = (poll: BasePoll) => queryClient.setQueryData<BasePoll>(
|
||||
['polls', 'entities', poll.id], poll,
|
||||
);
|
||||
|
||||
const isEmpty = (object: Record<string, any>) => {
|
||||
for (const i in object) return false;
|
||||
return true;
|
||||
};
|
||||
const importRelationship = (relationship: BaseRelationship) => queryClient.setQueryData<BaseRelationship>(
|
||||
['relationships', 'entities', relationship.id], relationship,
|
||||
);
|
||||
|
||||
const importStatus = (status: BaseStatus) => queryClient.setQueryData<Status>(
|
||||
['statuses', 'entities', status.id],
|
||||
_ => normalizeStatus(status),
|
||||
);
|
||||
|
||||
const isEmpty = (object: Record<string, any>) => !Object.values(object).some(value => value);
|
||||
|
||||
const importEntities = (entities: {
|
||||
accounts?: Array<BaseAccount>;
|
||||
|
@ -56,16 +57,12 @@ const importEntities = (entities: {
|
|||
const relationships: Record<string, BaseRelationship> = {};
|
||||
const statuses: Record<string, BaseStatus> = {};
|
||||
|
||||
const processAccount = (account: BaseAccount, withParent = true) => {
|
||||
if (withParent) accounts[account.id] = account;
|
||||
|
||||
const processAccount = (account: BaseAccount) => {
|
||||
if (account.moved) processAccount(account.moved);
|
||||
if (account.relationship) relationships[account.relationship.id] = account.relationship;
|
||||
};
|
||||
|
||||
const processNotification = (notification: DeduplicatedNotification, withParent = true) => {
|
||||
if (withParent) notifications[notification.id] = notification;
|
||||
|
||||
const processNotification = (notification: DeduplicatedNotification) => {
|
||||
processAccount(notification.account);
|
||||
if (notification.type === 'move') processAccount(notification.target);
|
||||
|
||||
|
@ -74,9 +71,8 @@ const importEntities = (entities: {
|
|||
processStatus(notification.status);
|
||||
};
|
||||
|
||||
const processStatus = (status: BaseStatus, withParent = true) => {
|
||||
const processStatus = (status: BaseStatus) => {
|
||||
if (status.account) {
|
||||
if (withParent) statuses[status.id] = status;
|
||||
processAccount(status.account);
|
||||
}
|
||||
|
||||
|
@ -87,21 +83,23 @@ const importEntities = (entities: {
|
|||
};
|
||||
|
||||
if (options.withParents) {
|
||||
entities.accounts?.forEach(account => accounts[account.id] = account);
|
||||
entities.groups?.forEach(group => groups[group.id] = group);
|
||||
entities.notifications?.forEach(notification => notifications[notification.id] = notification);
|
||||
entities.polls?.forEach(poll => polls[poll.id] = poll);
|
||||
entities.relationships?.forEach(relationship => relationships[relationship.id] = relationship);
|
||||
entities.statuses?.forEach(status => statuses[status.id] = status);
|
||||
}
|
||||
|
||||
entities.accounts?.forEach((account) => processAccount(account, options.withParents));
|
||||
entities.notifications?.forEach((notification) => processNotification(notification, options.withParents));
|
||||
entities.statuses?.forEach((status) => processStatus(status, options.withParents));
|
||||
entities.accounts?.forEach((account) => processAccount(account));
|
||||
entities.notifications?.forEach((notification) => processNotification(notification));
|
||||
entities.statuses?.forEach((status) => processStatus(status));
|
||||
|
||||
if (!isEmpty(accounts)) dispatch(importAccounts(Object.values(accounts)));
|
||||
if (!isEmpty(groups)) dispatch(importGroups(Object.values(groups)));
|
||||
if (!isEmpty(accounts)) Object.values(accounts).forEach(importAccount);
|
||||
if (!isEmpty(groups)) Object.values(groups).forEach(importGroup);
|
||||
if (!isEmpty(notifications)) Object.values(notifications).forEach(importNotification);
|
||||
if (!isEmpty(polls)) dispatch(importPolls(Object.values(polls)));
|
||||
if (!isEmpty(relationships)) dispatch(importEntityStoreEntities(Object.values(relationships), Entities.RELATIONSHIPS));
|
||||
if (!isEmpty(statuses)) dispatch(importStatuses(Object.values(statuses)));
|
||||
if (!isEmpty(polls)) Object.values(polls).forEach(importPoll);
|
||||
if (!isEmpty(relationships)) Object.values(relationships).forEach(importRelationship);
|
||||
if (!isEmpty(statuses)) Object.values(statuses).forEach(importStatus);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import omit from 'lodash/omit';
|
||||
|
||||
import { DeduplicatedNotification } from '../normalizers/deduplicateNotifications';
|
||||
import { DeduplicatedNotification } from '../normalizers/normalizeNotifications';
|
||||
|
||||
import type { AccountWarning, RelationshipSeveranceEvent } from 'pl-api';
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import { getNotificationStatus } from 'pl-fe/features/notifications/components/notification';
|
||||
|
||||
import type { Account as BaseAccount, Notification as BaseNotification } from 'pl-api';
|
||||
|
||||
type DeduplicatedNotification = BaseNotification & {
|
||||
accounts: Array<BaseAccount>;
|
||||
duplicate?: boolean;
|
||||
}
|
||||
|
||||
const STATUS_NOTIFICATION_TYPES = [
|
||||
'favourite',
|
||||
'reblog',
|
||||
'emoji_reaction',
|
||||
'event_reminder',
|
||||
'participation_accepted',
|
||||
'participation_request',
|
||||
];
|
||||
|
||||
const deduplicateNotifications = (notifications: Array<BaseNotification>) => {
|
||||
const deduplicatedNotifications: DeduplicatedNotification[] = [];
|
||||
|
||||
for (const notification of notifications) {
|
||||
if (STATUS_NOTIFICATION_TYPES.includes(notification.type)) {
|
||||
const existingNotification = deduplicatedNotifications
|
||||
.find(deduplicated =>
|
||||
deduplicated.type === notification.type
|
||||
&& ((notification.type === 'emoji_reaction' && deduplicated.type === 'emoji_reaction') ? notification.emoji === deduplicated.emoji : true)
|
||||
&& getNotificationStatus(deduplicated)?.id === getNotificationStatus(notification)?.id,
|
||||
);
|
||||
|
||||
if (existingNotification) {
|
||||
existingNotification.accounts.push(notification.account);
|
||||
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: true });
|
||||
} else {
|
||||
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: false });
|
||||
}
|
||||
} else {
|
||||
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: false });
|
||||
}
|
||||
}
|
||||
|
||||
return deduplicatedNotifications;
|
||||
};
|
||||
|
||||
export { deduplicateNotifications, type DeduplicatedNotification };
|
|
@ -1,37 +1,9 @@
|
|||
import escapeTextContentForBrowser from 'escape-html';
|
||||
|
||||
import emojify from 'pl-fe/features/emoji';
|
||||
import { unescapeHTML } from 'pl-fe/utils/html';
|
||||
import { makeEmojiMap } from 'pl-fe/utils/normalizers';
|
||||
|
||||
import type { Account as BaseAccount } from 'pl-api';
|
||||
|
||||
const normalizeAccount = ({ moved, ...account }: BaseAccount) => {
|
||||
const missingAvatar = require('pl-fe/assets/images/avatar-missing.png');
|
||||
const missingHeader = require('pl-fe/assets/images/header-missing.png');
|
||||
const note = account.note === '<p></p>' ? '' : account.note;
|
||||
|
||||
const emojiMap = makeEmojiMap(account.emojis);
|
||||
|
||||
return {
|
||||
...account,
|
||||
moved_id: moved?.id || null,
|
||||
avatar: account.avatar || account.avatar_static || missingAvatar,
|
||||
avatar_static: account.avatar_static || account.avatar || missingAvatar,
|
||||
header: account.header || account.header_static || missingHeader,
|
||||
header_static: account.header_static || account.header || missingHeader,
|
||||
note,
|
||||
display_name_html: emojify(escapeTextContentForBrowser(account.display_name), emojiMap),
|
||||
note_emojified: emojify(account.note, emojiMap),
|
||||
note_plain: unescapeHTML(account.note),
|
||||
fields: account.fields.map(field => ({
|
||||
...field,
|
||||
name_emojified: emojify(escapeTextContentForBrowser(field.name), emojiMap),
|
||||
value_emojified: emojify(field.value, emojiMap),
|
||||
value_plain: unescapeHTML(field.value),
|
||||
})),
|
||||
};
|
||||
};
|
||||
const normalizeAccount = ({ moved, ...account }: BaseAccount) => ({
|
||||
...account,
|
||||
moved_id: moved?.id || null,
|
||||
});
|
||||
|
||||
type Account = ReturnType<typeof normalizeAccount>;
|
||||
|
||||
|
|
131
packages/pl-hooks/lib/normalizers/normalizeNotifications.ts
Normal file
131
packages/pl-hooks/lib/normalizers/normalizeNotifications.ts
Normal file
|
@ -0,0 +1,131 @@
|
|||
import omit from 'lodash/omit';
|
||||
|
||||
import type { AccountWarning, Account as BaseAccount, Notification as BaseNotification, RelationshipSeveranceEvent } from 'pl-api';
|
||||
|
||||
type DeduplicatedNotification = BaseNotification & {
|
||||
accounts: Array<BaseAccount>;
|
||||
duplicate?: boolean;
|
||||
}
|
||||
|
||||
const STATUS_NOTIFICATION_TYPES = [
|
||||
'mention',
|
||||
'status',
|
||||
'reblog',
|
||||
'favourite',
|
||||
'poll',
|
||||
'update',
|
||||
'emoji_reaction',
|
||||
'event_reminder',
|
||||
'participation_accepted',
|
||||
'participation_request',
|
||||
];
|
||||
|
||||
const getNotificationStatus = (n: Pick<BaseNotification, 'type'>) => {
|
||||
if (STATUS_NOTIFICATION_TYPES.includes(n.type))
|
||||
// @ts-ignore
|
||||
return n.status;
|
||||
return null;
|
||||
};
|
||||
|
||||
const normalizeNotifications = (notifications: Array<BaseNotification>): Array<NormalizedNotification> => {
|
||||
const deduplicatedNotifications: DeduplicatedNotification[] = [];
|
||||
|
||||
for (const notification of notifications) {
|
||||
if (STATUS_NOTIFICATION_TYPES.includes(notification.type)) {
|
||||
const existingNotification = deduplicatedNotifications
|
||||
.find(deduplicated =>
|
||||
deduplicated.type === notification.type
|
||||
&& ((notification.type === 'emoji_reaction' && deduplicated.type === 'emoji_reaction') ? notification.emoji === deduplicated.emoji : true)
|
||||
&& getNotificationStatus(deduplicated)?.id === getNotificationStatus(notification)?.id,
|
||||
);
|
||||
|
||||
if (existingNotification) {
|
||||
existingNotification.accounts.push(notification.account);
|
||||
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: true });
|
||||
} else {
|
||||
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: false });
|
||||
}
|
||||
} else {
|
||||
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: false });
|
||||
}
|
||||
}
|
||||
|
||||
return deduplicatedNotifications.map(normalizeNotification);
|
||||
};
|
||||
|
||||
|
||||
const normalizeNotification = (notification: BaseNotification | DeduplicatedNotification) => {
|
||||
// @ts-ignore
|
||||
const minifiedNotification: {
|
||||
duplicate: boolean;
|
||||
account_id: string;
|
||||
account_ids: string[];
|
||||
created_at: string;
|
||||
id: string;
|
||||
group_key: string;
|
||||
} & (
|
||||
| { type: 'follow' | 'follow_request' | 'admin.sign_up' | 'bite' }
|
||||
| {
|
||||
type: 'mention';
|
||||
subtype?: 'reply';
|
||||
status_id: string;
|
||||
}
|
||||
| {
|
||||
type: 'status' | 'reblog' | 'favourite' | 'poll' | 'update' | 'event_reminder';
|
||||
status_id: string;
|
||||
}
|
||||
| {
|
||||
type: 'admin.report';
|
||||
report: Report;
|
||||
}
|
||||
| {
|
||||
type: 'severed_relationships';
|
||||
relationship_severance_event: RelationshipSeveranceEvent;
|
||||
}
|
||||
| {
|
||||
type: 'moderation_warning';
|
||||
moderation_warning: AccountWarning;
|
||||
}
|
||||
| {
|
||||
type: 'move';
|
||||
target_id: string;
|
||||
}
|
||||
| {
|
||||
type: 'emoji_reaction';
|
||||
emoji: string;
|
||||
emoji_url: string | null;
|
||||
status_id: string;
|
||||
}
|
||||
| {
|
||||
type: 'chat_mention';
|
||||
chat_message_id: string;
|
||||
}
|
||||
| {
|
||||
type: 'participation_accepted' | 'participation_request';
|
||||
status_id: string;
|
||||
participation_message: string | null;
|
||||
}
|
||||
) = {
|
||||
duplicate: false,
|
||||
...omit(notification, ['account', 'accounts', 'status', 'target', 'chat_message']),
|
||||
account_id: notification.account.id,
|
||||
account_ids: ('accounts' in notification) ? notification.accounts.map(({ id }) => id) : [notification.account.id],
|
||||
created_at: notification.created_at,
|
||||
id: notification.id,
|
||||
type: notification.type,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
if (notification.status) minifiedNotification.status_id = notification.status.id;
|
||||
// @ts-ignore
|
||||
if (notification.target) minifiedNotification.target_id = notification.target.id;
|
||||
// @ts-ignore
|
||||
if (notification.chat_message) minifiedNotification.chat_message_id = notification.chat_message.id;
|
||||
|
||||
return minifiedNotification;
|
||||
};
|
||||
|
||||
type NormalizedNotification = ReturnType<typeof normalizeNotification>;
|
||||
|
||||
|
||||
export { normalizeNotifications, normalizeNotification, type NormalizedNotification };
|
|
@ -3,96 +3,12 @@
|
|||
* Converts API statuses into our internal format.
|
||||
* @see {@link https://docs.joinmastodon.org/entities/status/}
|
||||
*/
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import DOMPurify from 'isomorphic-dompurify';
|
||||
import { type Account as BaseAccount, type Status as BaseStatus, type MediaAttachment, mentionSchema, type Translation } from 'pl-api';
|
||||
|
||||
import emojify from 'pl-fe/features/emoji';
|
||||
import { queryClient } from 'pl-fe/queries/client';
|
||||
import { unescapeHTML } from 'pl-fe/utils/html';
|
||||
import { makeEmojiMap } from 'pl-fe/utils/normalizers';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
import { type Account as BaseAccount, type Status as BaseStatus, type MediaAttachment, mentionSchema } from 'pl-api';
|
||||
|
||||
type StatusApprovalStatus = Exclude<BaseStatus['approval_status'], null>;
|
||||
type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct' | 'group' | 'mutuals_only' | 'local';
|
||||
|
||||
type CalculatedValues = {
|
||||
search_index: string;
|
||||
contentHtml: string;
|
||||
spoilerHtml: string;
|
||||
contentMapHtml?: Record<string, string>;
|
||||
spoilerMapHtml?: Record<string, string>;
|
||||
expanded?: boolean | null;
|
||||
hidden?: boolean | null;
|
||||
translation?: Translation | null | false;
|
||||
currentLanguage?: string;
|
||||
};
|
||||
|
||||
type OldStatus = Pick<BaseStatus, 'content' | 'spoiler_text'> & CalculatedValues;
|
||||
|
||||
// Gets titles of poll options from status
|
||||
const getPollOptionTitles = ({ poll }: Pick<BaseStatus, 'poll'>): readonly string[] => {
|
||||
if (poll && typeof poll === 'object') {
|
||||
return poll.options.map(({ title }) => title);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// Gets usernames of mentioned users from status
|
||||
const getMentionedUsernames = (status: Pick<BaseStatus, 'mentions'>): Array<string> =>
|
||||
status.mentions.map(({ acct }) => `@${acct}`);
|
||||
|
||||
// Creates search text from the status
|
||||
const buildSearchContent = (status: Pick<BaseStatus, 'poll' | 'mentions' | 'spoiler_text' | 'content'>): string => {
|
||||
const pollOptionTitles = getPollOptionTitles(status);
|
||||
const mentionedUsernames = getMentionedUsernames(status);
|
||||
|
||||
const fields = [
|
||||
status.spoiler_text,
|
||||
status.content,
|
||||
...pollOptionTitles,
|
||||
...mentionedUsernames,
|
||||
];
|
||||
|
||||
return unescapeHTML(fields.join('\n\n')) || '';
|
||||
};
|
||||
|
||||
const calculateContent = (text: string, emojiMap: any, hasQuote?: boolean) => emojify(text, emojiMap);
|
||||
const calculateSpoiler = (text: string, emojiMap: any) => DOMPurify.sanitize(emojify(escapeTextContentForBrowser(text), emojiMap), { USE_PROFILES: { html: true } });
|
||||
|
||||
const calculateStatus = (status: BaseStatus, oldStatus?: OldStatus): CalculatedValues => {
|
||||
if (oldStatus && oldStatus.content === status.content && oldStatus.spoiler_text === status.spoiler_text) {
|
||||
const {
|
||||
search_index, contentHtml, spoilerHtml, contentMapHtml, spoilerMapHtml, hidden, expanded, translation, currentLanguage,
|
||||
} = oldStatus;
|
||||
|
||||
return {
|
||||
search_index, contentHtml, spoilerHtml, contentMapHtml, spoilerMapHtml, hidden, expanded, translation, currentLanguage,
|
||||
};
|
||||
} else {
|
||||
const searchContent = buildSearchContent(status);
|
||||
const emojiMap = makeEmojiMap(status.emojis);
|
||||
|
||||
return {
|
||||
search_index: domParser.parseFromString(searchContent, 'text/html').documentElement.textContent || '',
|
||||
contentHtml: calculateContent(status.content, emojiMap, !!status.quote),
|
||||
spoilerHtml: calculateSpoiler(status.spoiler_text, emojiMap),
|
||||
contentMapHtml: status.content_map
|
||||
? Object.fromEntries(Object.entries(status.content_map)?.map(([key, value]) => [key, calculateContent(value, emojiMap, !!status.quote)]))
|
||||
: undefined,
|
||||
spoilerMapHtml: status.spoiler_text_map
|
||||
? Object.fromEntries(Object.entries(status.spoiler_text_map).map(([key, value]) => [key, calculateSpoiler(value, emojiMap)]))
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeStatus = ({ account, accounts, reblog, poll, group, quote, ...status }: BaseStatus & { accounts?: Array<BaseAccount> }) => {
|
||||
const oldStatus = queryClient.getQueryData<OldStatus>(['statuses', 'entities', status.id]);
|
||||
const calculated = calculateStatus(status, oldStatus);
|
||||
|
||||
// Sort the replied-to mention to the top
|
||||
let mentions = status.mentions.toSorted((a, _b) => {
|
||||
if (a.id === status.in_reply_to_account_id) {
|
||||
|
@ -107,7 +23,7 @@ const normalizeStatus = ({ account, accounts, reblog, poll, group, quote, ...sta
|
|||
const hasSelfMention = status.mentions.some(mention => account.id === mention.id);
|
||||
|
||||
if (isSelfReply && !hasSelfMention) {
|
||||
const selfMention = mentionSchema.parse(status.account);
|
||||
const selfMention = mentionSchema.parse(account);
|
||||
mentions = [selfMention, ...mentions];
|
||||
}
|
||||
|
||||
|
@ -151,13 +67,9 @@ const normalizeStatus = ({ account, accounts, reblog, poll, group, quote, ...sta
|
|||
mentions,
|
||||
expanded: null,
|
||||
hidden: null,
|
||||
/** Rewrite `<p></p>` to empty string. */
|
||||
content: status.content === '<p></p>' ? '' : status.content,
|
||||
filtered: status.filtered?.map(result => result.filter.title),
|
||||
event,
|
||||
media_attachments,
|
||||
...calculated,
|
||||
translation: (status.translation || calculated.translation || null) as Translation | null | false,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
12
packages/pl-hooks/lib/utils/queries.ts
Normal file
12
packages/pl-hooks/lib/utils/queries.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type { InfiniteData } from '@tanstack/react-query';
|
||||
import type { PaginatedResponse } from 'pl-api';
|
||||
|
||||
/** Flatten paginated results into a single array. */
|
||||
const flattenPages = <T>(queryData: InfiniteData<Pick<PaginatedResponse<T>, 'items'>> | undefined) => {
|
||||
return queryData?.pages.reduce<T[]>(
|
||||
(prev: T[], curr) => [...prev, ...(curr.items)],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
export { flattenPages };
|
|
@ -18,6 +18,7 @@
|
|||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/node": "^20.14.12",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
|
@ -32,6 +33,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"lodash": "^4.17.21",
|
||||
"pl-api": "^0.0.37"
|
||||
},
|
||||
"module": "./dist/main.es.js",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
|
@ -18,7 +19,11 @@
|
|||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
"paths": {
|
||||
"pl-hooks/*": ["lib/*"],
|
||||
},
|
||||
},
|
||||
"include": ["src", "lib"]
|
||||
"include": ["lib"]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { fileURLToPath, URL } from 'node:url';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
|
@ -16,4 +17,9 @@ export default defineConfig({
|
|||
target: 'esnext',
|
||||
sourcemap: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{ find: 'pl-hooks', replacement: fileURLToPath(new URL('./lib', import.meta.url)) },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
|
@ -408,6 +408,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||
|
||||
"@types/lodash@^4.17.10":
|
||||
version "4.17.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.10.tgz#64f3edf656af2fe59e7278b73d3e62404144a6e6"
|
||||
integrity sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==
|
||||
|
||||
"@types/node@^20.14.12":
|
||||
version "20.14.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.12.tgz#129d7c3a822cb49fc7ff661235f19cfefd422b49"
|
||||
|
@ -1863,7 +1868,7 @@ lodash.pick@^4.4.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
|
||||
integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
|
||||
|
||||
lodash@~4.17.15:
|
||||
lodash@^4.17.21, lodash@~4.17.15:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
|
Loading…
Reference in a new issue