Skip to content

Commit

Permalink
Feat: Fellowship member (#2903)
Browse files Browse the repository at this point in the history
  • Loading branch information
tuul-wq authored Dec 26, 2024
1 parent 3c4da74 commit 2815bd7
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 43 deletions.
12 changes: 6 additions & 6 deletions src/renderer/domains/collectives/model/members/service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { type Account, type Chain } from '@/shared/core';
import { type Account, type Chain, type Wallet } from '@/shared/core';
import { dictionary } from '@/shared/lib/utils';
import { accountUtils } from '@/entities/wallet';

import { type CoreMember, type Member } from './types';

const findMachingMember = (accounts: Account[], members: Member[], chain: Chain) => {
const findMatchingMember = (wallet: Wallet, accounts: Account[], chain: Chain, members: Member[]) => {
const walletAccounts = accounts.filter(account => {
return !accountUtils.isBaseAccount(account) && accountUtils.isChainAndCryptoMatch(account, chain);
return accountUtils.isNonBaseVaultAccount(account, wallet) && accountUtils.isChainAndCryptoMatch(account, chain);
});
const accountsDictionary = dictionary(walletAccounts, 'accountId');

return members.find(member => member.accountId in accountsDictionary) ?? null;
};

const findMachingAccount = (accounts: Account[], member: Member) => {
const findMatchingAccount = (accounts: Account[], member: Member) => {
return accounts.find(a => a.accountId === member.accountId) ?? null;
};

Expand All @@ -26,7 +26,7 @@ const isCoreMember = (member: Member | CoreMember): member is CoreMember => {
};

export const membersService = {
findMachingMember,
findMachingAccount,
findMatchingMember,
findMatchingAccount,
isCoreMember,
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { useGate, useUnit } from 'effector-react';
import { memo } from 'react';

import { useI18n } from '@/shared/i18n';
import { toAddress } from '@/shared/lib/utils';
import { nonNullable, nullable, toAddress } from '@/shared/lib/utils';
import { FootnoteText, Icon, SmallTitleText } from '@/shared/ui';
import { Address } from '@/shared/ui-entities';
import { Box, Skeleton, Surface } from '@/shared/ui-kit';
import { Box, Skeleton, Surface, Tooltip } from '@/shared/ui-kit';
import { ERROR } from '../constants';
import { profileModel } from '../model/profile';
import { profileFeatureStatus } from '../model/status';
Expand All @@ -18,7 +18,8 @@ export const ProfileCard = memo(() => {
const featureInput = useUnit(profileFeatureStatus.input);
const member = useUnit(profileModel.$currentMember);
const identity = useUnit(profileModel.$identity);
const fulfilled = useUnit(profileModel.$fulfilled);
const pending = useUnit(profileModel.$pending);
const isAccountExist = useUnit(profileModel.$isAccountExist);

const isNetworkDisabled = featureState.status === 'failed' && featureState.error.message === ERROR.networkDisabled;

Expand All @@ -30,8 +31,40 @@ export const ProfileCard = memo(() => {
<Icon name="profile" size={16} />
<FootnoteText className="text-text-secondary">{t('fellowship.yourProfile')}</FootnoteText>
</Box>
<Skeleton fullWidth active={!fulfilled && !isNetworkDisabled}>
{member ? (
<Skeleton fullWidth active={pending || isNetworkDisabled}>
{!isAccountExist && (
<Box direction="row" gap={1} verticalAlign="center">
<SmallTitleText className="text-text-tertiary">{t('fellowship.noAccount')}</SmallTitleText>

<Tooltip>
<Tooltip.Trigger>
<div tabIndex={0}>
<Icon name="questionOutline" size={14} />
</div>
</Tooltip.Trigger>
<Tooltip.Content>
{t('fellowship.tooltips.noAccount', { chain: featureInput?.chain.name || '' })}
</Tooltip.Content>
</Tooltip>
</Box>
)}

{isAccountExist && nullable(member) && (
<Box direction="row" gap={1} verticalAlign="center">
<SmallTitleText className="text-text-tertiary">{t('fellowship.noProfile')}</SmallTitleText>

<Tooltip>
<Tooltip.Trigger>
<div tabIndex={0}>
<Icon name="questionOutline" size={14} />
</div>
</Tooltip.Trigger>
<Tooltip.Content>{t('fellowship.tooltips.noProfile')}</Tooltip.Content>
</Tooltip>
</Box>
)}

{isAccountExist && nonNullable(member) && (
<Box direction="row" width="100%" gap={2} verticalAlign="center">
<SmallTitleText className="w-full">
<Address
Expand All @@ -44,8 +77,6 @@ export const ProfileCard = memo(() => {
/>
</SmallTitleText>
</Box>
) : (
<SmallTitleText className="text-text-tertiary">{t('fellowship.noProfile')}</SmallTitleText>
)}
</Skeleton>
</Box>
Expand Down
22 changes: 17 additions & 5 deletions src/renderer/features/fellowship-profile/model/profile.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { combine, sample } from 'effector';
import { and, not, or } from 'patronum';
import { and, or } from 'patronum';

import { attachToFeatureInput } from '@/shared/effector';
import { nonNullable, nullable } from '@/shared/lib/utils';
import { nullable } from '@/shared/lib/utils';
import { collectiveDomain } from '@/domains/collectives';
import { identityDomain } from '@/domains/identity';
import { accountUtils } from '@/entities/wallet';

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

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

const $identities = combine(profileFeatureStatus.input, identityDomain.identity.$list, (featureInput, list) => {
if (nullable(featureInput)) return {};

Expand All @@ -19,7 +21,9 @@ const $identities = combine(profileFeatureStatus.input, identityDomain.identity.
const $currentMember = combine(profileFeatureStatus.input, $members, (featureInput, members) => {
if (nullable(featureInput) || members.length === 0) return null;

return collectiveDomain.membersService.findMachingMember(featureInput.accounts, members, featureInput.chain);
const { wallet, accounts, chain } = featureInput;

return collectiveDomain.membersService.findMatchingMember(wallet, accounts, chain, members);
});

const $identity = combine($currentMember, $identities, (member, identities) => {
Expand All @@ -28,6 +32,14 @@ const $identity = combine($currentMember, $identities, (member, identities) => {
return identities[member.accountId] ?? null;
});

const $isAccountExist = profileFeatureStatus.input.map(store => {
if (!store) return false;

return store.accounts.some(account => {
return !accountUtils.isBaseAccount(account) && accountUtils.isChainAndCryptoMatch(account, store.chain);
});
});

const $pendingMember = and(collectiveDomain.members.pending, $currentMember.map(nullable));
const $pendingIdentity = and(identityDomain.identity.pending, $identity.map(nullable));

Expand Down Expand Up @@ -55,6 +67,6 @@ sample({
export const profileModel = {
$currentMember,
$identity,
$isAccountExist,
$pending: or($pendingMember, $pendingIdentity, profileFeatureStatus.isStarting),
$fulfilled: and($currentMember.map(nonNullable), not($pendingMember), not($pendingIdentity)),
};
1 change: 1 addition & 0 deletions src/renderer/features/fellowship-profile/model/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const $input = combine(
chainId: network.chainId,
palletType: network.palletType,
accounts: wallet.accounts,
wallet,
};
},
);
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/features/fellowship-voting/model/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ const $input = combine(
chain: network.chain,
chainId: network.chainId,
palletType: network.palletType,
accounts: wallet.accounts,
wallets,
wallet,
accounts: wallet.accounts,
};
},
);
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/features/fellowship-voting/model/votingStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ const $referendum = combine($referendums, $referendumId, (referendums, referendu
const $currentMember = combine(votingFeatureStatus.input, $members, (featureInput, members) => {
if (nullable(featureInput)) return null;

return collectiveDomain.membersService.findMachingMember(featureInput.accounts, members, featureInput.chain);
const { wallet, accounts, chain } = featureInput;

return collectiveDomain.membersService.findMatchingMember(wallet, accounts, chain, members);
});

const $votingAccount = combine(votingFeatureStatus.input, $currentMember, (input, member) => {
if (nullable(member) || nullable(input)) return null;

return collectiveDomain.membersService.findMachingAccount(input.accounts, member);
return collectiveDomain.membersService.findMatchingAccount(input.accounts, member);
});

const $hasRequiredRank = combine(
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/pages/Fellowship/ui/Fellowship.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const Fellowship = () => {
const selectedChain = useUnit(fellowshipNetworkFeature.model.network.$selectedChainId);

useLayoutEffect(() => {
if (chainId && chainId.startsWith('0x')) {
if (chainId?.startsWith('0x')) {
fellowshipNetworkFeature.model.network.selectCollective({ chainId: chainId as ChainId });
} else {
// navigate to default chain
Expand Down
45 changes: 25 additions & 20 deletions src/renderer/shared/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@
"reloadButton": "Refresh"
},
"fellowship": {
"details": {
"noDetails": "Details not found"
},
"errors": {
"disconnect": {
"action": "Open network settings",
Expand All @@ -322,22 +325,7 @@
"modalAccountTitle": "Account",
"modalTitle": "Fellowship members"
},
"voting": {
"voted": "Voted:",
"errors": {
"rankThreshold": "You cannot vote in this referendum because your rank is below the required level."
},
"votingStatus": "Voting status",
"summary": "Summary",
"title": "Vote on",
"threshold": "Threshold",
"aye": "Aye",
"nay": "Nay",
"confirmation": {
"vote": "Vote",
"fee": "Fee"
}
},
"noAccount": "Account not found",
"noProfile": "Profile doesn’t exist",
"referendums": {
"tracks": {
Expand Down Expand Up @@ -365,7 +353,26 @@
}
},
"title": "Fellowship",
"yourProfile": "Your profile",
"tooltips": {
"noProfile": "You are not a fellowship member",
"noAccount": "Please add or create account to the { chain } network"
},
"voting": {
"voted": "Voted:",
"errors": {
"rankThreshold": "You cannot vote in this referendum because your rank is below the required level."
},
"votingStatus": "Voting status",
"summary": "Summary",
"title": "Vote on",
"threshold": "Threshold",
"aye": "Aye",
"nay": "Nay",
"confirmation": {
"vote": "Vote",
"fee": "Fee"
}
},
"votingHistory": {
"modalTitle": "Vote history",
"votes_one": "{count} vote",
Expand All @@ -374,9 +381,7 @@
"emptyList": "There is no vote history",
"votingPowerDescription": "Voting power is a number of votes calculated based on the member's rank."
},
"details": {
"noDetails": "Details not found"
}
"yourProfile": "Your profile"
},
"general": {
"actions": {
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/shared/ui-kit/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const Root = ({
onToggle,
children,
side = 'top',
sideOffset = 2,
sideOffset = 1,
align = 'center',
alignOffset = 0,
testId = 'Tooltip',
Expand Down

0 comments on commit 2815bd7

Please sign in to comment.