Skip to content

Commit 6091f9d

Browse files
vhcsilvamoshmageickasVitor Hugo
authored
BEPRO 2.32 (#510)
* Bepro 2261 disable access logs with and environment variable (#504) * add new env to control access logs * only send access logs if it is enabled * Bepro 2259 disable elastic search with an environment variable (#505) * add missing variable * fix parsing error * remove verification from here because we may want to sent access logs but disable other logs * create helper method to parse array values and using direct on the service * use direct value instead of creating new object * needs to pass object because of elastic index * rollback changes * use body as object because its how its supposed to be * fix query * use different parameters for address and handle * Bepro 2258 limit the size of the comments on frontend and backend (#506) * validate comment length on fe * validate comment length on be * improve error feedback * Bepro 2256 when i confirm the user email the user should be redirected (#507) * fix dashboard url * improve validation response * improve verification feedback * fix: improve copy and design * Bepro 2257 support to develop locally with a ganache network (#508) * add localhost to supported chains * use polygon as default chain * get initial chain from config * fix: only allow ganache if is dev environment * update dev with master (#512) * Fix email templates (#496) * use handle if exists * adjust template layout * add missing prop * also exclude user who commented, because he dont need to receive notification * fix cancelable time nan (#509) * add new tags (#511) --------- Co-authored-by: moshmage <moshmage@gmail.com> Co-authored-by: Henrique <henrique@layerx.xyz> * [TKAI-4225] enforce task prize to be divisible by 100 (#515) * fix default values * ignore logs * add missing event type * adjust value to be divisible by 100 * clear inputs to ensure recalculation * add tests and fix --------- Co-authored-by: Vitor Hugo <vhcsilva@gmail.com> --------- Co-authored-by: moshmage <moshmage@gmail.com> Co-authored-by: Henrique <henrique@layerx.xyz> Co-authored-by: Vitor Hugo <vhcsilva@gmail.com>
1 parent 78f9e4e commit 6091f9d

25 files changed

Lines changed: 470 additions & 160 deletions

File tree

.env.example

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ NEXT_PUBLIC_PROPOSAL_REJECTED=
9393
# Google Analytics Measurement ID
9494
NEXT_PUBLIC_GA_MEASUREMENT_ID=
9595

96+
# Enable/Disable Elastic logs: true | false
97+
NEXT_LOG_TO_ELASTIC=false
98+
9699
# API log level: 0 none, 1 error, 2 warn, 3 info, 4 trace, 5 log, 6 debug
97100
LOG_LEVEL=6
98101

@@ -149,4 +152,7 @@ NEXT_PUBLIC_MODAL_FEATURE_LINK=
149152

150153
# ImgProxy
151154
IMGPROXY_KEY=
152-
IMGPROXY_SALT=
155+
IMGPROXY_SALT=
156+
157+
# Enable/Disable access logs: true | false
158+
ACCESS_LOGS_ENABLED=false

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ yarn-debug.log*
3232
yarn-error.log*
3333
.vscode/
3434
.editorconfig
35+
logs
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { NumberFormatValues } from "react-number-format";
2+
3+
import { toSmartContractDecimals } from "@taikai/dappkit/dist/src/utils/numbers";
4+
import { fireEvent } from "@testing-library/dom";
5+
import BigNumber from "bignumber.js";
6+
7+
import CreateBountyTokenAmount, {
8+
ZeroNumberFormatValues,
9+
} from "components/bounty/create-bounty/token-amount/controller";
10+
11+
import { Network } from "interfaces/network";
12+
import { DistributionsProps } from "interfaces/proposal";
13+
import { Token } from "interfaces/token";
14+
15+
import { render } from "__tests__/utils/custom-render";
16+
17+
jest.mock("x-hooks/use-bepro", () => () => ({}));
18+
19+
const mockCurrentToken: Token = {
20+
address: "0x1234567890123456789012345678901234567890",
21+
name: "Mock Token",
22+
symbol: "MOCK",
23+
};
24+
25+
const mockCurrentNetwork: Network = {
26+
id: 1,
27+
name: "Mock Network",
28+
updatedAt: new Date(),
29+
createdAt: new Date(),
30+
description: "Mock Description",
31+
networkAddress: "0x1234567890123456789012345678901234567890",
32+
creatorAddress: "0x1234567890123456789012345678901234567890",
33+
openBounties: 0,
34+
totalBounties: 0,
35+
allowCustomTokens: false,
36+
councilMembers: [],
37+
banned_domains: [],
38+
closeTaskAllowList: [],
39+
allow_list: [],
40+
mergeCreatorFeeShare: 0.05,
41+
proposerFeeShare: 0.5,
42+
chain: {
43+
chainId: 1,
44+
chainRpc: "https://mock-rpc.com",
45+
name: "Mock Chain",
46+
chainName: "Mock Chain",
47+
chainShortName: "MOCK",
48+
chainCurrencySymbol: "MOCK",
49+
chainCurrencyDecimals: "18",
50+
chainCurrencyName: "Mock Token",
51+
blockScanner: "https://mock-scanner.com",
52+
registryAddress: "0x1234567890123456789012345678901234567890",
53+
eventsApi: "https://mock-events.com",
54+
isDefault: true,
55+
closeFeePercentage: 10,
56+
cancelFeePercentage: 1.0,
57+
networkCreationFeePercentage: 0.5,
58+
},
59+
};
60+
61+
describe("TokenAmountController", () => {
62+
beforeEach(() => {
63+
jest.clearAllMocks();
64+
});
65+
66+
it("fuzzes total input and ensures internal adjusted total is divisible by 100 in contract units", () => {
67+
let executions = 0;
68+
69+
let issueAmount = ZeroNumberFormatValues;
70+
let previewAmount = ZeroNumberFormatValues;
71+
72+
const setPreviewAmount = jest.fn((value: NumberFormatValues) => {
73+
previewAmount = value;
74+
});
75+
const updateIssueAmount = jest.fn((value: NumberFormatValues) => {
76+
issueAmount = value;
77+
});
78+
79+
while (executions < 100) {
80+
const decimals = Math.floor(Math.random() * 13) + 6;
81+
const randomValue = parseFloat((Math.random() * 499999 + 1).toFixed(decimals));
82+
83+
const result = render(<CreateBountyTokenAmount
84+
currentToken={mockCurrentToken}
85+
updateCurrentToken={jest.fn()}
86+
addToken={jest.fn()}
87+
canAddCustomToken={true}
88+
defaultToken={mockCurrentToken}
89+
userAddress="0x1234567890123456789012345678901234567890"
90+
customTokens={[]}
91+
tokenBalance={new BigNumber(1)}
92+
issueAmount={issueAmount}
93+
updateIssueAmount={updateIssueAmount}
94+
isFunders={true}
95+
decimals={decimals}
96+
isFunding={false}
97+
needValueValidation={false}
98+
previewAmount={previewAmount}
99+
distributions={{} as DistributionsProps}
100+
currentNetwork={mockCurrentNetwork}
101+
setPreviewAmount={setPreviewAmount}
102+
setDistributions={jest.fn()}
103+
sethasAmountError={jest.fn()}
104+
/>);
105+
106+
const totalAmountInput = result.getAllByTestId("total-amount-input")[0];
107+
108+
const valueString = randomValue.toString();
109+
110+
fireEvent.change(totalAmountInput, { target: { value: valueString } });
111+
112+
result.rerender(<CreateBountyTokenAmount
113+
currentToken={mockCurrentToken}
114+
updateCurrentToken={jest.fn()}
115+
addToken={jest.fn()}
116+
canAddCustomToken={true}
117+
defaultToken={mockCurrentToken}
118+
userAddress="0x1234567890123456789012345678901234567890"
119+
customTokens={[]}
120+
tokenBalance={new BigNumber(1)}
121+
issueAmount={issueAmount}
122+
updateIssueAmount={updateIssueAmount}
123+
isFunders={true}
124+
decimals={decimals}
125+
isFunding={false}
126+
needValueValidation={false}
127+
previewAmount={previewAmount}
128+
distributions={{} as DistributionsProps}
129+
currentNetwork={mockCurrentNetwork}
130+
setPreviewAmount={setPreviewAmount}
131+
setDistributions={jest.fn()}
132+
sethasAmountError={jest.fn()}
133+
/>);
134+
135+
const newValueContract = toSmartContractDecimals(issueAmount.value, decimals);
136+
137+
expect(Number(BigInt(newValueContract) % BigInt(100))).toBe(0);
138+
139+
jest.clearAllMocks();
140+
result.unmount();
141+
142+
executions += 1;
143+
}
144+
});
145+
});

components/bounty/comments/input-comment/controller.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import {ChangeEvent, useState} from "react";
22

3+
import { AxiosError } from "axios";
34
import {useTranslation} from "next-i18next";
45

56
import InputCommentView from "components/bounty/comments/input-comment/view";
67

8+
import { COMMENT_MAX_LENGTH } from "helpers/constants";
79
import {QueryKeys} from "helpers/query-keys";
810

911
import {IdsComment, TypeComment} from "interfaces/comments";
1012

1113
import {CreateComment} from "x-hooks/api/comments";
14+
import { useToastStore } from "x-hooks/stores/toasts/toasts.store";
1215
import useReactQueryMutation from "x-hooks/use-react-query-mutation";
1316
import { useTaskSubscription } from "x-hooks/use-task-subscription";
1417

@@ -34,7 +37,10 @@ export default function InputComment({
3437
deliverable: QueryKeys.deliverable(ids?.deliverableId?.toString()),
3538
proposal: QueryKeys.proposalComments(ids?.proposalId?.toString())
3639
}[type];
40+
const commentLength = comment?.length || 0;
41+
const error = commentLength > COMMENT_MAX_LENGTH ? "max-length" : null;
3742

43+
const { addError, addSuccess } = useToastStore();
3844
const { refresh: refreshSubscriptions } = useTaskSubscription();
3945
const { mutate: addComment } = useReactQueryMutation({
4046
queryKey: queryKey,
@@ -43,9 +49,12 @@ export default function InputComment({
4349
...ids,
4450
type
4551
}),
46-
toastSuccess: t("bounty:actions.comment.success"),
47-
toastError: t("bounty:actions.comment.error"),
48-
onSuccess: () => {
52+
onSettled: (data, error: AxiosError<{ message: string }>) => {
53+
if (error) {
54+
addError(t("actions.failed"), `${error?.response?.data?.message}`);
55+
return;
56+
}
57+
addSuccess(t("actions.success"), t("bounty:actions.comment.success"));
4958
setComment("");
5059
refreshSubscriptions();
5160
}
@@ -61,6 +70,9 @@ export default function InputComment({
6170
userAddress={userAddress}
6271
avatarHash={avatar}
6372
comment={comment}
73+
commentLength={commentLength}
74+
maxLength={COMMENT_MAX_LENGTH}
75+
error={error}
6476
onCommentChange={onCommentChange}
6577
onCommentSubmit={addComment}
6678
/>

components/bounty/comments/input-comment/view.tsx

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {useTranslation} from "next-i18next";
44

55
import AvatarOrIdenticon from "components/avatar-or-identicon";
66
import Button from "components/button";
7+
import If from "components/If";
78

89
import {truncateAddress} from "helpers/truncate-address";
910

@@ -12,55 +13,76 @@ export default function InputCommentView({
1213
userAddress,
1314
avatarHash,
1415
comment,
16+
commentLength,
17+
maxLength,
18+
error = null,
1519
onCommentSubmit,
1620
onCommentChange ,
1721
}: {
1822
handle?: string;
1923
userAddress: string;
2024
avatarHash: string;
2125
comment: string;
26+
commentLength: number;
27+
maxLength: number;
28+
error: "max-length" | null;
2229
onCommentSubmit: (...props) => void;
2330
onCommentChange : (e: ChangeEvent<HTMLTextAreaElement>) => void
2431
}) {
2532
const { t } = useTranslation("common");
2633

34+
const borderColor = error ? "danger" : "gray-700";
35+
const errorMessage = {
36+
"max-length": t("errors.comment.max-length", { max: maxLength }),
37+
}[error];
38+
2739
return (
28-
<div className="border-radius-8 p-3 bg-gray-850 mb-3 border-gray-700 border">
29-
<div className="d-flex align-items-center mb-2">
30-
<div className="d-flex align-items-center text-truncate">
31-
<AvatarOrIdenticon
32-
user={{
33-
avatar: avatarHash,
34-
handle,
35-
address: userAddress,
36-
}}
37-
size="xsm"
38-
/>
39-
<span className="xs-medium ms-2 text-truncate">
40-
{handle ? `@${handle}` : truncateAddress(userAddress)}{" "}
41-
</span>
40+
<>
41+
<div className={`border-radius-8 p-3 bg-gray-850 border border-${borderColor}`}>
42+
<div className="d-flex align-items-center justify-content-between mb-2">
43+
<div className="d-flex align-items-center text-truncate">
44+
<AvatarOrIdenticon
45+
user={{
46+
avatar: avatarHash,
47+
handle,
48+
address: userAddress,
49+
}}
50+
size="xsm"
51+
/>
52+
<span className="xs-medium ms-2 text-truncate">
53+
{handle ? `@${handle}` : truncateAddress(userAddress)}{" "}
54+
</span>
55+
</div>
56+
57+
<div className="xs-medium text-gray-300">
58+
{commentLength}/{maxLength}
59+
</div>
4260
</div>
43-
</div>
44-
<textarea
45-
tabIndex={0}
46-
className="ps-0 form-control input-comment"
47-
rows={2}
48-
data-testid="comments-textarea"
49-
placeholder={t("comments.input.placeholder")}
50-
value={comment}
51-
onChange={onCommentChange}
52-
/>
61+
<textarea
62+
tabIndex={0}
63+
className="ps-0 form-control input-comment"
64+
rows={2}
65+
data-testid="comments-textarea"
66+
placeholder={t("comments.input.placeholder")}
67+
value={comment}
68+
onChange={onCommentChange}
69+
/>
5370

54-
<div className="d-flex justify-content-end mt-2">
55-
<Button
56-
className="btn-comment"
57-
data-testid="comments-btn"
58-
onClick={onCommentSubmit}
59-
disabled={!comment?.length}
60-
>
61-
{t("comments.button")}
62-
</Button>
71+
<div className="d-flex justify-content-end mt-2">
72+
<Button
73+
className="btn-comment"
74+
data-testid="comments-btn"
75+
onClick={onCommentSubmit}
76+
disabled={!comment?.length || !!error}
77+
>
78+
{t("comments.button")}
79+
</Button>
80+
</div>
6381
</div>
64-
</div>
82+
83+
<If condition={!!error}>
84+
<small className="xs-small text-danger">{errorMessage}</small>
85+
</If>
86+
</>
6587
);
6688
}

0 commit comments

Comments
 (0)