Skip to content

Commit

Permalink
Notification for refund and blocked duplicate refund vote (#261)
Browse files Browse the repository at this point in the history
* first refund vote sync and fetching balance after 10 seconds

* dummy push

* notification for refund

* added missing migration file

* new notification type added

* block duplicate refund vote and notification fix

* quick fix
  • Loading branch information
ssani7 authored Nov 21, 2023
1 parent f8b3206 commit b9291d4
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 16 deletions.
9 changes: 9 additions & 0 deletions src/components/NotificationsModal/NotificationsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AcceptBreifNotifications from './notificationComponents/AcceptBreifNotifi
import ApplyBreifNotification from './notificationComponents/ApplyBreifNotifications';
import GrantApproversNotifications from './notificationComponents/GrantApproversNotifications';
import MilestoneApprovedNotifications from './notificationComponents/MilestoneApprovedNotifications';
import RefundNotification from './notificationComponents/RefundNotification';
import SubmitMilestoneNotification from './notificationComponents/SubmitMilestoneNotification';
import NotificationsLoader from './NotificationsLoader.tsx';

Expand Down Expand Up @@ -55,6 +56,14 @@ export default function NotificationsModal({ onClose }: { onClose: any }) {
{activity.object === 'AddApprovers.testing' && (
<GrantApproversNotifications {...activity} />
)}
{
(
activity.object === "refund.initiated" ||
activity.object === "refund.complete" ||
activity.object === "refund_initialed.testing"
) && (
<RefundNotification {...activity} />
)}
</div>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function ApplyBreifNotification(activity: any) {
</p>
<p className='text-sm mt-3'>
You have a new applicant for your{' '}
<span id='link' className='text-lg underline text-imbue-purple'>
<span id='link' className='underline text-imbue-purple'>
brief
</span>{' '}
. Take a moment to review their application and consider their
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Image from 'next/image';
import { useRouter } from 'next/router';

export default function RefundNotification(activity: any) {
const router = useRouter();
const handleClick = (e: any) => {
if (e.target.id === 'user') {
router.push(`/profile/${activity.data.sender.username}`);
} else router.push(`/projects/${activity.data.briefId}`);
};

return (
<div
onClick={handleClick}
className='flex hover:bg-imbue-light-purple-three cursor-pointer py-2 border-t border-b px-5'
>
<div className='w-9 flex flex-shrink-0 h-9 mr-3'>
<Image
className='rounded-full w-9 h-9 object-cover'
src={activity.data?.sender?.profile_photo || '/profile-image.png'}
height={20}
width={30}
alt='user'
/>
</div>
<div className='text-left'>
<p className='text-base font-semibold'>
{activity.data.title || 'You have a notification'}
</p>
<p className='text-sm mt-3'>
<p dangerouslySetInnerHTML={{ __html: activity.data.text }} ></p>
</p>
</div>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/Project/ProjectBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ const ProjectBalance = (props: ProjectBalanceType) => {
}
}

getAndSetBalace();
// getAndSetBalace();
if (currency_id == undefined) {
setCurrency_id(project.currency_id);
}

const timer = setInterval(() => {
getAndSetBalace();
}, 5000);
}, 10000);
return () => clearInterval(timer);

// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
2 changes: 1 addition & 1 deletion src/components/Project/V2/ExpandableMilestone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ const ExpandableMilestone = (props: ExpandableMilestonProps) => {
<div className='lg:flex gap-1 lg:items-center rounded-2xl bg-imbue-coral px-3 py-1 text-sm text-white w-fit ml-auto mt-3 '>
<ErrorOutlineOutlinedIcon className='h-4 w-4 inline' />
<p className='inline'>
No funds have been deposited to the escrow address. If you still want to submit your work the risks are upto you.
No funds have been deposited to the escrow address yet.
</p>
</div>
)
Expand Down
8 changes: 0 additions & 8 deletions src/components/Project/V2/MilestoneVoteBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ const MilestoneVoteBox = (props: MilestoneVoteBoxProps) => {
projectId,
firstPendingMilestone
);
console.log(
'🚀 ~ file: MilestoneVoteBox.tsx:87 ~ syncVotes ~ voteResp:',
voteResp
);
setVotes(voteResp);
// const resp = await syncProjectVotes(projectId, firstPendingMilestone, votes)
}
Expand All @@ -132,10 +128,6 @@ const MilestoneVoteBox = (props: MilestoneVoteBoxProps) => {
projectId,
firstPendingMilestone
);
console.log(
'🚀 ~ file: MilestoneVoteBox.tsx:99 ~ setVotingList ~ voteResp:',
voteResp
);
setVotes(voteResp);
setMilestoneVotes(voteResp?.allVoters);
setLoading(false);
Expand Down
16 changes: 12 additions & 4 deletions src/components/Project/V2/NoConfidenceVoteBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Web3WalletModal from '@/components/WalletModal/Web3WalletModal';

import { Project, User } from '@/model';
import ChainService from '@/redux/services/chainService';
import { insertNoConfidenceVote } from '@/redux/services/projectServices';

type MilestoneVoteBoxProps = {
user: User;
Expand All @@ -21,10 +22,11 @@ type MilestoneVoteBoxProps = {
setLoadingMain: (_value: boolean) => void;
approversCount: number;
approverVotedOnRefund: boolean;
loading: boolean;
}

const NoConfidenceBox = (props: MilestoneVoteBoxProps) => {
const { user, project, setLoadingMain, setError, noConfidenceVoters, approverVotedOnRefund } = props;
const { user, project, setLoadingMain, setError, noConfidenceVoters, approverVotedOnRefund, loading } = props;

const [milestoneKeyInView, setMilestoneKeyInView] = useState<number>(0);
const [showPolkadotAccounts, setShowPolkadotAccounts] = useState<boolean>(false);
Expand All @@ -45,7 +47,7 @@ const NoConfidenceBox = (props: MilestoneVoteBoxProps) => {

useEffect(() => {
const getNoConfidenceVotesChain = async () => {
if (!project.chain_project_id) return
if (!project.chain_project_id || !project.id || loading) return

const imbueApi = await initPolkadotJSAPI(
process.env.IMBUE_NETWORK_WEBSOCK_ADDR!
Expand All @@ -63,11 +65,17 @@ const NoConfidenceBox = (props: MilestoneVoteBoxProps) => {
const noconfidenceVotes = await chainService.getNoConfidenceVoters(
Number(project.chain_project_id)
);
console.log("🚀 ~ file: NoConfidenceVoteBox.tsx:64 ~ getNoConfidenceVotesChain ~ noconfidenceVotes:", noconfidenceVotes)

if (!noconfidenceVotes.length) return
// the first no confidence vote will always be false so adding noConfidenceVotes[0] vote to the list if not added
if (!yesVote.map(v => v.web3_address).includes(noconfidenceVotes[0]) && user.web3_address === noconfidenceVotes[0]) {
await insertNoConfidenceVote(project.id, { voter: user, vote: false })
}
}

getNoConfidenceVotesChain()
}, [project.chain_project_id])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loading, project.chain_project_id, project.id])

return (
<div>
Expand Down
57 changes: 57 additions & 0 deletions src/components/ReviewModal/VoteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface VotingModalProps {
setError: (_value: any) => void;
targetUser?: any;
projectType?: 'grant' | 'brief' | null;
approversPreview?: User[];
}

export default function VoteModal({
Expand All @@ -58,6 +59,7 @@ export default function VoteModal({
setError,
refundOnly = false,
projectType,
approversPreview
}: VotingModalProps) {
const [vote, setVote] = useState<boolean>(true);
const [voteRefund, setVoteRefund] = useState<boolean | undefined>();
Expand Down Expand Up @@ -173,6 +175,16 @@ export default function VoteModal({
await insertNoConfidenceVote(project?.id, voteData);
await updateProject(project?.id, project);

if (approversPreview?.length)
sendNotification(
[...approversPreview.map(a => String(a.id)), String(project.user_id)],
'refund.complete',
'Refund Completed',
`The project <span class='mx-0.5 underline text-imbue-purple'>project</span> has been officially refunded. If you encounter any issues or have further concerns, please utilise the report feature for assistance.`,
Number(project.id),
Number(project.first_pending_milestone) + 1
);

setVisible(true);
setVoteRefund(vote);
setStep(4);
Expand Down Expand Up @@ -224,13 +236,57 @@ export default function VoteModal({
// project.project_in_voting_of_no_confidence = true;
await updateProject(project?.id, { ...project, project_in_voting_of_no_confidence: true });
await insertNoConfidenceVote(project?.id, voteData);

// notification to voters for knowing refund initialed
if (approversPreview?.length)
sendNotification(
approversPreview.filter(a => a.id !== user.id)?.map(a => String(a.id)),
'refund.initiated',
'Refund has been initiated for a grant',
`One of your fellow grant voters initiated refund for project <span class='mx-0.5 underline text-imbue-purple'>project</span>. Please add your vote on refunding the project`,
Number(project.id),
Number(project.first_pending_milestone) + 1
);

// notification to grant owner for knowing refund initialed
sendNotification(
[String(project.user_id)],
'refund.initiated',
'Refund has been initiated for your grant',
`The client is requesting a refund due to their dissatisfaction with your work. Please review the project <span class='mx-0.5 underline text-imbue-purple'>project</span>. for more details.`,
Number(project.id),
Number(project.first_pending_milestone) + 1
);

setStep(4);
setVoteRefund(vote);
setVisible(true);
} else if (result.status) {
// project.project_in_voting_of_no_confidence = true;
await updateProject(project?.id, { ...project, project_in_voting_of_no_confidence: true });
await insertNoConfidenceVote(project?.id, voteData);

// notification to voters for knowing refund initialed
if (approversPreview?.length)
sendNotification(
approversPreview.filter(a => a.id !== user.id)?.map(a => String(a.id)),
'refund.initiated',
'Refund has been initiated for a grant',
`One of your fellow grant voters initiated refund for project <span class='mx-0.5 underline text-imbue-purple'>project</span>. Please add your vote on refunding the project`,
Number(project.id),
Number(project.first_pending_milestone) + 1
);

// notification to grant owner for knowing refund initialed
sendNotification(
[String(project.user_id)],
'refund.initiated',
'Refund has been initiated for your grant',
`The client is requesting a refund due to their dissatisfaction with your work. Please review the project <span class='mx-0.5 underline text-imbue-purple'>project</span>. for more details.`,
Number(project.id),
Number(project.first_pending_milestone) + 1
);

setStep(4);
setVoteRefund(vote);
setVisible(true);
Expand All @@ -249,6 +305,7 @@ export default function VoteModal({
}
};


const handleVote = async () => {
if (project?.brief_id) {
setLoading(true);
Expand Down
20 changes: 20 additions & 0 deletions src/db/migrations/20231116151702_create_review_table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Knex } from 'knex';


export async function up(knex: Knex): Promise<void> {
await knex.schema.createTable('reviews', (builder) => {
builder.increments('id', { primaryKey: true });
builder.timestamps(true, true);
builder.integer('freelancer_id');
builder.integer('ratings');
builder.text('title');
builder.text('description');
builder.integer('user_id');
builder.foreign('user_id').references('users.id');
builder.foreign('freelancer_id').references('freelancers.id');
});
}

export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists('reviews');
}
13 changes: 13 additions & 0 deletions src/pages/api/project/noConfidenceVote/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ export default nextConnect()
)(tx);
verifyUserIdFromJwt(req, res, [userAuth.id, ...projectApproverIds]);

const existingVote = await tx('no_confidence_voters')
.select('*')
.where({
project_id: Number(projectId),
web3_address: voter.web3_address,
})
.first();

if (existingVote?.id)
return res
.status(401)
.json({ status: 'Error', message: 'vote already exists' });

const result = await insertToNoConfidenceVoters(
Number(projectId),
voteData
Expand Down
2 changes: 2 additions & 0 deletions src/pages/projects/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ function Project() {
<NoConfidenceBox
noConfidenceVoters={noConfidenceVoters}
setLoadingMain={setLoading}
loading={loading}
approversCount={approversPreview?.length || 0}
{...{
setError,
Expand Down Expand Up @@ -695,6 +696,7 @@ function Project() {
votingWalletAccount,
projectType,
targetUser,
approversPreview
}}
/>

Expand Down
5 changes: 5 additions & 0 deletions src/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,11 @@ input:-webkit-autofill {
color: white;
} */

/* notification styles */
.project_link{
color: var(--theme-primary);
}

@media screen and (max-width: 500px) {
h2 {
font-size: 26px;
Expand Down

0 comments on commit b9291d4

Please sign in to comment.