Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Gov/Fellowship selectors #2741

Merged
merged 12 commits into from
Nov 29, 2024
4 changes: 4 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,10 @@ module.exports = {
message: 'Unnecessary cnTw usage, use simple string instead.',
selector: 'CallExpression[callee.name="cnTw"][arguments.length=1][arguments.0.type="Literal"]',
},
{
message: '`combine` must be called with not more than 3 arguments.',
selector: 'CallExpression[callee.name="combine"][arguments.length>3]',
},
],
},
},
Expand Down
61 changes: 30 additions & 31 deletions src/renderer/entities/operations/ui/SignatorySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,40 +38,39 @@ export const SignatorySelector = ({
};

return (
<div className="flex flex-col gap-y-2">
<Field text={t('proxy.addProxy.signatoryLabel')}>
<Select
placeholder={t('proxy.addProxy.signatoryPlaceholder')}
value={signatory?.id.toString() ?? null}
invalid={hasError}
onChange={(value) => selectSigner(Number(value))}
>
{signatories.map(({ signer, balance }) => {
const isShard = accountUtils.isShardAccount(signer);
const address = toAddress(signer.accountId, { prefix: addressPrefix });
<Field text={t('proxy.addProxy.signatoryLabel')}>
<Select
placeholder={t('proxy.addProxy.signatoryPlaceholder')}
value={signatory?.id.toString() ?? null}
invalid={hasError}
onChange={(value) => selectSigner(Number(value))}
>
{signatories.map(({ signer, balance }) => {
const isShard = accountUtils.isShardAccount(signer);
const address = toAddress(signer.accountId, { prefix: addressPrefix });

return (
<Select.Item key={signer.id} value={signer.id.toString()}>
<div className="flex w-full items-center justify-between">
<Address
showIcon
hideAddress
variant="short"
iconSize={20}
address={address}
title={isShard ? address : signer.name}
canCopy={false}
/>
<AssetBalance value={balance.toString()} asset={asset} />
</div>
</Select.Item>
);
})}
</Select>

return (
<Select.Item key={signer.id} value={signer.id.toString()}>
<div className="flex w-full items-center justify-between">
<Address
showIcon
hideAddress
variant="short"
iconSize={20}
address={address}
title={isShard ? address : signer.name}
canCopy={false}
/>
<AssetBalance value={balance.toString()} asset={asset} />
</div>
</Select.Item>
);
})}
</Select>
</Field>
<InputHint variant="error" active={hasError}>
{errorText}
</InputHint>
</div>
</Field>
);
};
2 changes: 1 addition & 1 deletion src/renderer/entities/transaction/ui/Fee/Fee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { transactionService } from '../../lib';
import { FeeLoader } from '../FeeLoader/FeeLoader';

