Skip to content

Commit

Permalink
feat: extend and improve address select modal
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandros Kalogerakis committed Nov 4, 2024
1 parent 54c139c commit adb61d4
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 135 deletions.
179 changes: 173 additions & 6 deletions src/composables/accountSelector.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,201 @@
import { useAddressBook } from '@/composables/addressBook';
import { ref } from 'vue';
import { ACCOUNT_SELECT_TYPE_FILTER, AccountSelectTypeFilter } from '@/constants';
import { createCustomScopedComposable } from '@/utils';
import { computed, ref, watch } from 'vue';

import {
IAddressBookEntry,
type ICommonTransaction,
type ITransaction,
} from '@/types';
import {
ACCOUNT_SELECT_TYPE_FILTER,
AccountSelectTypeFilter,
PROTOCOLS,
TX_DIRECTION,
} from '@/constants';
import { createCustomScopedComposable, getDefaultAccountLabel, pipe } from '@/utils';
import { useAccounts, useLatestTransactionList } from '@/composables';

import { useAeMiddleware } from '@/protocols/aeternity/composables';
import { useAeNames } from '@/protocols/aeternity/composables/aeNames';
import {
getInnerTransaction,
getOwnershipStatus,
getTxDirection,
getTxOwnerAddress,
} from '@/protocols/aeternity/helpers';
import { AE_TRANSACTION_OWNERSHIP_STATUS } from '@/protocols/aeternity/config';

