Skip to content

Commit

Permalink
Merge pull request #10052 from hicommonwealth/salman/Issues-8709-addr…
Browse files Browse the repository at this point in the history
…ess-management-chart

Address management chart
  • Loading branch information
salman-neslit authored Dec 9, 2024
2 parents 4553ea5 + f4fbc75 commit f8805e6
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ const EditProfile = () => {
/>
</ProfileSection>
<ProfileSection
title="Linked addresses"
description="Manage your addresses."
title="Manage your addresses"
description="Connect or disconnect your addresses and manage your community memberships here."
>
<LinkedAddresses
// @ts-expect-error <StrictNullChecks/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@
justify-content: left;
padding: 4px 0 4px 12px;
}

&.no-background {
background-color: transparent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,30 @@ import React from 'react';

import './cw_truncated_address.scss';

import clsx from 'clsx';
import { formatAddressShort } from '../../../helpers';
import { CWCommunityAvatar } from './cw_community_avatar';

type TruncatedAddressProps = {
address: string;
address?: string;
communityInfo?: {
iconUrl: string;
name: string;
};
showCommunityname?: boolean;
};

export const CWTruncatedAddress = ({
address,
communityInfo,
showCommunityname,
}: TruncatedAddressProps) => {
return (
<div
className={
communityInfo ? 'TruncatedAddress with-community' : 'TruncatedAddress'
}
className={clsx('TruncatedAddress', {
'with-community': communityInfo,
'no-background': showCommunityname,
})}
>
{communityInfo && (
<CWCommunityAvatar
Expand All @@ -32,7 +36,8 @@ export const CWTruncatedAddress = ({
size="small"
/>
)}
{formatAddressShort(address)}
{showCommunityname && communityInfo?.name}
{address && formatAddressShort(address)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@
align-items: center;
}
}

.AddressDetails {
display: flex;
justify-content: space-between;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';

import './linked_addresses.scss';

import { formatAddressShort } from 'client/scripts/helpers';
import { useGetCommunityByIdQuery } from 'state/api/communities';
import { PopoverMenu } from 'views/components/component_kit/CWPopoverMenu';
import type AddressInfo from '../../models/AddressInfo';
Expand All @@ -10,9 +11,16 @@ import { DeleteAddressModal } from '../modals/delete_address_modal';
import { CWIconButton } from './component_kit/cw_icon_button';
import { CWTruncatedAddress } from './component_kit/cw_truncated_address';
import { CWModal } from './component_kit/new_designs/CWModal';
import { CWTable } from './component_kit/new_designs/CWTable';
import { CWTableColumnInfo } from './component_kit/new_designs/CWTable/CWTable';

/* eslint-disable react/no-multi-comp */

type AddressProps = {
address: string;
};

type AddressDetailsProps = {
profile: NewProfile;
addressInfo: AddressInfo;
toggleRemoveModal: (val: boolean, address: AddressInfo) => void;
Expand All @@ -25,6 +33,16 @@ type LinkedAddressesProps = {
};

const Address = (props: AddressProps) => {
const { address } = props;

return (
<div className="AddressContainer">
<CWTruncatedAddress address={address} />
</div>
);
};

const AddressDetails = (props: AddressDetailsProps) => {
const { addressInfo, toggleRemoveModal } = props;
const { address, community } = addressInfo;

Expand All @@ -37,30 +55,44 @@ const Address = (props: AddressProps) => {
});

return (
<div className="AddressContainer">
<div className="AddressDetails">
<CWTruncatedAddress
address={address}
communityInfo={{
iconUrl: fetchedCommunity?.icon_url || '',
name: fetchedCommunity?.name || '',
}}
showCommunityname
/>
<PopoverMenu
menuItems={[
{
label: 'Remove',
iconLeft: 'trash',
label: `Disconnect ${formatAddressShort(address)}`,
onClick: () => toggleRemoveModal(true, addressInfo),
},
]}
renderTrigger={(onclick) => (
<CWIconButton iconName="dotsVertical" onClick={onclick} />
<CWIconButton iconName="dotsHorizontal" onClick={onclick} />
)}
/>
</div>
);
};

const columnInfo: CWTableColumnInfo[] = [
{
key: 'address',
header: 'Address',
numeric: false,
sortable: false,
},
{
key: 'communities',
header: 'Communities',
numeric: false,
sortable: false,
},
];

export const LinkedAddresses = (props: LinkedAddressesProps) => {
const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false);
const [currentAddress, setCurrentAddress] = useState<AddressInfo | null>(
Expand All @@ -69,27 +101,54 @@ export const LinkedAddresses = (props: LinkedAddressesProps) => {

const { profile, addresses, refreshProfiles } = props;

const groupedAddresses = useMemo(() => {
return addresses.reduce((acc: Record<string, AddressInfo[]>, addr) => {
if (!acc[addr.address]) acc[addr.address] = [];
acc[addr.address].push(addr);
return acc;
}, {});
}, [addresses]);

const rowData = Object.entries(groupedAddresses).map(
([address, communities]) => ({
address: <Address address={address} />,
communities: (
<div>
{communities.map((addr, index) => {
return (
<AddressDetails
key={index}
profile={profile}
addressInfo={addr}
toggleRemoveModal={(
val: boolean,
selectedAddress: AddressInfo,
) => {
setIsRemoveModalOpen(val);
setCurrentAddress(selectedAddress);
}}
/>
);
})}
</div>
),
}),
);

// Memoize CWTable to prevent unnecessary re-renders.
const TableComponent = useMemo(() => {
return <CWTable columnInfo={columnInfo} rowData={rowData} />;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [addresses]);

return (
<div className="LinkedAddresses">
{addresses?.map((addr, i) => {
return (
<Address
key={i}
profile={profile}
addressInfo={addr}
toggleRemoveModal={(val: boolean, address: AddressInfo) => {
setIsRemoveModalOpen(val);
setCurrentAddress(address);
}}
/>
);
})}
<div>
{TableComponent}
<CWModal
size="small"
content={
currentAddress && (
<DeleteAddressModal
profile={profile}
addresses={addresses}
address={currentAddress}
chain={currentAddress?.community?.id}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import jdenticon from 'jdenticon';
import React from 'react';

import { SERVER_URL } from 'state/api/config';
Expand All @@ -7,23 +6,20 @@ import {
notifySuccess,
} from '../../controllers/app/notifications';
import AddressInfo from '../../models/AddressInfo';
import NewProfile from '../../models/NewProfile';
import { CWText } from '../components/component_kit/cw_text';
import { CWTruncatedAddress } from '../components/component_kit/cw_truncated_address';
import { CWButton } from '../components/component_kit/new_designs/CWButton';
import {
CWModalBody,
CWModalFooter,
CWModalHeader,
} from '../components/component_kit/new_designs/CWModal';

import { DEFAULT_NAME } from '@hicommonwealth/shared';
import axios from 'axios';
import { formatAddressShort } from 'client/scripts/helpers';
import useUserStore from 'state/ui/user';
import './delete_address_modal.scss';

type DeleteAddressModalAttrs = {
profile: NewProfile;
addresses: AddressInfo[];
address: AddressInfo;
chain: string;
Expand All @@ -35,7 +31,6 @@ export const DeleteAddressModal = ({
addresses,
chain,
closeModal,
profile,
}: DeleteAddressModalAttrs) => {
const user = useUserStore();

Expand Down Expand Up @@ -89,36 +84,19 @@ export const DeleteAddressModal = ({
.catch(console.error);
};

const { name } = profile;
const defaultAvatar = jdenticon.toSvg(profile.userId, 90);

return (
<div className="DeleteAddressModal">
<CWModalHeader
label="Delete Address"
label={`Disconnect ${formatAddressShort(address.address)}`}
icon="danger"
onModalClose={closeModal}
/>
<CWModalBody>
<CWText>
Address will be removed from the following linked profile.
By removing this address you will be leaving the{' '}
{address.community.id}. Your contributions and comments will remain.
Don&apos;t worry, you can rejoin anytime.
</CWText>
<div className="profile">
{profile?.avatarUrl ? (
<img src={profile.avatarUrl} />
) : (
<img
src={`data:image/svg+xml;utf8,${encodeURIComponent(
defaultAvatar,
)}`}
/>
)}
<CWText fontWeight="bold">{name || DEFAULT_NAME}</CWText>
</div>
<div className="confirmation">
<CWText>Are you sure you want to remove this address?</CWText>
<CWTruncatedAddress address={address.address} />
</div>
</CWModalBody>
<CWModalFooter>
<CWButton
Expand All @@ -128,7 +106,7 @@ export const DeleteAddressModal = ({
buttonHeight="sm"
/>
<CWButton
label="Delete"
label="Disconnect Address"
buttonType="destructive"
onClick={handleDelete}
buttonHeight="sm"
Expand Down

0 comments on commit f8805e6

Please sign in to comment.