diff --git a/frontend/app/@types/define-modules.d.ts b/frontend/app/@types/define-modules.d.ts new file mode 100644 index 00000000..672a166a --- /dev/null +++ b/frontend/app/@types/define-modules.d.ts @@ -0,0 +1,2 @@ +declare module 'intersection-observer'; +declare module 'whatwg-fetch'; diff --git a/frontend/app/@types/global.d.ts b/frontend/app/@types/global.d.ts new file mode 100644 index 00000000..d891e2b8 --- /dev/null +++ b/frontend/app/@types/global.d.ts @@ -0,0 +1,11 @@ +import { Theme } from '@app/common/types'; +import { CommentsConfig } from '@app/common/config-types'; + +declare global { + interface Window { + remark_config: CommentsConfig; + REMARK42: { + changeTheme(theme: Theme): void; + }; + } +} diff --git a/frontend/app/common/api.ts b/frontend/app/common/api.ts index 96c89812..bd81e389 100644 --- a/frontend/app/common/api.ts +++ b/frontend/app/common/api.ts @@ -292,31 +292,3 @@ export const emailConfirmationForSubscribe = (token: string) => * Decline current subscription to updates */ export const unsubscribeFromEmailUpdates = () => fetcher.delete({ url: `/email`, withCredentials: true }); - -export default { - logIn, - logOut, - getConfig, - getPostComments, - getCommentsCount, - getComment, - getUserComments, - putCommentVote, - addComment, - updateComment, - removeMyComment, - getUser, - getPreview, - - pinComment, - unpinComment, - setVerifyStatus: setVerifiedStatus, - removeVerifyStatus: removeVerifiedStatus, - removeComment, - blockUser, - unblockUser, - getBlocked, - disableComments, - enableComments, - uploadImage, -}; diff --git a/frontend/app/common/constants.config.ts b/frontend/app/common/constants.config.ts index c2b42f95..6bdbac53 100644 --- a/frontend/app/common/constants.config.ts +++ b/frontend/app/common/constants.config.ts @@ -1,7 +1,5 @@ -export const BASE_URL: string = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ((window as any).remark_config && (window as any).remark_config.host) || process.env.REMARK_URL!; -export const NODE_ID: string = process.env.REMARK_NODE!; +export const BASE_URL = (window.remark_config && window.remark_config.host) || process.env.REMARK_URL!; +export const NODE_ID = process.env.REMARK_NODE!; export const API_BASE = '/api/v1'; export const COMMENT_NODE_CLASSNAME_PREFIX = 'remark42__comment-'; export const COUNTER_NODE_CLASSNAME = 'remark42__counter'; diff --git a/frontend/app/common/constants.ts b/frontend/app/common/constants.ts index 4fd2de13..6f97da88 100644 --- a/frontend/app/common/constants.ts +++ b/frontend/app/common/constants.ts @@ -1,10 +1,6 @@ import { Sorting, AuthProvider, Theme } from './types'; -import * as configConstant from './constants.config'; -export const BASE_URL = configConstant.BASE_URL; -export const API_BASE = configConstant.API_BASE; -export const NODE_ID = configConstant.NODE_ID; -export const COMMENT_NODE_CLASSNAME_PREFIX = configConstant.COMMENT_NODE_CLASSNAME_PREFIX; +export { BASE_URL, API_BASE, NODE_ID, COMMENT_NODE_CLASSNAME_PREFIX } from './constants.config'; export const LAST_COMMENTS_NODE_CLASSNAME = 'remark42__last-comments'; export const MAX_SHOWN_ROOT_COMMENTS = 10; @@ -28,8 +24,8 @@ export const LS_COLLAPSE_KEY = '__remarkCollapsed'; /** locastorage key for hidden users */ export const LS_HIDDEN_USERS_KEY = '__remarkHiddenUsers'; -/** cookie key under which sort preference resides */ -export const COOKIE_SORT_KEY = 'remarkSort'; +/** localstorage key under which sort preference resides */ +export const LS_SORT_KEY = '__remarkSort'; export const THEMES: Theme[] = ['light', 'dark']; diff --git a/frontend/app/common/polyfills.ts b/frontend/app/common/polyfills.ts index 1d8cea5c..f48f9c7c 100644 --- a/frontend/app/common/polyfills.ts +++ b/frontend/app/common/polyfills.ts @@ -5,27 +5,27 @@ import '@webcomponents/custom-elements'; import './closest-polyfill'; export default async function loadPolyfills() { - const fillCoreJs = async () => { + function fillCoreJs() { if ( 'startsWith' in String.prototype && 'endsWith' in String.prototype && 'includes' in Array.prototype && 'assign' in Object && 'keys' in Object - ) + ) { return; + } - await import(/* webpackChunkName: "core-js" */ 'core-js').then(); - return; - }; + return import(/* webpackChunkName: "core-js" */ 'core-js'); + } - const fillFetch = async () => { + function fillFetch() { if ('fetch' in window) return; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await import(/* webpackChunkName: "whatwg-fetch" */ 'whatwg-fetch' as any).then(); - }; - const fillIntersectionObserver = async () => { + return import(/* webpackChunkName: "whatwg-fetch" */ 'whatwg-fetch'); + } + + function fillIntersectionObserver() { if ( 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && @@ -34,9 +34,8 @@ export default async function loadPolyfills() { return; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await import(/* webpackChunkName: "intersection-observer" */ 'intersection-observer' as any).then(); - }; + return import(/* webpackChunkName: "intersection-observer" */ 'intersection-observer'); + } await Promise.all([fillCoreJs(), fillFetch(), fillIntersectionObserver()]); return; diff --git a/frontend/app/common/settings.ts b/frontend/app/common/settings.ts index 0816faf1..339211bf 100644 --- a/frontend/app/common/settings.ts +++ b/frontend/app/common/settings.ts @@ -1,5 +1,6 @@ import { Theme } from './types'; import { THEMES, MAX_SHOWN_ROOT_COMMENTS } from './constants'; +import parseQuery from '@app/utils/parseQuery'; export interface QuerySettingsType { site_id?: string; @@ -11,15 +12,7 @@ export interface QuerySettingsType { token?: string; } -export const querySettings: Partial = - window.location.search - .substr(1) - .split('&') - .reduce<{ [key: string]: string }>((acc, param) => { - const pair = param.split('='); - acc[pair[0]] = decodeURIComponent(pair[1]); - return acc; - }, {}) || {}; +export const querySettings: Partial = parseQuery(); if (querySettings.max_shown_comments) { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/frontend/app/common/user-info-settings.ts b/frontend/app/common/user-info-settings.ts index fd33dfd4..5fee1f02 100644 --- a/frontend/app/common/user-info-settings.ts +++ b/frontend/app/common/user-info-settings.ts @@ -1,23 +1,10 @@ import { UserInfo } from './types'; +import parseQuery from '@app/utils/parseQuery'; -export const userInfo: Partial = - window.location.search - .substr(1) - .split('&') - .reduce<{ [key: string]: string }>((acc, param) => { - const pair = param.split('='); - acc[pair[0]] = decodeURIComponent(pair[1]); - return acc; - }, {}) || {}; +export const userInfo: Partial = parseQuery(); // eslint-disable-next-line @typescript-eslint/no-explicit-any -if (((userInfo.isDefaultPicture as any) as string) !== '1') { - userInfo.isDefaultPicture = false; -} else { - userInfo.isDefaultPicture = true; -} - +export const isDefaultPicture = ((userInfo.isDefaultPicture as any) as string) !== '1'; export const id = userInfo.id; export const name = userInfo.name; -export const isDefaultPicture = userInfo.isDefaultPicture; export const picture = userInfo.picture; diff --git a/frontend/app/components/auth-panel/auth-panel.tsx b/frontend/app/components/auth-panel/auth-panel.tsx index 4a4a1e02..1c1029ff 100644 --- a/frontend/app/components/auth-panel/auth-panel.tsx +++ b/frontend/app/components/auth-panel/auth-panel.tsx @@ -32,8 +32,7 @@ interface PropsWithoutIntl { onSortChange(s: Sorting): Promise; onSignIn(p: AuthProvider): Promise; onSignOut(): Promise; - onCommentsEnable(): Promise; - onCommentsDisable(): Promise; + onCommentsChangeReadOnlyMode(readOnly: boolean): Promise; onBlockedUsersShow(): void; onBlockedUsersHide(): void; } @@ -76,7 +75,6 @@ export class AuthPanel extends Component { }; this.toggleBlockedVisibility = this.toggleBlockedVisibility.bind(this); - this.toggleCommentsAvailability = this.toggleCommentsAvailability.bind(this); this.onSortChange = this.onSortChange.bind(this); this.onSignIn = this.onSignIn.bind(this); this.onEmailSignIn = this.onEmailSignIn.bind(this); @@ -108,9 +106,7 @@ export class AuthPanel extends Component { } onSortChange(e: Event) { - if (this.props.onSortChange) { - this.props.onSortChange((e.target! as HTMLOptionElement).value as Sorting); - } + this.props.onSortChange((e.target! as HTMLOptionElement).value as Sorting); } onSortFocus = () => { @@ -131,13 +127,9 @@ export class AuthPanel extends Component { this.setState({ isBlockedVisible: !this.state.isBlockedVisible }); } - toggleCommentsAvailability() { - if (this.props.isCommentsDisabled) { - this.props.onCommentsEnable && this.props.onCommentsEnable(); - } else { - this.props.onCommentsDisable && this.props.onCommentsDisable(); - } - } + toggleCommentsAvailability = () => { + this.props.onCommentsChangeReadOnlyMode(!this.props.isCommentsDisabled); + }; toggleUserInfoVisibility() { const user = this.props.user; @@ -368,7 +360,7 @@ export class AuthPanel extends Component {