export const useAccountSelector = createCustomScopedComposable(() => {
const { getName } = useAeNames();
const { getMiddleware } = useAeMiddleware();
const latestTransactions = ref<IAddressBookEntry[]>([]);
const searchQuery = ref<string>('');
const {
addressBookFiltered,
addressBookFilteredByProtocol,
protocolFilter,
searchQuery,
showBookmarked,
getAddressBookEntryByAddress,
setProtocolFilter,
setShowBookmarked,
clearFilters: addressBookClearFilters,
} = useAddressBook();
const {
accounts,
activeAccount,
accountsGroupedByProtocol,
getAccountByAddress,
} = useAccounts();
const { accountsTransactionsLatest } = useLatestTransactionList();
const accountSelectType = ref<AccountSelectTypeFilter>(ACCOUNT_SELECT_TYPE_FILTER.addressBook);
const prevAccountSelectType = ref<AccountSelectTypeFilter>(accountSelectType.value);
const ownAddresses = computed(() => {
if (protocolFilter.value) {
return accountsGroupedByProtocol.value[protocolFilter.value]?.map((account) => (
{
name: getName(account.address).value || getDefaultAccountLabel(account),
address: account.address,
isBookmarked: false,
protocol: protocolFilter.value ?? PROTOCOLS.aeternity,
isOwnAddress: true,
type: account.type,
}
));
}
return [];
});
const accountsFilteredByType = computed(
() => {
switch (accountSelectType.value) {
case ACCOUNT_SELECT_TYPE_FILTER.bookmarked:
return addressBookFiltered.value;
case ACCOUNT_SELECT_TYPE_FILTER.addressBook:
return addressBookFiltered.value;
case ACCOUNT_SELECT_TYPE_FILTER.recent:
return latestTransactions.value;
case ACCOUNT_SELECT_TYPE_FILTER.owned:
return ownAddresses.value;
case ACCOUNT_SELECT_TYPE_FILTER.all:
return [...(addressBookFiltered.value ?? []), ...(ownAddresses.value ?? [])];
default:
return [];
}
},
);
function filterAccountsBookBySearchPhrase(entries: IAddressBookEntry[]) {
const searchQueryLower = searchQuery.value.toLowerCase();
return entries.filter(({ name, address }) => (
[name, address].some((val) => val.toLowerCase().includes(searchQueryLower))
));
}
function sortAccounts(entries: IAddressBookEntry[]) {
return entries.sort((a, b) => {
if (a === b) return 0;
return a ? 1 : -1;
});
}
function removeDuplicates(entries: IAddressBookEntry[]) {
return entries.filter(
(addr1, i, addresses) => addresses.findIndex(
(addr2) => addr2.address === addr1.address,
) === i,
);
}
const accountsFiltered = computed(
() => pipe([
filterAccountsBookBySearchPhrase,
sortAccounts,
removeDuplicates,
])(accountsFilteredByType.value ?? []),
);

function setAccountSelectType(type: AccountSelectTypeFilter, resetProtocolFilter = false) {
accountSelectType.value = type;
setShowBookmarked(type === ACCOUNT_SELECT_TYPE_FILTER.bookmarked, resetProtocolFilter);
}

function clearFilters(resetProtocolFilter = false) {
accountSelectType.value = ACCOUNT_SELECT_TYPE_FILTER.addressBook;
addressBookClearFilters(resetProtocolFilter);
}

watch(
() => [accountsTransactionsLatest.value, activeAccount.value.address, protocolFilter.value],
async () => {
const filteredTransactions = accountsTransactionsLatest
.value[activeAccount.value.address].filter(
(transaction: ICommonTransaction) => {
const outerTx = transaction.tx!;
const innerTx = transaction.tx ? getInnerTransaction(transaction.tx) : null;
const txOwnerAddress = getTxOwnerAddress(innerTx);

const direction = getTxDirection(
outerTx?.payerId ? outerTx : innerTx,
(transaction as ITransaction).transactionOwner
|| (
(
getOwnershipStatus(activeAccount.value, accounts.value, innerTx)
!== AE_TRANSACTION_OWNERSHIP_STATUS.current
)
&& txOwnerAddress
)
|| activeAccount.value.address,
);

return (
direction === TX_DIRECTION.sent
&& (outerTx?.payerId ? outerTx : innerTx).recipientId
);
},
);

latestTransactions.value = await Promise.all(
filteredTransactions
.map(async (transaction: ICommonTransaction): Promise<IAddressBookEntry> => {
const outerTx = transaction.tx!;
const innerTx = transaction.tx ? getInnerTransaction(transaction.tx) : null;
const { recipientId } = outerTx?.payerId ? outerTx : innerTx;
const middleware = await getMiddleware();

let address = recipientId;
const accountFound = getAccountByAddress(recipientId!);
let name = getName(accountFound?.address).value
|| getDefaultAccountLabel(accountFound);
if (recipientId?.startsWith('nm_')) {
address = (await middleware.getName(recipientId)).name;
name = address;
}
const addressBookEntryByAddress = getAddressBookEntryByAddress(address);
if (addressBookEntryByAddress) {
return addressBookEntryByAddress;
}
return {
name,
address,
isBookmarked: false,
protocol: protocolFilter.value ?? PROTOCOLS.aeternity,
};
}),
);
},
{ immediate: true }, // Run immediately on initialization
);

// Storing the previous type in order to revert to it when the input is cleared
let savedPrevAccountSelectType = false;
watch(searchQuery, (newSearch) => {
if (newSearch !== '' && !savedPrevAccountSelectType) {
savedPrevAccountSelectType = true;
prevAccountSelectType.value = accountSelectType.value;
accountSelectType.value = ACCOUNT_SELECT_TYPE_FILTER.all;
} else if (newSearch === '') {
savedPrevAccountSelectType = false;
accountSelectType.value = prevAccountSelectType.value;
}
});

return {
accountSelectType,
addressBookFiltered,
accountsFiltered,
addressBookFilteredByProtocol,
protocolFilter,
showBookmarked,
Expand Down
2 changes: 2 additions & 0 deletions src/popup/components/AddressBook/AddressBookFilters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/>

<BtnFilter
v-if="hasBookmarkedEntries || !isSelector"
:is-active="accountSelectType === ACCOUNT_SELECT_TYPE_FILTER.bookmarked"
data-cy="bookmarked-filter"
@click="() => setAccountSelectType(ACCOUNT_SELECT_TYPE_FILTER.bookmarked, !isSelector)"
Expand Down Expand Up @@ -105,6 +106,7 @@ export default defineComponent({
},
props: {
isSelector: Boolean,
hasBookmarkedEntries: Boolean,
},
setup() {
const {
Expand Down
Loading

0 comments on commit adb61d4

Please sign in to comment.