@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
'markdown-toolbar': {
|
||||
|
||||
@@ -81,7 +81,9 @@ const fetcher = methods.reduce<Partial<FetcherObject>>((acc, method) => {
|
||||
try {
|
||||
err = JSON.parse(text);
|
||||
} catch (e) {
|
||||
throw text;
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
throw 'Something went wrong.';
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
@@ -12,7 +12,8 @@ import { StaticStore } from '@app/common/static_store';
|
||||
import debounce from '@app/utils/debounce';
|
||||
import copy from '@app/common/copy';
|
||||
import { Theme, BlockTTL, Comment as CommentType, PostInfo, User, CommentMode, Image } from '@app/common/types';
|
||||
import { extractErrorMessageFromResponse, FetcherResponse } from '@app/utils/errorUtils';
|
||||
import { extractErrorMessageFromResponse, FetcherError } from '@app/utils/errorUtils';
|
||||
import { isUserAnonymous } from '@app/utils/isUserAnonymous';
|
||||
|
||||
import { Input } from '@app/components/input';
|
||||
import { AvatarIcon } from '@app/components/avatar-icon';
|
||||
@@ -213,7 +214,7 @@ export class Comment extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
handleVoteError(e: FetcherResponse, originalScore: number, originalDelta: number) {
|
||||
handleVoteError(e: FetcherError, originalScore: number, originalDelta: number) {
|
||||
this.setState({
|
||||
scoreDelta: originalDelta,
|
||||
cachedScore: originalScore,
|
||||
@@ -315,7 +316,7 @@ export class Comment extends Component<Props, State> {
|
||||
* Defines whether current client is logged in via `Anonymous provider`
|
||||
*/
|
||||
isAnonymous(): boolean {
|
||||
return this.props.user! && this.props.user!.id.substr(0, 10) === 'anonymous_';
|
||||
return isUserAnonymous(this.props.user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,6 +370,7 @@ export class Comment extends Component<Props, State> {
|
||||
const isUpvotingDisabled = upvotingDisabledReason !== null;
|
||||
const editable = props.repliesCount === 0 && state.editDeadline;
|
||||
const scoreSignEnabled = !StaticStore.config.positive_score;
|
||||
const uploadImageHandler = this.isAnonymous() ? undefined : this.props.uploadImage;
|
||||
|
||||
/**
|
||||
* CommentType adapted for rendering
|
||||
@@ -676,7 +678,7 @@ export class Comment extends Component<Props, State> {
|
||||
onCancel={this.toggleReplying}
|
||||
getPreview={this.props.getPreview!}
|
||||
autofocus={true}
|
||||
uploadImage={this.props.uploadImage!}
|
||||
uploadImage={uploadImageHandler}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -691,7 +693,7 @@ export class Comment extends Component<Props, State> {
|
||||
getPreview={this.props.getPreview!}
|
||||
errorMessage={state.editDeadline === null ? 'Editing time has expired.' : undefined}
|
||||
autofocus={true}
|
||||
uploadImage={this.props.uploadImage!}
|
||||
uploadImage={uploadImageHandler}
|
||||
/>
|
||||
)}
|
||||
</article>
|
||||
|
||||
@@ -38,7 +38,7 @@ interface Props {
|
||||
getPreview(text: string): Promise<string>;
|
||||
/** action on cancel. optional as root input has no cancel option */
|
||||
onCancel?: () => void;
|
||||
uploadImage: (image: File) => Promise<Image>;
|
||||
uploadImage?: (image: File) => Promise<Image>;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -194,6 +194,7 @@ export class Input extends Component<Props, State> {
|
||||
}
|
||||
|
||||
onDragOver(e: DragEvent) {
|
||||
if (!this.props.uploadImage) return;
|
||||
if (StaticStore.config.max_image_size === 0) return;
|
||||
if (!this.textAreaRef) return;
|
||||
if (!e.dataTransfer) return;
|
||||
@@ -204,6 +205,7 @@ export class Input extends Component<Props, State> {
|
||||
}
|
||||
|
||||
onDrop(e: DragEvent) {
|
||||
if (!this.props.uploadImage) return;
|
||||
if (StaticStore.config.max_image_size === 0) return;
|
||||
if (!e.dataTransfer) return;
|
||||
|
||||
@@ -217,20 +219,19 @@ export class Input extends Component<Props, State> {
|
||||
|
||||
/** wrapper with error handling for props.uploadImage */
|
||||
uploadImage(file: File): Promise<Image | Error> {
|
||||
return this.props
|
||||
.uploadImage(file)
|
||||
.catch(
|
||||
(e: ApiError | string) =>
|
||||
new Error(
|
||||
typeof e === 'string'
|
||||
? `${file.name} upload failed with "${e}"`
|
||||
: `${file.name} upload failed with "${e.error}"`
|
||||
)
|
||||
);
|
||||
return this.props.uploadImage!(file).catch(
|
||||
(e: ApiError | string) =>
|
||||
new Error(
|
||||
typeof e === 'string'
|
||||
? `${file.name} upload failed with "${e}"`
|
||||
: `${file.name} upload failed with "${e.error}"`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** performs upload process */
|
||||
async uploadImages(files: File[]) {
|
||||
if (!this.props.uploadImage) return;
|
||||
if (!this.textAreaRef) return;
|
||||
|
||||
/** Human readable image size limit, i.e 5MB */
|
||||
|
||||
@@ -49,6 +49,7 @@ import { Input } from '@app/components/input';
|
||||
import Preloader from '@app/components/preloader';
|
||||
import { Thread } from '@app/components/thread';
|
||||
import { uploadImage } from '@app/common/api';
|
||||
import { isUserAnonymous } from '@app/utils/isUserAnonymous';
|
||||
|
||||
interface Props {
|
||||
user: User | null;
|
||||
@@ -192,6 +193,13 @@ export class Root extends Component<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines whether current client is logged in via `Anonymous provider`
|
||||
*/
|
||||
isAnonymous(): boolean {
|
||||
return isUserAnonymous(this.props.user);
|
||||
}
|
||||
|
||||
render(props: RenderableProps<Props>, { isLoaded, isCommentsListLoading, commentsShown }: State) {
|
||||
if (!isLoaded) {
|
||||
return (
|
||||
@@ -205,6 +213,7 @@ export class Root extends Component<Props, State> {
|
||||
|
||||
const isGuest = !props.user;
|
||||
const isCommentsDisabled = !!props.info.read_only;
|
||||
const imageUploadHandler = this.isAnonymous() ? undefined : this.props.uploadImage;
|
||||
|
||||
return (
|
||||
<div id={NODE_ID}>
|
||||
@@ -235,7 +244,7 @@ export class Root extends Component<Props, State> {
|
||||
userId={this.props.user!.id}
|
||||
onSubmit={(text, title) => this.props.addComment(text, title)}
|
||||
getPreview={this.props.getPreview}
|
||||
uploadImage={this.props.uploadImage}
|
||||
uploadImage={imageUploadHandler}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -20,14 +20,15 @@ const errorMessageForCodes = new Map([
|
||||
[18, 'Requested file cannot be found.'],
|
||||
]);
|
||||
|
||||
export type FetcherResponse =
|
||||
export type FetcherError =
|
||||
| string
|
||||
| {
|
||||
code?: number;
|
||||
details?: string;
|
||||
error: string;
|
||||
};
|
||||
|
||||
export function extractErrorMessageFromResponse(response: FetcherResponse): string {
|
||||
export function extractErrorMessageFromResponse(response: FetcherError): string {
|
||||
const defaultErrorMessage = 'Something went wrong. Please try again a bit later.';
|
||||
if (!response) {
|
||||
return defaultErrorMessage;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { User } from '@app/common/types';
|
||||
|
||||
/**
|
||||
* Defines whether current client is logged in via `Anonymous provider`
|
||||
*/
|
||||
export function isUserAnonymous(user?: User | null): boolean {
|
||||
return user! && user!.id.substr(0, 10) === 'anonymous_';
|
||||
}
|
||||
Generated
+30
-11
@@ -4854,7 +4854,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -4875,12 +4876,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -4895,17 +4898,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -5022,7 +5028,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -5034,6 +5041,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -5048,6 +5056,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -5055,12 +5064,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -5079,6 +5090,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -5159,7 +5171,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -5171,6 +5184,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -5256,7 +5270,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -5292,6 +5307,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -5311,6 +5327,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -5354,12 +5371,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"build": "webpack --mode production",
|
||||
"start": "webpack-dev-server --mode development",
|
||||
"check": "tsc -p tsconfig.json --noEmit --skipLibCheck",
|
||||
"lint": "eslint --ext=.ts,.tsx,.js,.jsx .",
|
||||
"lint": "eslint --max-warnings=0 --ext=.ts,.tsx,.js,.jsx .",
|
||||
"test": "jest",
|
||||
"test:coverage": "jest --coverage",
|
||||
"prettier": "prettier --write \"./**/*.{js,jsx,ts,tsx,scss}\""
|
||||
|
||||
Reference in New Issue
Block a user