Browse Source

Allow reward distribution

master
Jared 1 year ago
parent
commit
9ccff211ac
3 changed files with 144 additions and 50 deletions
  1. +8
    -0
      src/api/composed.ts
  2. +11
    -0
      src/api/types.ts
  3. +125
    -50
      src/views/FundView.vue

+ 8
- 0
src/api/composed.ts View File

@@ -7,6 +7,7 @@ import {
CreateQueueRequest, CreateQueueRequest,
CreateQueueResponse, CreateQueueResponse,
CreateRewardFundRequest, CreateRewardFundRequest,
DistributeRewardsRequest,
EditQueueRequest, EditQueueRequest,
GetBalanceRequest, GetBalanceRequest,
GetBalanceResponse, GetBalanceResponse,
@@ -23,6 +24,7 @@ import {
NearlyCompleteFundsRequest, NearlyCompleteFundsRequest,
NearlyCompleteFundsResponse, NearlyCompleteFundsResponse,
QueueMember, QueueMember,
RewardDistributionInfo,
SubmitRewardFundRequest, SubmitRewardFundRequest,
SuccessResponse, SuccessResponse,
} from '@/api/types'; } from '@/api/types';
@@ -120,3 +122,9 @@ export const submitRewardFund = (fundID: number, submit: boolean) => controller.
fundID, fundID,
submit, submit,
}); });

export const distributeRewardFund = (rewardFundID: number, payments: RewardDistributionInfo[], distribute: boolean) => controller.post<SuccessResponse, DistributeRewardsRequest>('DistributeRewardFund', {
rewardFundID,
payments,
distribute,
});

+ 11
- 0
src/api/types.ts View File

@@ -197,3 +197,14 @@ export interface SubmitRewardFundRequest {
fundID: number; fundID: number;
submit: boolean; submit: boolean;
} }

export interface RewardDistributionInfo {
destination: string;
amount: number;
}

export interface DistributeRewardsRequest {
rewardFundID: number;
payments: RewardDistributionInfo[];
distribute: boolean;
}

+ 125
- 50
src/views/FundView.vue View File

@@ -123,20 +123,23 @@
</div> </div>
</div> </div>
<div id="contribution-container" @scroll="loadMoreIfNeeded"> <div id="contribution-container" @scroll="loadMoreIfNeeded">
<p v-if="store.getters.getToken && hasPermission(Privileges.Admin)">
Enable contribution consolidation in order to select.
Click to select a row in order to mark a wallet to receive rewards.
</p>
<table class="contribution-table table is-fullwidth"> <table class="contribution-table table is-fullwidth">
<thead> <thead>
<tr> <tr>
<th>Wallet</th> <th>Wallet</th>
<th>Amount</th> <th>Amount</th>
<th v-if="!enableConsolidation">Time</th> <th v-if="!enableConsolidation">Time</th>
<template v-else>
<th>Bonus</th>
<th v-if="store.getters.getToken && hasPermission(Privileges.Admin)">Post</th>
</template>
<th v-else>Bonus</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(contribution, i) in contributions" v-bind:key="i">
<tr v-for="(contribution, i) in contributions"
:class="{ 'contribution-selected': contribution.selected }"
v-bind:key="i" @click="selectContribution(contribution)">
<td>{{ truncateWallet(contribution.wallet, calculateWalletChars(), undefined) }}</td> <td>{{ truncateWallet(contribution.wallet, calculateWalletChars(), undefined) }}</td>
<td>{{ contribution.amount }}</td> <td>{{ contribution.amount }}</td>
<td v-if="!enableConsolidation"> <td v-if="!enableConsolidation">
@@ -144,18 +147,29 @@
{{ formatDate(contribution.createdAt, true) }} {{ formatDate(contribution.createdAt, true) }}
</span> </span>
</td> </td>
<template v-else>
<td>
<span>{{ calculateReward(contribution.amount.div(fund.fundInfo.price)) }}</span>
</td>
<td v-if="store.getters.getToken && hasPermission(Privileges.Admin)">
<button class="button is-small is-outlined">Send</button>
</td>
</template>
<td v-else>
<span>{{ calculateReward(contribution.amount.div(fund.fundInfo.price)) }}</span>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div v-if="store.getters.getToken && hasPermission(Privileges.Admin)">
<div class="box is-flex is-flex-direction-row is-justify-content-space-between">
<div class="my-auto">
<label for="distribute-confirm" class="checkbox">
<input type="checkbox" id="distribute-confirm" v-model="allowDistribution">
Allow Distribution
</label>
</div>
<div>
<button class="button is-success"
:disabled="!allowDistribution" @click="distributeFund">
Distribute Rewards
</button>
</div>
</div>
</div>
</section> </section>
<section class="section is-small px-0" <section class="section is-small px-0"
v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> v-if="store.getters.getToken && hasPermission(Privileges.Admin)">
@@ -236,6 +250,7 @@ import ErrorDisplay, { SignetError } from '@/components/ErrorDisplay.vue';
import { import {
contribute, contribute,
deleteRewardFund, deleteRewardFund,
distributeRewardFund,
getBalance, getBalance,
getContributions, getContributions,
getRewardFund, getRewardFund,
@@ -274,40 +289,6 @@ const amount = computed({
}, },
}); });