type Props = {
api?: ApiPromise;
api: ApiPromise | null;
multiply?: number;
asset: Asset;
transaction?: Transaction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { transactionService } from '../../lib';
import { FeeLoader } from '../FeeLoader/FeeLoader';

type Props = {
api?: ApiPromise;
api: ApiPromise | null;
asset: Asset;
threshold: MultisigThreshold;
className?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { AssetFiatBalance, priceProviderModel } from '@/entities/price';
import { FeeLoader } from '../FeeLoader/FeeLoader';

type Props = {
api?: ApiPromise;
api: ApiPromise | null;
asset: Asset;
deposit?: string;
proxyNumber?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ const $maxRank = fellowshipModel.$store.map(x => x?.maxRank ?? 0);
const $members = fellowshipModel.$store.map(x => x?.members ?? []);

const $thresholds = combine(
$referendums,
$maxRank,
$members,
tracksModel.$list,
(referendums, maxRank, members, tracks) => {
{
referendums: $referendums,
maxRank: $maxRank,
members: $members,
tracks: tracksModel.$list,
},
({ referendums, maxRank, members, tracks }) => {
const result: Thresholds = {};

for (const referendum of referendums) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { useUnit } from 'effector-react';

import { useI18n } from '@/shared/i18n';
import { Button, MultiSelect, Select } from '@/shared/ui';
import { Box } from '@/shared/ui-kit';
import { Button, MultiSelect } from '@/shared/ui';
import { Box, Select } from '@/shared/ui-kit';
import { filterModel } from '../../model/filter';

import { voteOptions } from './constants';

export const Filters = () => {
const { t } = useI18n();

const query = useUnit(filterModel.$query);
const tracks = useUnit(filterModel.$tracks);
const selectedTrackIds = useUnit(filterModel.$selectedTracks);
const selectedVoteId = useUnit(filterModel.$selectedVotingStatus);
const query = useUnit(filterModel.$query);
const isFiltersSelected = useUnit(filterModel.$isFiltersSelected);

if (query) {
Expand All @@ -25,36 +24,32 @@ export const Filters = () => {
element: name.toString(),
}));

const voteFilterOptions = voteOptions.map(({ id, value, element }) => ({
id,
value,
element: t(element),
}));

return (
<Box direction="row" gap={4} padding={[4, 0, 2]}>
<MultiSelect
className="w-[200px]"
placeholder={t('governance.filters.tracks')}
multiPlaceholder={t('governance.filters.tracks')}
selectedIds={selectedTrackIds.map(x => x.toString())}
options={trackFilterOptions}
disabled={tracks.length === 0}
onChange={value => {
filterModel.events.selectTracks(value.map(x => x.value));
}}
/>
<Select
className="w-[103px]"
placeholder={t('governance.filters.vote')}
selectedId={selectedVoteId ?? ''}
options={voteFilterOptions}
onChange={value => {
filterModel.events.selectVotingStatus(value.value);
}}
/>
<Box direction="row" horizontalAlign="space-between" verticalAlign="center" shrink={0}>
<div className="grid grid-cols-[200px,104px] gap-x-4">
<MultiSelect
placeholder={t('governance.filters.tracks')}
multiPlaceholder={t('governance.filters.tracks')}
selectedIds={selectedTrackIds.map(x => x.toString())}
options={trackFilterOptions}
disabled={tracks.length === 0}
onChange={value => {
filterModel.events.selectTracks(value.map(x => x.value));
}}
/>

<Select
placeholder={t('governance.filters.vote')}
value={selectedVoteId}
onChange={filterModel.events.selectVotingStatus}
>
<Select.Item value="voted">{t('governance.voted')}</Select.Item>
<Select.Item value="notVoted">{t('governance.filters.notVoted')}</Select.Item>
</Select>
</div>

{Boolean(isFiltersSelected) && (
<Button variant="text" className="ml-auto h-8.5 py-0" onClick={() => filterModel.events.filtersReset()}>
<Button variant="text" className="h-8.5" onClick={() => filterModel.events.filtersReset()}>
{t('operations.filters.clearAll')}
</Button>
)}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export { Search } from './Search';
export { Filters } from './Filters';
export { voteOptions } from './constants';
16 changes: 6 additions & 10 deletions src/renderer/features/fellowship-referendums/model/filter.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import { combine, createEvent, createStore, restore, sample } from 'effector';
import { combine, createEvent, restore, sample } from 'effector';
import { debounce } from 'patronum';

import { nonNullable } from '@/shared/lib/utils';
import { type TrackId } from '@/shared/pallet/referenda';
import { collectiveDomain } from '@/domains/collectives';

import { fellowshipModel } from './fellowship';
import { referendumsFeatureStatus } from './status';

export type VotingStatus = 'voted' | 'notVoted';

const $tracks = fellowshipModel.$store.map(x => x?.tracks ?? []);

const queryChanged = createEvent<string>();
const selectTracks = createEvent<TrackId[]>();
const selectVotingStatus = createEvent<VotingStatus | null>();
const selectVotingStatus = createEvent<null | 'voted' | 'notVoted'>();
const filtersReset = createEvent();

const $selectedTracks = restore(selectTracks, []);
const $selectedVotingStatus = createStore<VotingStatus | null>(null).on(selectVotingStatus, (v, p) =>
v === p ? null : p,
);

const $query = restore(debounce(queryChanged, 100), '');
const $selectedTracks = restore(selectTracks, []);
const $selectedVotingStatus = restore(selectVotingStatus, null);

const $isFiltersSelected = combine($selectedTracks, $selectedVotingStatus, (tracks, voteId) => {
return tracks.length > 0 || voteId !== null;
return tracks.length > 0 || nonNullable(voteId);
});

sample({
Expand Down
10 changes: 6 additions & 4 deletions src/renderer/features/fellowship-voting/model/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { walletModel } from '@/entities/wallet';
import { fellowshipNetworkFeature } from '@/features/fellowship-network';

const $input = combine(
fellowshipNetworkFeature.model.network.$network,
walletModel.$wallets,
walletModel.$activeWallet,
(network, wallets, wallet) => {
{
network: fellowshipNetworkFeature.model.network.$network,
wallets: walletModel.$wallets,
wallet: walletModel.$activeWallet,
},
({ network, wallets, wallet }) => {
if (nullable(network) || nullable(wallet)) return null;

return {
Expand Down
19 changes: 13 additions & 6 deletions src/renderer/features/fellowship-voting/model/votingStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,20 @@ const $votingAccount = combine(votingFeatureStatus.input, $currentMember, (input
return collectiveDomain.membersService.findMachingAccount(input.wallet, member);
});

const $hasRequiredRank = combine($currentMember, $referendum, $maxRank, (member, referendum, maxRank) => {
if (nullable(member) || nullable(referendum) || collectiveDomain.referendumService.isCompleted(referendum)) {
return false;
}
const $hasRequiredRank = combine(
{
member: $currentMember,
referendum: $referendum,
maxRank: $maxRank,
},
({ member, referendum, maxRank }) => {
if (nullable(member) || nullable(referendum) || collectiveDomain.referendumService.isCompleted(referendum)) {
return false;
}

return collectiveDomain.tracksService.rankSatisfiesVotingThreshold(member.rank, maxRank, referendum.track);
});
return collectiveDomain.tracksService.rankSatisfiesVotingThreshold(member.rank, maxRank, referendum.track);
},
);

const $canVote = $currentMember.map(nonNullable);

Expand Down
12 changes: 7 additions & 5 deletions src/renderer/features/governance/aggregates/delegatedVotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import { networkSelectorModel } from '../model/networkSelector';
import { proposerIdentityAggregate } from './proposerIdentity';

const $delegatedVotesInChain = combine(
delegatedVotesModel.$delegatedVotes,
networkSelectorModel.$governanceChain,
(votes, chain) => {
if (!chain) return {};
{
votes: delegatedVotesModel.$delegatedVotes,
chainId: networkSelectorModel.$governanceChainId,
},
({ votes, chainId }) => {
if (!chainId) return {};

return votes[chain.chainId] ?? {};
return votes[chainId] ?? {};
},
);

Expand Down
10 changes: 8 additions & 2 deletions src/renderer/features/governance/aggregates/delegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ const $totalDelegations = combine(
);

const $activeDelegations = combine(
{ activeVotes: votingAggregate.$activeWalletVotes, chain: networkSelectorModel.$governanceChain },
{
activeVotes: votingAggregate.$activeWalletVotes,
chain: networkSelectorModel.$governanceChain,
},
({ activeVotes, chain }) => {
const activeBalances: DelegationBalanceMap = {};

Expand All @@ -58,7 +61,10 @@ const $activeDelegations = combine(
);

const $activeTracks = combine(
{ activeVotes: votingAggregate.$activeWalletVotes, chain: networkSelectorModel.$governanceChain },
{
activeVotes: votingAggregate.$activeWalletVotes,
chain: networkSelectorModel.$governanceChain,
},
({ activeVotes, chain }) => {
const activeTracks: DelegationTracksMap = {};

Expand Down
Loading
Loading