use named imports
This commit is contained in:
@@ -2,6 +2,4 @@ import createMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockStore = createMockStore<any, any>([thunk]);
|
||||
|
||||
export default mockStore;
|
||||
export const mockStore = createMockStore<any, any>([thunk]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Comment } from './types';
|
||||
import { apiFetcher } from './fetcher';
|
||||
|
||||
export default function getLastComments(siteId: string, max: number): Promise<Comment[]> {
|
||||
export function getLastComments(siteId: string, max: number): Promise<Comment[]> {
|
||||
return apiFetcher.get(`/last/${max}`, { site: siteId });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// based on https://github.com/sindresorhus/copy-text-to-clipboard, but improved to copy text styles too
|
||||
export default function copy(input: string): boolean {
|
||||
export function copy(input: string): boolean {
|
||||
const element = document.createElement('textarea') as HTMLTextAreaElement;
|
||||
const previouslyFocusedElement = document.activeElement as HTMLElement;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import parseQuery from 'utils/parseQuery';
|
||||
import { parseQuery } from 'utils/parseQuery';
|
||||
|
||||
import type { Theme } from './types';
|
||||
import { THEMES, MAX_SHOWN_ROOT_COMMENTS } from './constants';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import parseQuery from 'utils/parseQuery';
|
||||
import { parseQuery } from 'utils/parseQuery';
|
||||
|
||||
import type { UserInfo } from './types';
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IntlProvider } from 'react-intl';
|
||||
import type { User } from 'common/types';
|
||||
import enMessages from 'locales/en.json';
|
||||
|
||||
import AuthPanel, { Props } from './auth-panel';
|
||||
import { AuthPanel, Props } from './auth-panel';
|
||||
|
||||
const DefaultProps = {
|
||||
postInfo: {
|
||||
|
||||
@@ -6,16 +6,15 @@ import b from 'bem-react-helper';
|
||||
import { User, Sorting, Theme, PostInfo } from 'common/types';
|
||||
import { IS_STORAGE_AVAILABLE, IS_THIRD_PARTY } from 'common/constants';
|
||||
import { requestDeletion } from 'utils/email';
|
||||
import postMessage from 'utils/postMessage';
|
||||
import { postMessage } from 'utils/postMessage';
|
||||
import { getHandleClickProps } from 'common/accessibility';
|
||||
import { StoreState } from 'store';
|
||||
import { Dropdown, DropdownItem } from 'components/dropdown';
|
||||
import { Button } from 'components/button';
|
||||
import Auth from 'components/auth';
|
||||
import { Auth } from 'components/auth';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
|
||||
import useTheme from 'hooks/useTheme';
|
||||
|
||||
export interface OwnProps {
|
||||
interface OwnProps {
|
||||
user: User | null;
|
||||
hiddenUsers: StoreState['hiddenUsers'];
|
||||
isCommentsDisabled: boolean;
|
||||
@@ -40,7 +39,7 @@ interface State {
|
||||
sortSelectFocused: boolean;
|
||||
}
|
||||
|
||||
export class AuthPanel extends Component<Props, State> {
|
||||
class AuthPanelComponent extends Component<Props, State> {
|
||||
state = {
|
||||
isBlockedVisible: false,
|
||||
anonymousUsernameInputValue: 'anon',
|
||||
@@ -319,10 +318,10 @@ function getSortArray(currentSort: Sorting, intl: IntlShape) {
|
||||
});
|
||||
}
|
||||
|
||||
export default function AuthPanelConnected(props: OwnProps) {
|
||||
export function AuthPanel(props: OwnProps) {
|
||||
const intl = useIntl();
|
||||
const theme = useTheme();
|
||||
const sort = useSelector<StoreState, Sorting>((state) => state.comments.sort);
|
||||
|
||||
return <AuthPanel intl={intl} theme={theme} sort={sort} {...props} />;
|
||||
return <AuthPanelComponent intl={intl} theme={theme} sort={sort} {...props} />;
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@ import './_theme/_light/auth-panel_theme_light.css';
|
||||
|
||||
import './_logged-in/auth-panel_logged-in.css';
|
||||
|
||||
export { default } from './auth-panel';
|
||||
export * from './auth-panel';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
export const messages = defineMessages({
|
||||
signin: {
|
||||
id: 'auth.signin',
|
||||
defaultMessage: 'Sign In',
|
||||
@@ -58,5 +58,3 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Submit',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { fireEvent, render, waitFor } from '@testing-library/preact';
|
||||
import { OAuthProvider, User } from 'common/types';
|
||||
import { StaticStore } from 'common/static-store';
|
||||
|
||||
import Auth from './auth';
|
||||
import { Auth } from './auth';
|
||||
import * as utils from './auth.utils';
|
||||
import * as api from './auth.api';
|
||||
import { getProviderData } from './components/oauth.utils';
|
||||
@@ -14,7 +14,9 @@ jest.mock('react-redux', () => ({
|
||||
useDispatch: () => jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('hooks/useTheme', () => () => 'light');
|
||||
jest.mock('hooks/useTheme', () => ({
|
||||
useTheme: () => 'light',
|
||||
}));
|
||||
|
||||
describe('<Auth/>', () => {
|
||||
let defaultProviders = StaticStore.config.auth_providers;
|
||||
|
||||
@@ -6,18 +6,18 @@ import { useDispatch } from 'react-redux';
|
||||
|
||||
import { setUser } from 'store/user/actions';
|
||||
import { Input } from 'components/input';
|
||||
import TextareaAutosize from 'components/textarea-autosize';
|
||||
import { TextareaAutosize } from 'components/textarea-autosize';
|
||||
|
||||
import Button from './components/button';
|
||||
import OAuthProviders from './components/oauth';
|
||||
import messages from './auth.messsages';
|
||||
import { Button } from './components/button';
|
||||
import { OAuth } from './components/oauth';
|
||||
import { messages } from './auth.messsages';
|
||||
import { useDropdown } from './auth.hooks';
|
||||
import { getProviders, getTokenInvalidReason } from './auth.utils';
|
||||
import { emailSignin, verifyEmailSignin, anonymousSignin } from './auth.api';
|
||||
|
||||
import styles from './auth.module.css';
|
||||
|
||||
export default function Auth() {
|
||||
export function Auth() {
|
||||
const intl = useIntl();
|
||||
const dispath = useDispatch();
|
||||
const [oauthProviders, formProviders] = getProviders();
|
||||
@@ -199,7 +199,7 @@ export default function Auth() {
|
||||
<h5 className={clsx('auth-form-title', styles.title)}>
|
||||
{intl.formatMessage(messages.oauthSource)}
|
||||
</h5>
|
||||
<OAuthProviders providers={oauthProviders} />
|
||||
<OAuth providers={oauthProviders} />
|
||||
</>
|
||||
)}
|
||||
{hasOAuthProviders && hasFormProviders && (
|
||||
|
||||
@@ -3,7 +3,7 @@ import { StaticStore } from 'common/static-store';
|
||||
import type { FormProvider, OAuthProvider } from 'common/types';
|
||||
|
||||
import { OAUTH_PROVIDERS } from './components/oauth.consts';
|
||||
import messages from './auth.messsages';
|
||||
import { messages } from './auth.messsages';
|
||||
|
||||
export function getProviders(): [OAuthProvider[], FormProvider[]] {
|
||||
const oauthProviders: OAuthProvider[] = [];
|
||||
|
||||
@@ -11,7 +11,7 @@ type Props = Omit<JSX.HTMLAttributes<HTMLButtonElement>, 'size'> & {
|
||||
selected?: boolean;
|
||||
};
|
||||
|
||||
export default function Button({ children, size, kind, suffix, selected, className, ...props }: Props) {
|
||||
export function Button({ children, size, kind, suffix, selected, className, ...props }: Props) {
|
||||
return (
|
||||
<button
|
||||
className={clsx(className, styles.button, kind && styles[kind], size && styles[size], {
|
||||
|
||||
@@ -5,14 +5,16 @@ import type { User } from 'common/types';
|
||||
import * as userActions from 'store/user/actions';
|
||||
import { BASE_URL } from 'common/constants.config';
|
||||
|
||||
import OAuth from './oauth';
|
||||
import { OAuth } from './oauth';
|
||||
import * as api from './oauth.api';
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
useDispatch: () => jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('hooks/useTheme', () => () => 'light');
|
||||
jest.mock('hooks/useTheme', () => ({
|
||||
useTheme: () => 'light',
|
||||
}));
|
||||
|
||||
describe('<OAuth />', () => {
|
||||
it('should have permanent class name', () => {
|
||||
|
||||
@@ -3,17 +3,17 @@ import { useDispatch } from 'react-redux';
|
||||
import clsx from 'clsx';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { siteId } from 'common/settings';
|
||||
import type { OAuthProvider } from 'common/types';
|
||||
import useTheme from 'hooks/useTheme';
|
||||
import { siteId } from 'common/settings';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
import { setUser } from 'store/user/actions';
|
||||
|
||||
import messages from 'components/auth/auth.messsages';
|
||||
import { messages } from 'components/auth/auth.messsages';
|
||||
|
||||
import { getButtonVariant, getProviderData } from './oauth.utils';
|
||||
import { oauthSignin } from './oauth.api';
|
||||
import styles from './oauth.module.css';
|
||||
import { BASE_URL } from 'common/constants.config';
|
||||
import { getButtonVariant, getProviderData } from './oauth.utils';
|
||||
import styles from './oauth.module.css';
|
||||
|
||||
const location = encodeURIComponent(`${window.location.origin}${window.location.pathname}?selfClose`);
|
||||
|
||||
@@ -21,7 +21,7 @@ type Props = {
|
||||
providers: OAuthProvider[];
|
||||
};
|
||||
|
||||
export default function OAuthProviders({ providers }: Props) {
|
||||
export function OAuth({ providers }: Props) {
|
||||
const intl = useIntl();
|
||||
const dispath = useDispatch();
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OAuthProvider, Theme } from 'common/types';
|
||||
import capitalizeFirstLetter from 'utils/capitalize-first-letter';
|
||||
import { capitalizeFirstLetter } from 'utils/capitalize-first-letter';
|
||||
|
||||
import { OAUTH_DATA } from './oauth.consts';
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './auth';
|
||||
export * from './auth';
|
||||
|
||||
+3
-3
@@ -10,14 +10,14 @@ import { StoreState } from 'store';
|
||||
import { setUserSubscribed } from 'store/user/actions';
|
||||
import { sleep } from 'utils/sleep';
|
||||
import { extractErrorMessageFromResponse } from 'utils/errorUtils';
|
||||
import useTheme from 'hooks/useTheme';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
import { getHandleClickProps } from 'common/accessibility';
|
||||
import { emailVerificationForSubscribe, emailConfirmationForSubscribe, unsubscribeFromEmailUpdates } from 'common/api';
|
||||
import { Input } from 'components/input';
|
||||
import { Button } from 'components/button';
|
||||
import { Dropdown } from 'components/dropdown';
|
||||
import Preloader from 'components/preloader';
|
||||
import TextareaAutosize from 'components/textarea-autosize';
|
||||
import { Preloader } from 'components/preloader';
|
||||
import { TextareaAutosize } from 'components/textarea-autosize';
|
||||
import { isUserAnonymous } from 'utils/isUserAnonymous';
|
||||
import { isJwtExpired } from 'utils/jwt';
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { h, FunctionComponent } from 'preact';
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import useTheme from 'hooks/useTheme';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
import { siteId, url } from 'common/settings';
|
||||
import { BASE_URL, API_BASE } from 'common/constants';
|
||||
import { Dropdown, DropdownItem } from 'components/dropdown';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { user, anonymousUser } from '__stubs__/user';
|
||||
import { StaticStore } from 'common/static-store';
|
||||
import { LS_SAVED_COMMENT_VALUE } from 'common/constants';
|
||||
import * as localStorageModule from 'common/local-storage';
|
||||
import TextareaAutosize from 'components/textarea-autosize';
|
||||
import { TextareaAutosize } from 'components/textarea-autosize';
|
||||
|
||||
import { CommentForm, CommentFormProps, messages } from './comment-form';
|
||||
import { SubscribeByEmail } from './__subscribe-by-email';
|
||||
|
||||
@@ -10,15 +10,15 @@ import { isUserAnonymous } from 'utils/isUserAnonymous';
|
||||
import { sleep } from 'utils/sleep';
|
||||
import { replaceSelection } from 'utils/replaceSelection';
|
||||
import { Button } from 'components/button';
|
||||
import TextareaAutosize from 'components/textarea-autosize';
|
||||
import Auth from 'components/auth';
|
||||
import { TextareaAutosize } from 'components/textarea-autosize';
|
||||
import { Auth } from 'components/auth';
|
||||
import { getJsonItem, updateJsonItem } from 'common/local-storage';
|
||||
import { LS_SAVED_COMMENT_VALUE } from 'common/constants';
|
||||
|
||||
import { SubscribeByEmail } from './__subscribe-by-email';
|
||||
import { SubscribeByRSS } from './__subscribe-by-rss';
|
||||
|
||||
import MarkdownToolbar from './markdown-toolbar';
|
||||
import { MarkdownToolbar } from './markdown-toolbar';
|
||||
import { TextExpander } from './text-expander';
|
||||
|
||||
let textareaId = 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function BoldIcon() {
|
||||
export function BoldIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function CodeIcon() {
|
||||
export function CodeIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function HeaderIcon() {
|
||||
export function HeaderIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function ImageIcon() {
|
||||
export function ImageIcon() {
|
||||
return (
|
||||
<svg className="comment-form__toolbar-icon" width="11.25" height="15" viewBox="0 0 384 512" aria-hidden="true">
|
||||
<path
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function ItalicIcon() {
|
||||
export function ItalicIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function LinkIcon() {
|
||||
export function LinkIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function OrderedListIcon() {
|
||||
export function OrderedListIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function QuoteIcon() {
|
||||
export function QuoteIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { h } from 'preact';
|
||||
|
||||
export default function UnorderedListIcon() {
|
||||
export function UnorderedListIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="comment-form__toolbar-icon"
|
||||
|
||||
@@ -4,15 +4,15 @@ import { h, Component } from 'preact';
|
||||
import { defineMessages, IntlShape } from 'react-intl';
|
||||
|
||||
// TODO: Use SVGR
|
||||
import BoldIcon from './markdown-toolbar-icons/bold-icon';
|
||||
import HeaderIcon from './markdown-toolbar-icons/header-icon';
|
||||
import ItalicIcon from './markdown-toolbar-icons/italic-icon';
|
||||
import QuoteIcon from './markdown-toolbar-icons/quote-icon';
|
||||
import CodeIcon from './markdown-toolbar-icons/code-icon';
|
||||
import LinkIcon from './markdown-toolbar-icons/link-icon';
|
||||
import ImageIcon from './markdown-toolbar-icons/image-icon';
|
||||
import UnorderedListIcon from './markdown-toolbar-icons/unordered-list-icon';
|
||||
import OrderedListIcon from './markdown-toolbar-icons/ordered-list-icon';
|
||||
import { BoldIcon } from './markdown-toolbar-icons/bold-icon';
|
||||
import { HeaderIcon } from './markdown-toolbar-icons/header-icon';
|
||||
import { ItalicIcon } from './markdown-toolbar-icons/italic-icon';
|
||||
import { QuoteIcon } from './markdown-toolbar-icons/quote-icon';
|
||||
import { CodeIcon } from './markdown-toolbar-icons/code-icon';
|
||||
import { LinkIcon } from './markdown-toolbar-icons/link-icon';
|
||||
import { ImageIcon } from './markdown-toolbar-icons/image-icon';
|
||||
import { UnorderedListIcon } from './markdown-toolbar-icons/unordered-list-icon';
|
||||
import { OrderedListIcon } from './markdown-toolbar-icons/ordered-list-icon';
|
||||
|
||||
interface Props {
|
||||
intl: IntlShape;
|
||||
@@ -69,7 +69,7 @@ const messages = defineMessages({
|
||||
},
|
||||
});
|
||||
|
||||
export default class MarkdownToolbar extends Component<Props> {
|
||||
export class MarkdownToolbar extends Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.uploadImages = this.uploadImages.bind(this);
|
||||
|
||||
@@ -5,7 +5,7 @@ import clsx from 'clsx';
|
||||
|
||||
import { StaticStore } from 'common/static-store';
|
||||
import { Theme } from 'common/types';
|
||||
import useTheme from 'hooks/useTheme';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
|
||||
import styles from './text-expander.module.css';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { User, Comment as CommentType, PostInfo } from 'common/types';
|
||||
import { StaticStore } from 'common/static-store';
|
||||
import { sleep } from 'utils/sleep';
|
||||
|
||||
import Comment, { CommentProps } from './comment';
|
||||
import { Comment, CommentProps } from './comment';
|
||||
|
||||
const mount = <T extends JSX.Element>(component: T) =>
|
||||
enzymeMount(
|
||||
|
||||
@@ -5,8 +5,8 @@ import { getHandleClickProps } from 'common/accessibility';
|
||||
import { API_BASE, BASE_URL, COMMENT_NODE_CLASSNAME_PREFIX } from 'common/constants';
|
||||
|
||||
import { StaticStore } from 'common/static-store';
|
||||
import debounce from 'utils/debounce';
|
||||
import copy from 'common/copy';
|
||||
import { debounce } from 'utils/debounce';
|
||||
import { copy } from 'common/copy';
|
||||
import { Theme, BlockTTL, Comment as CommentType, PostInfo, User, CommentMode } from 'common/types';
|
||||
import { extractErrorMessageFromResponse, FetcherError } from 'utils/errorUtils';
|
||||
import { isUserAnonymous } from 'utils/isUserAnonymous';
|
||||
@@ -14,9 +14,9 @@ import { isUserAnonymous } from 'utils/isUserAnonymous';
|
||||
import { CommentFormProps } from 'components/comment-form';
|
||||
import { AvatarIcon } from 'components/avatar-icon';
|
||||
import { Button } from 'components/button';
|
||||
import Countdown from 'components/countdown';
|
||||
import { Countdown } from 'components/countdown';
|
||||
import { getPreview, uploadImage } from 'common/api';
|
||||
import postMessage from 'utils/postMessage';
|
||||
import { postMessage } from 'utils/postMessage';
|
||||
import { FormattedMessage, useIntl, IntlShape, defineMessages } from 'react-intl';
|
||||
import { getVoteMessage, VoteMessagesTypes } from './getVoteMessage';
|
||||
import { getBlockingDurations } from './getBlockingDurations';
|
||||
@@ -137,7 +137,7 @@ export interface State {
|
||||
initial: boolean;
|
||||
}
|
||||
|
||||
class Comment extends Component<CommentProps, State> {
|
||||
export class Comment extends Component<CommentProps, State> {
|
||||
votingPromise: Promise<unknown> = Promise.resolve();
|
||||
/** comment text node. Used in comment text copying */
|
||||
textNode = createRef<HTMLDivElement>();
|
||||
@@ -898,5 +898,3 @@ function FormatTime({ time }: { time: Date }) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Comment;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { StoreState } from 'store';
|
||||
import { addComment, removeComment, updateComment, setPinState, putVote, setCommentMode } from 'store/comments/actions';
|
||||
import { blockUser, unblockUser, hideUser, setVerifiedStatus } from 'store/user/actions';
|
||||
|
||||
import Comment, { CommentProps } from './comment';
|
||||
import { Comment, CommentProps } from './comment';
|
||||
import { getCommentMode } from 'store/comments/getters';
|
||||
import { uploadImage, getPreview } from 'common/api';
|
||||
import { getThreadIsCollapsed } from 'store/thread/getters';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './comment';
|
||||
export * from './comment';
|
||||
|
||||
@@ -12,7 +12,7 @@ interface State {
|
||||
}
|
||||
|
||||
/** Component which uses plain DOM mutation instead of rerendering react reactive reactivity */
|
||||
export default class Countdown extends Component<Props, State> {
|
||||
export class Countdown extends Component<Props, State> {
|
||||
elemRef = createRef<HTMLSpanElement>();
|
||||
intervalID?: number;
|
||||
constructor(props: Props) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import './__item/list-comments__item.css';
|
||||
|
||||
export { default } from './list-comments';
|
||||
export * from './list-comments';
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { h, FunctionComponent } from 'preact';
|
||||
import { h } from 'preact';
|
||||
import { useIntl } from 'react-intl';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import type { Comment as CommentType } from 'common/types';
|
||||
import Comment from 'components/comment';
|
||||
import { Comment } from 'components/comment';
|
||||
|
||||
import styles from './list-comments.module.css';
|
||||
|
||||
export type ListCommentsProps = {
|
||||
type Props = {
|
||||
comments: CommentType[];
|
||||
};
|
||||
|
||||
const ListComments: FunctionComponent<ListCommentsProps> = ({ comments = [] }) => {
|
||||
export function ListComments({ comments = [] }: Props) {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
@@ -33,6 +33,4 @@ const ListComments: FunctionComponent<ListCommentsProps> = ({ comments = [] }) =
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListComments;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default } from './preloader';
|
||||
export * from './preloader';
|
||||
|
||||
// all styles were moved to iframe.html
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { h, FunctionComponent } from 'preact';
|
||||
import { h } from 'preact';
|
||||
import b, { Mix } from 'bem-react-helper';
|
||||
|
||||
interface Props {
|
||||
type Props = {
|
||||
mix?: Mix;
|
||||
};
|
||||
|
||||
export function Preloader({ mix }: Props) {
|
||||
return <div className={b('preloader', { mix })} />;
|
||||
}
|
||||
|
||||
const Preloader: FunctionComponent<Props> = ({ mix }) => <div className={b('preloader', { mix })} />;
|
||||
|
||||
export default Preloader;
|
||||
|
||||
@@ -24,11 +24,11 @@ function getObserver(): { observer: IntersectionObserver; instanceMap: WeakMap<E
|
||||
return { observer, instanceMap };
|
||||
}
|
||||
|
||||
type InViewProps = {
|
||||
type Props = {
|
||||
children: <T>(props: { inView: boolean; ref: PropRef<T> }) => VNode;
|
||||
};
|
||||
|
||||
const InView = ({ children }: InViewProps) => {
|
||||
export function InView({ children }: Props) {
|
||||
const [inView, setInView] = useState(false);
|
||||
const ref = useRef<Component<unknown, unknown>>(null);
|
||||
|
||||
@@ -53,6 +53,4 @@ const InView = ({ children }: InViewProps) => {
|
||||
}, []);
|
||||
|
||||
return children({ inView, ref });
|
||||
};
|
||||
|
||||
export default InView;
|
||||
}
|
||||
|
||||
@@ -30,16 +30,16 @@ import { setCommentsReadOnlyState } from 'store/post-info/actions';
|
||||
import { setTheme } from 'store/theme/actions';
|
||||
|
||||
import { Button } from 'components/button';
|
||||
import Preloader from 'components/preloader';
|
||||
import Settings from 'components/settings';
|
||||
import AuthPanel from 'components/auth-panel';
|
||||
import { Preloader } from 'components/preloader';
|
||||
import { Settings } from 'components/settings';
|
||||
import { AuthPanel } from 'components/auth-panel';
|
||||
import { CommentForm } from 'components/comment-form';
|
||||
import { Thread } from 'components/thread';
|
||||
import { ConnectedComment as Comment } from 'components/comment/connected-comment';
|
||||
import { uploadImage, getPreview } from 'common/api';
|
||||
import { isUserAnonymous } from 'utils/isUserAnonymous';
|
||||
import { bindActions } from 'utils/actionBinder';
|
||||
import postMessage from 'utils/postMessage';
|
||||
import { postMessage } from 'utils/postMessage';
|
||||
import { useActions } from 'hooks/useAction';
|
||||
import { setCollapse } from 'store/thread/actions';
|
||||
import { logout } from 'components/auth/auth.api';
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import withTheme from '../with-theme';
|
||||
import Settings from './settings';
|
||||
|
||||
import './settings.css';
|
||||
|
||||
import './__action/settings__action.css';
|
||||
@@ -13,4 +10,4 @@ import './__user-id/settings__user-id.css';
|
||||
import './_theme/_dark/settings_theme_dark.css';
|
||||
import './_theme/_light/settings_theme_light.css';
|
||||
|
||||
export default withTheme(Settings);
|
||||
export * from './settings';
|
||||
|
||||
@@ -5,6 +5,7 @@ import { User, BlockedUser, Theme, BlockTTL } from 'common/types';
|
||||
import { getHandleClickProps } from 'common/accessibility';
|
||||
import { StoreState } from 'store';
|
||||
import { defineMessages, IntlShape, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
|
||||
interface Props {
|
||||
theme: Theme;
|
||||
@@ -49,7 +50,7 @@ const messages = defineMessages({
|
||||
},
|
||||
});
|
||||
|
||||
export default class Settings extends Component<Props, State> {
|
||||
class SettingsComponent extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
@@ -205,10 +206,15 @@ export default class Settings extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
export function Settings(props: Omit<Props, 'theme'>) {
|
||||
const theme = useTheme();
|
||||
|
||||
return <SettingsComponent theme={theme} {...props} />;
|
||||
}
|
||||
|
||||
function FormatTime({ time }: { time: Date }) {
|
||||
const intl = useIntl();
|
||||
const currentYear = new Date().getFullYear();
|
||||
// let's assume that if block ttl is more than 50 years then user blocked permanently
|
||||
if (time.getFullYear() - currentYear >= 50)
|
||||
return <FormattedMessage id="settings.permanently" defaultMessage="permanently" />;
|
||||
|
||||
@@ -2,32 +2,28 @@ import { h, JSX } from 'preact';
|
||||
import { forwardRef } from 'preact/compat';
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
|
||||
export type TextareaAutosizeProps = JSX.HTMLAttributes<HTMLTextAreaElement> & {};
|
||||
|
||||
function autoResize(textarea: HTMLTextAreaElement) {
|
||||
textarea.style.height = '';
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}
|
||||
|
||||
const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
|
||||
({ onInput, value, ...props }, externalRef) => {
|
||||
const localRef = useRef<HTMLTextAreaElement>();
|
||||
const ref = externalRef || localRef;
|
||||
type Props = JSX.HTMLAttributes<HTMLTextAreaElement>;
|
||||
|
||||
const handleInput: JSX.GenericEventHandler<HTMLTextAreaElement> = (evt) => {
|
||||
if (onInput) {
|
||||
return onInput.call(ref.current, evt);
|
||||
}
|
||||
export const TextareaAutosize = forwardRef<HTMLTextAreaElement, Props>(({ onInput, value, ...props }, externalRef) => {
|
||||
const localRef = useRef<HTMLTextAreaElement>();
|
||||
const ref = externalRef || localRef;
|
||||
|
||||
autoResize(ref.current);
|
||||
};
|
||||
const handleInput: JSX.GenericEventHandler<HTMLTextAreaElement> = (evt) => {
|
||||
if (onInput) {
|
||||
return onInput.call(ref.current, evt);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
autoResize(ref.current);
|
||||
}, [value, ref]);
|
||||
autoResize(ref.current);
|
||||
};
|
||||
|
||||
return <textarea {...props} onInput={handleInput} value={value} ref={ref} />;
|
||||
}
|
||||
);
|
||||
useEffect(() => {
|
||||
autoResize(ref.current);
|
||||
}, [value, ref]);
|
||||
|
||||
export default TextareaAutosize;
|
||||
return <textarea {...props} onInput={handleInput} value={value} ref={ref} />;
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getHandleClickProps } from 'common/accessibility';
|
||||
import { StoreState } from 'store';
|
||||
import { setCollapse } from 'store/thread/actions';
|
||||
import { getThreadIsCollapsed } from 'store/thread/getters';
|
||||
import InView from 'components/root/in-view/in-view';
|
||||
import { InView } from 'components/root/in-view/in-view';
|
||||
import { ConnectedComment as Comment } from 'components/comment/connected-comment';
|
||||
import { CommentForm } from 'components/comment-form';
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { h, Fragment } from 'preact';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { Comment as CommentType } from 'common/types';
|
||||
|
||||
import Comment from 'components/comment';
|
||||
import Preloader from 'components/preloader';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Comment } from 'components/comment';
|
||||
import { Preloader } from 'components/preloader';
|
||||
|
||||
const LastCommentsList = ({ comments, isLoading }: { comments: CommentType[]; isLoading: boolean }) => {
|
||||
type Props = {
|
||||
comments: CommentType[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
export function LastCommentsList({ comments, isLoading }: Props) {
|
||||
const intl = useIntl();
|
||||
|
||||
if (isLoading) {
|
||||
@@ -31,6 +36,4 @@ const LastCommentsList = ({ comments, isLoading }: { comments: CommentType[]; is
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LastCommentsList;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ import { Comment } from 'common/types';
|
||||
import { fetchInfo } from 'store/user-info/actions';
|
||||
import { userInfo } from 'common/user-info-settings';
|
||||
|
||||
import postMessage from 'utils/postMessage';
|
||||
import { postMessage } from 'utils/postMessage';
|
||||
import { bindActions } from 'utils/actionBinder';
|
||||
import { useActions } from 'hooks/useAction';
|
||||
|
||||
import { AvatarIcon } from '../avatar-icon';
|
||||
import LastCommentsList from './last-comments-list';
|
||||
import { LastCommentsList } from './last-comments-list';
|
||||
|
||||
const boundActions = bindActions({ fetchInfo });
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { h, FunctionComponent, ComponentType } from 'preact';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { StoreState } from 'store';
|
||||
import { Theme } from 'common/types';
|
||||
|
||||
const themeSelector = (state: StoreState) => state.theme;
|
||||
|
||||
/**
|
||||
* Connects redux theme property to component's
|
||||
*/
|
||||
function withTheme<P extends { theme: Theme }>(PlainComponent: ComponentType<P>) {
|
||||
const C: FunctionComponent<Omit<P, 'theme'>> = (props) => {
|
||||
const theme = useSelector(themeSelector);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return <PlainComponent theme={theme} {...(props as any)} />;
|
||||
};
|
||||
C.displayName = `withTheme(${PlainComponent.displayName || PlainComponent.name})`;
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
export default withTheme;
|
||||
@@ -3,7 +3,7 @@ import { useSelector } from 'react-redux';
|
||||
import { StoreState } from 'store';
|
||||
import { Theme } from 'common/types';
|
||||
|
||||
export default function useTheme() {
|
||||
export function useTheme() {
|
||||
const theme = useSelector<StoreState, Theme>(({ theme }) => theme);
|
||||
|
||||
return theme;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { h, render } from 'preact';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import getLastComments from 'common/api.getLastComments';
|
||||
import { getLastComments } from 'common/api.getLastComments';
|
||||
import { BASE_URL } from 'common/constants.config';
|
||||
import { loadLocale } from 'utils/loadLocale';
|
||||
import { getLocale } from 'utils/getLocale';
|
||||
import ListComments from 'components/list-comments';
|
||||
import { ListComments } from 'components/list-comments';
|
||||
|
||||
const LAST_COMMENTS_NODE_CLASSNAME = 'remark42__last-comments';
|
||||
const DEFAULT_LAST_COMMENTS_MAX = 15;
|
||||
|
||||
@@ -7,13 +7,13 @@ import { loadLocale } from 'utils/loadLocale';
|
||||
import { getLocale } from 'utils/getLocale';
|
||||
import { ConnectedRoot } from 'components/root';
|
||||
import { UserInfo } from 'components/user-info';
|
||||
import reduxStore from 'store';
|
||||
import { store } from 'store';
|
||||
import { NODE_ID, BASE_URL } from 'common/constants';
|
||||
import { StaticStore } from 'common/static-store';
|
||||
import { getConfig } from 'common/api';
|
||||
import { fetchHiddenUsers } from 'store/user/actions';
|
||||
import { restoreCollapsedThreads } from 'store/thread/actions';
|
||||
import parseQuery from 'utils/parseQuery';
|
||||
import { parseQuery } from 'utils/parseQuery';
|
||||
import { parseBooleansFromDictionary } from 'utils/parse-booleans-from-dictionary';
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
@@ -34,7 +34,7 @@ async function init(): Promise<void> {
|
||||
const params = parseQuery<{ page?: string; locale?: string; simple_view?: boolean }>();
|
||||
const locale = getLocale(params);
|
||||
const messages = await loadLocale(locale).catch(() => ({}));
|
||||
const boundActions = bindActionCreators({ fetchHiddenUsers, restoreCollapsedThreads }, reduxStore.dispatch);
|
||||
const boundActions = bindActionCreators({ fetchHiddenUsers, restoreCollapsedThreads }, store.dispatch);
|
||||
|
||||
boundActions.fetchHiddenUsers();
|
||||
boundActions.restoreCollapsedThreads();
|
||||
@@ -45,7 +45,7 @@ async function init(): Promise<void> {
|
||||
|
||||
render(
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
<Provider store={reduxStore}>{params.page === 'user-info' ? <UserInfo /> : <ConnectedRoot />}</Provider>
|
||||
<Provider store={store}>{params.page === 'user-info' ? <UserInfo /> : <ConnectedRoot />}</Provider>
|
||||
</IntlProvider>,
|
||||
node
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import stubStore from '__stubs__/store';
|
||||
import { mockStore } from '__stubs__/store';
|
||||
import { LS_SORT_KEY } from 'common/constants';
|
||||
|
||||
import { updateSorting } from './actions';
|
||||
@@ -7,7 +7,7 @@ import { COMMENTS_SET_SORT } from './types';
|
||||
describe('Store comments actions', () => {
|
||||
it('should save last sort to localstorage', async () => {
|
||||
const newSort = '+controversy';
|
||||
const store = stubStore({
|
||||
const store = mockStore({
|
||||
comments: {
|
||||
sort: '+active',
|
||||
},
|
||||
|
||||
@@ -198,7 +198,7 @@ function sort(state: Sorting = getInitialSort(), action: COMMENTS_SET_SORT_ACTIO
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
export const comments = combineReducers({
|
||||
sort,
|
||||
isFetching,
|
||||
topComments,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createStore, applyMiddleware, AnyAction, compose, combineReducers } from 'redux';
|
||||
import thunk, { ThunkAction, ThunkDispatch } from 'redux-thunk';
|
||||
import storeReducers from './reducers';
|
||||
import { rootProvider } from './reducers';
|
||||
import { ACTIONS } from './actions';
|
||||
|
||||
const reducers = combineReducers(storeReducers);
|
||||
const reducers = combineReducers(rootProvider);
|
||||
|
||||
export type StoreState = ReturnType<typeof reducers>;
|
||||
|
||||
@@ -24,11 +24,10 @@ const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
: compose;
|
||||
const store = createStore(reducers, composeEnhancers(middleware));
|
||||
|
||||
export const store = createStore(reducers, composeEnhancers(middleware));
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(window as any).ReduxStore = store;
|
||||
}
|
||||
|
||||
export default store;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import comments from './comments/reducers';
|
||||
import * as comments from './comments/reducers';
|
||||
import * as postInfo from './post-info/reducers';
|
||||
import * as theme from './theme/reducers';
|
||||
import * as user from './user/reducers';
|
||||
@@ -6,13 +6,11 @@ import * as userInfo from './user-info/reducers';
|
||||
import * as thread from './thread/reducers';
|
||||
|
||||
/** Merged store reducers */
|
||||
const rootProvider = {
|
||||
comments,
|
||||
export const rootProvider = {
|
||||
...comments,
|
||||
...theme,
|
||||
...postInfo,
|
||||
...userInfo,
|
||||
...thread,
|
||||
...user,
|
||||
};
|
||||
|
||||
export default rootProvider;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import stubStore from '__stubs__/store';
|
||||
import { mockStore } from '__stubs__/store';
|
||||
import { User } from 'common/types';
|
||||
import { LS_HIDDEN_USERS_KEY } from 'common/constants';
|
||||
import { COMMENTS_PATCH } from 'store/comments/types';
|
||||
@@ -9,7 +9,7 @@ import { USER_UNHIDE, USER_HIDE, USER_UNBAN, USER_BANLIST_SET, USER_HIDELIST_SET
|
||||
|
||||
describe('store user actions', () => {
|
||||
test('fetchBlockedUsers', async () => {
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
await store.dispatch(fetchBlockedUsers());
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('store user actions', () => {
|
||||
});
|
||||
|
||||
test('fetchHiddenUsers', async () => {
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
await store.dispatch(fetchHiddenUsers());
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('store user actions', () => {
|
||||
|
||||
test('fetchHiddenUsers with data', async () => {
|
||||
const data = { '1': { id: '1' }, '2': { id: '2' } };
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
localStorage.setItem(LS_HIDDEN_USERS_KEY, JSON.stringify(data));
|
||||
await store.dispatch(fetchHiddenUsers());
|
||||
@@ -41,7 +41,7 @@ describe('store user actions', () => {
|
||||
});
|
||||
|
||||
test('setVerifiedStatus', async () => {
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
await store.dispatch(setVerifiedStatus('1', true));
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('store user actions', () => {
|
||||
});
|
||||
|
||||
test('unblockUser', async () => {
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
await store.dispatch(unblockUser('1'));
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('store user actions', () => {
|
||||
});
|
||||
|
||||
test('hideUser', async () => {
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
await store.dispatch(hideUser({ id: '1' } as User));
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('store user actions', () => {
|
||||
});
|
||||
|
||||
test('unhideUser', async () => {
|
||||
const store = stubStore(INITIAL_STORE);
|
||||
const store = mockStore(INITIAL_STORE);
|
||||
|
||||
localStorage.setItem(LS_HIDDEN_USERS_KEY, JSON.stringify({ '1': { id: '1' } }));
|
||||
await store.dispatch(unhideUser('1'));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as api from 'common/api';
|
||||
import { User, BlockedUser, BlockTTL } from 'common/types';
|
||||
import { ttlToTime } from 'utils/ttl-to-time';
|
||||
import getHiddenUsers from 'utils/get-hidden-users';
|
||||
import { getHiddenUsers } from 'utils/get-hidden-users';
|
||||
import { LS_HIDDEN_USERS_KEY } from 'common/constants';
|
||||
import { setItem } from 'common/local-storage';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import capitalizeFirstLetter from './capitalize-first-letter';
|
||||
import { capitalizeFirstLetter } from './capitalize-first-letter';
|
||||
|
||||
it('should capitalize first letter', () => {
|
||||
expect(capitalizeFirstLetter('one')).toBe('One');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default function capitalizeFirstLetter(str: string): string {
|
||||
export function capitalizeFirstLetter(str: string): string {
|
||||
return `${str.charAt(0).toLocaleUpperCase()}${str.slice(1)}`;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
type FnType<T extends unknown[]> = (...args: T) => unknown;
|
||||
|
||||
export default function debounce<T extends unknown[]>(
|
||||
fn: FnType<T>,
|
||||
wait = 1000
|
||||
): (...args: Parameters<FnType<T>>) => void {
|
||||
export function debounce<T extends unknown[]>(fn: FnType<T>, wait = 1000): (...args: Parameters<FnType<T>>) => void {
|
||||
let timeout: number | undefined;
|
||||
|
||||
return function (this: unknown, ...args): void {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LS_HIDDEN_USERS_KEY } from 'common/constants';
|
||||
|
||||
import getHiddenUsers from './get-hidden-users';
|
||||
import { getHiddenUsers } from './get-hidden-users';
|
||||
|
||||
describe('getHiddenUsers', () => {
|
||||
it('should get hidden users from local storage', async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { User } from 'common/types';
|
||||
import { getItem } from 'common/local-storage';
|
||||
import { LS_HIDDEN_USERS_KEY } from 'common/constants';
|
||||
|
||||
export default function getHiddenUsers() {
|
||||
export function getHiddenUsers() {
|
||||
try {
|
||||
const hiddenUsers: Record<string, User> = JSON.parse(getItem(LS_HIDDEN_USERS_KEY) || '{}');
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
export default function noop(): void {}
|
||||
@@ -1,4 +1,4 @@
|
||||
import parseQuery from './parseQuery';
|
||||
import { parseQuery } from './parseQuery';
|
||||
|
||||
describe('parseQuery', () => {
|
||||
it('should return empty object', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** converts window.location.search into object */
|
||||
|
||||
export default function parseQuery<T extends {}>(search: string = window.location.search): T {
|
||||
export function parseQuery<T extends {}>(search: string = window.location.search): T {
|
||||
const params: { [key: string]: string } = {};
|
||||
new URLSearchParams(search).forEach((value: string, key: string) => {
|
||||
params[key] = value;
|
||||
|
||||
@@ -15,7 +15,7 @@ export type Message =
|
||||
*
|
||||
* @returns request success of fail
|
||||
*/
|
||||
export default function postMessage(data: Message): boolean {
|
||||
export function postMessage(data: Message): boolean {
|
||||
if (!window.parent || window.parent === window) return false;
|
||||
window.parent.postMessage(JSON.stringify(data), '*');
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function shallowCompare<T extends Record<string, unknown>>(a: T, b: T): boolean {
|
||||
export function shallowCompare<T extends Record<string, unknown>>(a: T, b: T): boolean {
|
||||
const entriesA = Object.entries(a);
|
||||
const keysB = Object.keys(b);
|
||||
if (entriesA.length !== keysB.length) return false;
|
||||
|
||||
Reference in New Issue
Block a user