cleanup of the frontend code
- replace undocumented `substr` with `substring` - remove unused code - inline a few variables - simplify ifs when possible - improve saveCollapsedComments documentation - cleanup the unused imports - remove unused variables and types
This commit is contained in:
@@ -9,8 +9,6 @@ export const getConfig = (): Promise<Config> => apiFetcher.get('/config');
|
||||
|
||||
export const getPostComments = (sort: Sorting) => apiFetcher.get<Tree>('/find', { url, sort, format: 'tree' });
|
||||
|
||||
export const getComment = (id: Comment['id']): Promise<Comment> => apiFetcher.get(`/id/${id}`, { url });
|
||||
|
||||
export const getUserComments = (
|
||||
userId: User['id'],
|
||||
config: { limit: number; skip?: number } = { limit: 10, skip: 0 }
|
||||
|
||||
@@ -43,7 +43,3 @@ export function getCookie(name: string) {
|
||||
|
||||
return matches ? decodeURIComponent(matches[1]) : undefined;
|
||||
}
|
||||
|
||||
export function deleteCookie(name: string) {
|
||||
setCookie(name, '', { expires: -1 });
|
||||
}
|
||||
|
||||
@@ -69,9 +69,7 @@ const createFetcher = (baseUrl: string = ''): Methods => {
|
||||
// TODO: it should be clarified when frontend gets this header and what could be in it to simplify this logic and cover by tests
|
||||
const date = (res.headers.has('date') && res.headers.get('date')) || '';
|
||||
const timestamp = isNaN(Date.parse(date)) ? 0 : Date.parse(date);
|
||||
const timeDiff = (new Date().getTime() - timestamp) / 1000;
|
||||
|
||||
StaticStore.serverClientTimeDiff = timeDiff;
|
||||
StaticStore.serverClientTimeDiff = (new Date().getTime() - timestamp) / 1000;
|
||||
|
||||
// backend could update jwt in any time. so, we should handle it
|
||||
if (res.headers.has(JWT_HEADER)) {
|
||||
|
||||
@@ -72,12 +72,6 @@ export interface Comment {
|
||||
*/
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export interface CommentsResponse {
|
||||
comments: Comment[];
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
comment: Comment;
|
||||
replies?: Node[];
|
||||
|
||||
@@ -123,7 +123,7 @@ export function useErrorMessage(): [string | null, (e: unknown) => void] {
|
||||
}
|
||||
|
||||
const errorReason =
|
||||
err instanceof RequestError || (isObject(err) && typeof (err as Record<string, string>).error === 'string')
|
||||
err instanceof RequestError || isObject(err)
|
||||
? (err as Record<'error', string>).error
|
||||
: err instanceof Error
|
||||
? err.message
|
||||
|
||||
-1
@@ -26,7 +26,6 @@ const emailRegexp = /[^@]+@[^.]+\..+/;
|
||||
enum Step {
|
||||
Email,
|
||||
Token,
|
||||
Final,
|
||||
Close,
|
||||
Subscribed,
|
||||
Unsubscribed,
|
||||
|
||||
@@ -112,7 +112,7 @@ export class CommentForm extends Component<Props, State> {
|
||||
|
||||
onInput = (e: Event) => {
|
||||
const { value } = e.target as HTMLInputElement;
|
||||
const text = value.substr(0, StaticStore.config.max_comment_size);
|
||||
const text = value.substring(0, StaticStore.config.max_comment_size);
|
||||
|
||||
updatePersistedComments(this.props.id, value);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('<CommentVote />', () => {
|
||||
['downvote', -1, 'Vote down', 'Vote up', 'downVoteButtonActive'],
|
||||
])(
|
||||
'should go throught voting process and communicate with store when %s button is clicked',
|
||||
async (_, increment, activeButtonText, secondButtonText, activeButtonClass) => {
|
||||
async (_, increment, activeButtonText, secondButtonText) => {
|
||||
const putCommentVoteSpy = jest
|
||||
.spyOn(api, 'putCommentVote')
|
||||
.mockImplementationOnce(({ vote }) => Promise.resolve({ id: '1', score: 10 + vote }));
|
||||
|
||||
@@ -21,7 +21,7 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export function CommentVotes({ id, votes, vote, disabled, controversy = 0 }: Props) {
|
||||
export function CommentVotes({ id, votes, vote, disabled }: Props) {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const [loadingState, setLoadingState] = useState<{ vote: number; votes: number } | null>(null);
|
||||
|
||||
@@ -62,7 +62,6 @@ export interface State {
|
||||
}
|
||||
|
||||
export class Comment extends Component<CommentProps, State> {
|
||||
votingPromise: Promise<unknown> = Promise.resolve();
|
||||
/** comment text node. Used in comment text copying */
|
||||
textNode = createRef<HTMLDivElement>();
|
||||
|
||||
@@ -175,16 +174,6 @@ export class Comment extends Component<CommentProps, State> {
|
||||
}
|
||||
};
|
||||
|
||||
onBlockUserClick = (evt: Event) => {
|
||||
const target = evt.currentTarget;
|
||||
|
||||
if (target instanceof HTMLOptionElement) {
|
||||
// we have to debounce the blockUser function calls otherwise it will be
|
||||
// called 2 times (by change event and by blur event)
|
||||
this.blockUser(target.value as BlockTTL);
|
||||
}
|
||||
};
|
||||
|
||||
blockUser = debounce((ttl: BlockTTL): void => {
|
||||
const { user } = this.props.data;
|
||||
const blockingDurations = getBlockingDurations(this.props.intl);
|
||||
@@ -427,7 +416,7 @@ export class Comment extends Component<CommentProps, State> {
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{!isAdmin && !!o.user.verified && props.view !== 'user' && (
|
||||
{!isAdmin && o.user.verified && props.view !== 'user' && (
|
||||
<VerificationIcon className={styles.verificationIcon} title={intl.formatMessage(messages.verifiedUser)} />
|
||||
)}
|
||||
{o.user.paid_sub && (
|
||||
@@ -560,7 +549,7 @@ function getTextSnippet(html: string) {
|
||||
tmp.innerHTML = html.replace('</p><p>', ' ');
|
||||
|
||||
const result = tmp.innerText || '';
|
||||
const snippet = result.substr(0, LENGTH);
|
||||
const snippet = result.substring(0, LENGTH);
|
||||
|
||||
return snippet.length === LENGTH && result.length !== LENGTH ? `${snippet}...` : snippet;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { COMMENT_NODE_CLASSNAME_PREFIX, MAX_SHOWN_ROOT_COMMENTS, THEMES, IS_MOBI
|
||||
import { maxShownComments, url } from 'common/settings';
|
||||
|
||||
import {
|
||||
setUser,
|
||||
fetchUser,
|
||||
blockUser,
|
||||
unblockUser,
|
||||
@@ -20,7 +19,7 @@ import {
|
||||
unhideUser,
|
||||
signout,
|
||||
} from 'store/user/actions';
|
||||
import { fetchComments, addComment, updateComment, unsetCommentMode } from 'store/comments/actions';
|
||||
import { fetchComments, addComment, updateComment } from 'store/comments/actions';
|
||||
import { setCommentsReadOnlyState } from 'store/post-info/actions';
|
||||
import { setTheme } from 'store/theme/actions';
|
||||
|
||||
@@ -73,7 +72,6 @@ const mapStateToProps = (state: StoreState) => ({
|
||||
|
||||
const boundActions = bindActions({
|
||||
fetchComments,
|
||||
setUser,
|
||||
fetchUser,
|
||||
fetchBlockedUsers,
|
||||
setTheme,
|
||||
@@ -85,7 +83,6 @@ const boundActions = bindActions({
|
||||
addComment,
|
||||
updateComment,
|
||||
setCollapse,
|
||||
unsetCommentMode,
|
||||
signout,
|
||||
});
|
||||
|
||||
|
||||
@@ -91,8 +91,7 @@ class SettingsComponent extends Component<Props, State> {
|
||||
};
|
||||
|
||||
__isUserHidden = (user: User): boolean => {
|
||||
if (this.state.unhiddenUsers.indexOf(user.id) === -1) return true;
|
||||
return false;
|
||||
return !this.state.unhiddenUsers.includes(user.id);
|
||||
};
|
||||
|
||||
render({ user, theme }: Props, { blockedUsers, unblockedUsers, unhiddenUsers }: State) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { h, JSX } from 'preact';
|
||||
import { forwardRef } from 'preact/compat';
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
|
||||
function autoResize(textarea: HTMLTextAreaElement, onResize?: () => void) {
|
||||
function autoResize(textarea: HTMLTextAreaElement) {
|
||||
textarea.style.height = '';
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,5 @@ import { StoreState } from 'store';
|
||||
import { Theme } from 'common/types';
|
||||
|
||||
export function useTheme() {
|
||||
const theme = useSelector<StoreState, Theme>(({ theme }) => theme);
|
||||
|
||||
return theme;
|
||||
return useSelector<StoreState, Theme>(({ theme }) => theme);
|
||||
}
|
||||
|
||||
-15
@@ -33,19 +33,4 @@ declare global {
|
||||
| undefined;
|
||||
};
|
||||
}
|
||||
|
||||
namespace NodeJS {
|
||||
interface Global {
|
||||
Headers: typeof Headers;
|
||||
localStorage: typeof Storage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable responsive for dynamic setting public path for
|
||||
* assets. Dynamic imports with relative url will be resolved over this path.
|
||||
*
|
||||
* https://webpack.js.org/guides/public-path/#on-the-fly
|
||||
*/
|
||||
declare let __webpack_public_path__: string;
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
export function bench<T>(fn: () => T, label = 'bench'): T {
|
||||
const d = performance.now();
|
||||
const r = fn();
|
||||
const dd = performance.now();
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(label, dd - d);
|
||||
return r;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Node } from 'common/types';
|
||||
|
||||
/**
|
||||
* Function to debug node tree.
|
||||
*/
|
||||
export function debugNode(n: Node): Node {
|
||||
const d = (n: Node, level: number): void => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`${' '.repeat(level)}${n.comment.text.trim()} | id: ${n.comment.id} | delete: ${n.comment.delete} | pin: ${
|
||||
n.comment.pin
|
||||
}`
|
||||
);
|
||||
if (n.replies) {
|
||||
for (const node of n.replies) {
|
||||
d(node, level + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
d(n, 0);
|
||||
return n;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
/** type that makes certain properties of type optional */
|
||||
export type Derequire<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> & Partial<Pick<T, K>>;
|
||||
@@ -1,3 +1,3 @@
|
||||
export function replaceSelection(text: string, selection: [number, number], replacement: string): string {
|
||||
return text.substr(0, selection[0]) + replacement + text.substr(selection[1]);
|
||||
return text.substring(0, selection[0]) + replacement + text.substring(selection[1]);
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
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;
|
||||
for (const [key, value] of entriesA) {
|
||||
if (value !== b[key]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -52,7 +52,7 @@
|
||||
var query = (function () {
|
||||
if (window.location.search.length === 0) return {};
|
||||
return window.location.search
|
||||
.substr(1)
|
||||
.substring(1)
|
||||
.split('&')
|
||||
.map(function (item) {
|
||||
return item.split('=');
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
window.location.search.length < 2
|
||||
? {}
|
||||
: window.location.search
|
||||
.substr(1)
|
||||
.substring(1)
|
||||
.split('&')
|
||||
.reduce(function (c, x) {
|
||||
var splitted = x.split('=');
|
||||
|
||||
Reference in New Issue
Block a user