const allowDelete = ref(false);
const deleteFund = async () => {
const deleted = await deleteRewardFund(identifier, allowDelete.value);
if (deleted && deleted.success) {
await router.push('/');
}
};
const delTimeout = ref(undefined as number | undefined);

watch(allowDelete, () => {
if (delTimeout.value) window.clearTimeout(delTimeout.value);
delTimeout.value = window.setTimeout(() => {
allowDelete.value = false;
delTimeout.value = undefined;
}, 10000);
});

const allowSubmit = ref(false);
const submitFund = async () => {
const submitted = await submitRewardFund(identifier, allowSubmit.value);
if (submitted && submitted.success) {
console.log('submitted'); // TODO: provide feedback for submission
}
};
const subTimeout = ref(undefined as number | undefined);

watch(allowSubmit, () => {
if (subTimeout.value) window.clearTimeout(subTimeout.value);
subTimeout.value = window.setTimeout(() => {
allowSubmit.value = false;
subTimeout.value = undefined;
}, 10000);
});

const enableConsolidation = ref(false); const enableConsolidation = ref(false);
const fund = ref( const fund = ref(
{ {
@@ -354,9 +335,14 @@ if (fund.value.contributions.dates) {
); );
} }


interface SelectableContribution extends Contribution {
selected: boolean;
}

const processContributions = (contributions: Contribution[]) => contributions.map((c) => ({ const processContributions = (contributions: Contribution[]) => contributions.map((c) => ({
...c, ...c,
amount: new Decimal(c.amount), amount: new Decimal(c.amount),
selected: false,
})); }));


const reward = ref(new Decimal(0)); const reward = ref(new Decimal(0));
@@ -364,7 +350,7 @@ const maxBonus = ref(0);
const bonus = ref(undefined as Bonus | undefined); const bonus = ref(undefined as Bonus | undefined);
const amountHeld = ref(new Decimal(fund.value.total.amountHeld)); const amountHeld = ref(new Decimal(fund.value.total.amountHeld));
const amountAvailable = ref(new Decimal(fund.value.fundInfo.amountAvailable)); const amountAvailable = ref(new Decimal(fund.value.fundInfo.amountAvailable));
const contributions: Ref<Contribution[]> = ref(processContributions(
const contributions: Ref<SelectableContribution[]> = ref(processContributions(
fund.value.contributions.list ?? [], fund.value.contributions.list ?? [],
)); ));
const offset = ref(contributions.value.length); const offset = ref(contributions.value.length);
@@ -386,7 +372,7 @@ const calcPctComplete = () => round(amountHeld.value.div(amountAvailable.value)
.mul(100) .mul(100)
.toNumber()); .toNumber());


const calculateWalletChars = () => round(width.value / 114, 0);
const calculateWalletChars = () => round(width.value / 110, 0);


const hasInvalidValues = () => { const hasInvalidValues = () => {
if (!fund.value) throw new Error('Fund was not loaded!'); if (!fund.value) throw new Error('Fund was not loaded!');
@@ -427,6 +413,86 @@ const calculateReward = (bought: Decimal) => {
return reward.value.toLocaleString(); return reward.value.toLocaleString();
}; };


const selectedContributions = ref([] as Contribution[]);
const selectContribution = (contribution: SelectableContribution) => {
if (!store.getters.getToken || !hasPermission(Privileges.Admin)) return;
if (enableConsolidation.value) {
if (!contribution.selected) {
selectedContributions.value.push(contribution);
} else {
const existingContributionIndex = selectedContributions.value.findIndex(
(c) => c.wallet === contribution.wallet,
);
selectedContributions.value.splice(existingContributionIndex, 1);
}
const newContribution: SelectableContribution = {
...contribution,
selected: !contribution.selected,
};
const toReplaceIndex = contributions.value.findIndex((c) => c.wallet === contribution.wallet);
if (toReplaceIndex === -1) throw new Error('Could not find contribution to select');
contributions.value.splice(toReplaceIndex, 1, newContribution);
}
};

const allowDelete = ref(false);
const deleteFund = async () => {
const deleted = await deleteRewardFund(identifier, allowDelete.value);
if (deleted && deleted.success) {
await router.push('/');
}
};
const delTimeout = ref(undefined as number | undefined);

watch(allowDelete, () => {
if (delTimeout.value) window.clearTimeout(delTimeout.value);
delTimeout.value = window.setTimeout(() => {
allowDelete.value = false;
delTimeout.value = undefined;
}, 10000);
});

const allowDistribution = ref(false);
const distributeFund = async () => {
const distributed = await distributeRewardFund(
identifier,
selectedContributions.value.map((c) => ({
destination: c.wallet,
amount: c.amount.toNumber(),
})),
allowDistribution.value,
);
if (distributed && distributed.success) {
console.log('distributed'); // TODO: provide feedback for distribution
}
};
const distTimeout = ref(undefined as number | undefined);

watch(allowDistribution, () => {
if (distTimeout.value) window.clearTimeout(distTimeout.value);
distTimeout.value = window.setTimeout(() => {
allowDistribution.value = false;
distTimeout.value = undefined;
}, 10000);
});

const allowSubmit = ref(false);
const submitFund = async () => {
const submitted = await submitRewardFund(identifier, allowSubmit.value);
if (submitted && submitted.success) {
console.log('submitted'); // TODO: provide feedback for submission
}
};
const subTimeout = ref(undefined as number | undefined);

watch(allowSubmit, () => {
if (subTimeout.value) window.clearTimeout(subTimeout.value);
subTimeout.value = window.setTimeout(() => {
allowSubmit.value = false;
subTimeout.value = undefined;
}, 10000);
});

const errs: SignetError[] = [ const errs: SignetError[] = [
{ {
text: 'Amount is required', text: 'Amount is required',
@@ -553,7 +619,10 @@ watch(data, (newVal) => {
if (!fund.value) throw new Error('Fund not found'); if (!fund.value) throw new Error('Fund not found');
getCurrentBonus(); getCurrentBonus();
if (status.value === 'OPEN') { if (status.value === 'OPEN') {
const v = JSON.parse(newVal.trim()) as Contribution;
const v = {
...JSON.parse(newVal.trim()),
selected: false,
} as SelectableContribution;
v.createdAt = luxon.DateTime.now() v.createdAt = luxon.DateTime.now()
.toISO(); .toISO();
const formattedDate = formatDate(v.createdAt); const formattedDate = formatDate(v.createdAt);
@@ -597,6 +666,9 @@ watch(data, (newVal) => {
.signet-asset-name .signet-asset-name
cursor pointer cursor pointer


tbody tr
cursor pointer

#contribution-container #contribution-container
max-height 360px max-height 360px
overflow-y auto overflow-y auto
@@ -610,6 +682,9 @@ watch(data, (newVal) => {
.fund-details .fund-details
width 182px width 182px


.contribution-selected
background-color #e0cdbb

#consolidate #consolidate
vertical-align middle vertical-align middle




Loading…
Cancel
Save