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

Display failed imported token #5439

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
24ed897
New type UserTokenFailed
mstrasinskis Sep 11, 2024
d7f8ff8
Add UserTokenFailed records to the table
mstrasinskis Sep 11, 2024
eb8367f
Display failed token info
mstrasinskis Sep 11, 2024
5de0283
Add compareFailedTokensLast for table sorting
mstrasinskis Sep 11, 2024
811fab5
Use question-mark.svg for failed to load token
mstrasinskis Sep 12, 2024
99e258f
style: change (i) icon colour
mstrasinskis Sep 12, 2024
e8362fa
Add actions for failed imported token
mstrasinskis Sep 12, 2024
665eb37
Display unknown balance
mstrasinskis Sep 12, 2024
ce544fb
style: Fix squashed logos on mobile
mstrasinskis Sep 12, 2024
ce319c8
Merge branch 'main' into mstr/display-failed-imported-token
mstrasinskis Sep 12, 2024
6cc7aab
Separate UserTokenFailedAction and UserTokenAction
mstrasinskis Sep 13, 2024
2961080
Remove unused import
mstrasinskis Sep 13, 2024
ce814af
Fix actions types
mstrasinskis Sep 13, 2024
69c24e0
Merge remote-tracking branch 'origin/main' into mstr/display-failed-i…
mstrasinskis Sep 15, 2024
2a29253
Unify UserTokenActions
mstrasinskis Sep 15, 2024
6ec688b
Add failed token tooltip
mstrasinskis Sep 15, 2024
cc4656b
Move imported token remove logic to the confirmation modal
mstrasinskis Sep 15, 2024
2474637
Revert "Move imported token remove logic to the confirmation modal"
mstrasinskis Sep 15, 2024
e481ec4
Reapply "Move imported token remove logic to the confirmation modal"
mstrasinskis Sep 15, 2024
a24c9e1
refactor: move token remove logic to confirmation spec
mstrasinskis Sep 15, 2024
2cd7c7c
Reset failed imported tokens store after loading
mstrasinskis Sep 16, 2024
2b2762d
Fix imports
mstrasinskis Sep 16, 2024
77de561
Remove failed imported token button
mstrasinskis Sep 16, 2024
db81328
Test that failed store is reset on imported loading
mstrasinskis Sep 16, 2024
356e6c3
refactor: removeImportedToken to show busy screen
mstrasinskis Sep 16, 2024
69a5dab
Fix ImportTokenRemoveConfirmation props
mstrasinskis Sep 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions frontend/src/lib/assets/question-mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 5 additions & 22 deletions frontend/src/lib/components/accounts/IcrcWalletPage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@
} from "$lib/utils/accounts.utils";
import { replacePlaceholders } from "$lib/utils/i18n.utils";
import { IconDots, Island, Spinner } from "@dfinity/gix-components";
import type { Principal } from "@dfinity/principal";
import { Principal } from "@dfinity/principal";
import { TokenAmountV2, isNullish, nonNullish } from "@dfinity/utils";
import type { Writable } from "svelte/store";
import { ENABLE_IMPORT_TOKEN } from "$lib/stores/feature-flags.store";
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
import WalletMorePopover from "./WalletMorePopover.svelte";
import { isImportedToken as checkImportedToken } from "$lib/utils/imported-tokens.utils";
import { importedTokensStore } from "$lib/stores/imported-tokens.store";
import { startBusy, stopBusy } from "$lib/stores/busy.store";
import { removeImportedTokens } from "$lib/services/imported-tokens.services";
import ImportTokenRemoveConfirmation from "./ImportTokenRemoveConfirmation.svelte";
import type { Universe } from "$lib/types/universe";
Expand Down Expand Up @@ -173,26 +172,9 @@
// Just for type safety. This should never happen.
if (isNullish(ledgerCanisterId)) return;

startBusy({
initiator: "import-token-removing",
labelKey: "import_token.removing",
});

try {
const importedTokens = $importedTokensStore.importedTokens ?? [];
const { success } = await removeImportedTokens({
tokensToRemove: importedTokens.filter(
({ ledgerCanisterId: id }) =>
id.toText() === ledgerCanisterId?.toText()
),
importedTokens,
});

if (success) {
goto(AppPath.Tokens);
}
} finally {
stopBusy("import-token-removing");
const { success } = await removeImportedTokens(ledgerCanisterId);
if (success) {
goto(AppPath.Tokens);
}
};
</script>
Expand Down Expand Up @@ -273,6 +255,7 @@

{#if removeImportedTokenConfirmationVisible && nonNullish(universe)}
<ImportTokenRemoveConfirmation
ledgerCanisterId={Principal.fromText(universe.canisterId)}
{universe}
on:nnsClose={() => (removeImportedTokenConfirmationVisible = false)}
on:nnsConfirm={removeImportedToken}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import { nonNullish } from "@dfinity/utils";
import ConfirmationModal from "$lib/modals/common/ConfirmationModal.svelte";
import { Tag } from "@dfinity/gix-components";
import type { Principal } from "@dfinity/principal";

export let universe: Universe | undefined;
export let universe: Universe | undefined = undefined;
export let ledgerCanisterId: Principal;
</script>

<ConfirmationModal
Expand All @@ -18,7 +20,13 @@
<div class="content">
<h4>{$i18n.import_token.remove_confirmation_header}</h4>
<div class="token">
{#if nonNullish(universe)}<UniverseSummary {universe} />{/if}
{#if nonNullish(universe)}
<UniverseSummary {universe} />
{:else}
<span class="value" data-tid="ledger-canister-id"
>{ledgerCanisterId.toText()}</span
>
{/if}
<Tag>{$i18n.import_token.imported_token}</Tag>
</div>
<p class="description text_small">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
<script lang="ts">
import {
UserTokenAction,
type UserTokenFailed,
type UserTokenData,
type UserTokenLoading,
} from "$lib/types/tokens-page";
import { isUserTokenData } from "$lib/utils/user-token.utils";
import {
isUserTokenData,
isUserTokenFailed,
} from "$lib/utils/user-token.utils";
import GoToDashboardButton from "./actions/GoToDashboardButton.svelte";
import GoToDetailIcon from "./actions/GoToDetailIcon.svelte";
import ReceiveButton from "./actions/ReceiveButton.svelte";
import RemoveButton from "./actions/RemoveButton.svelte";
import SendButton from "./actions/SendButton.svelte";
import { nonNullish } from "@dfinity/utils";
import type { SvelteComponent, ComponentType } from "svelte";

export let rowData: UserTokenData | UserTokenLoading;
export let rowData: UserTokenData | UserTokenLoading | UserTokenFailed;

const actionMapper: Record<
UserTokenAction,
ComponentType<SvelteComponent<{ userToken: UserTokenData }>>
ComponentType<
SvelteComponent<{ userToken: UserTokenData | UserTokenFailed }>
>
> = {
[UserTokenAction.GoToDetail]: GoToDetailIcon,
[UserTokenAction.Receive]: ReceiveButton,
[UserTokenAction.Send]: SendButton,
[UserTokenAction.GoToDashboard]: GoToDashboardButton,
[UserTokenAction.Remove]: RemoveButton,
};

let userToken: UserTokenData | undefined;
$: userToken = isUserTokenData(rowData) ? rowData : undefined;
let userToken: UserTokenData | UserTokenFailed | undefined;
$: userToken =
isUserTokenData(rowData) || isUserTokenFailed(rowData)
? rowData
: undefined;
</script>

{#if nonNullish(userToken)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
<script lang="ts">
import AmountDisplay from "$lib/components/ic/AmountDisplay.svelte";
import type { UserTokenData, UserTokenLoading } from "$lib/types/tokens-page";
import type {
UserTokenData,
UserTokenFailed,
UserTokenLoading,
} from "$lib/types/tokens-page";
import { Spinner } from "@dfinity/gix-components";
import {
isUserTokenData,
isUserTokenLoading,
} from "$lib/utils/user-token.utils";

export let rowData: UserTokenData | UserTokenLoading;
export let rowData: UserTokenData | UserTokenLoading | UserTokenFailed;
</script>

{#if rowData.balance === "loading"}
{#if isUserTokenLoading(rowData)}
<span data-tid="token-value-label" class="balance-spinner"
><Spinner inline size="tiny" /></span
>
{:else}
{:else if isUserTokenData(rowData)}
<AmountDisplay singleLine amount={rowData.balance} />
{:else}
<span data-tid="unavailable-balance" class="value">-/-</span>
{/if}

<style lang="scss">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
<script lang="ts">
import type { UserTokenData, UserTokenLoading } from "$lib/types/tokens-page";
import type {
UserTokenData,
UserTokenFailed,
UserTokenLoading,
} from "$lib/types/tokens-page";
import { importedTokensStore } from "$lib/stores/imported-tokens.store";
import { isImportedToken } from "$lib/utils/imported-tokens.utils";
import Logo from "../../ui/Logo.svelte";
import Logo from "$lib/components/ui/Logo.svelte";
import { nonNullish } from "@dfinity/utils";
import { Tag } from "@dfinity/gix-components";
import { IconError, Tag, Tooltip } from "@dfinity/gix-components";
import { i18n } from "$lib/stores/i18n";
import Hash from "$lib/components/ui/Hash.svelte";
import { isUserTokenFailed } from "$lib/utils/user-token.utils";

export let rowData: UserTokenData | UserTokenLoading;
export let rowData: UserTokenData | UserTokenLoading | UserTokenFailed;

let importedToken = false;
$: importedToken = isImportedToken({
Expand All @@ -18,17 +24,35 @@

<div class="title-logo-wrapper">
<Logo src={rowData.logo} alt={rowData.title} size="medium" framed />
<div class="title-wrapper">
<h5 data-tid="project-name">{rowData.title}</h5>
{#if nonNullish(rowData.subtitle)}
<span data-tid="project-subtitle" class="description"
>{rowData.subtitle}</span
>
{/if}
</div>
{#if isUserTokenFailed(rowData)}
<Hash
testId="failed-ledger-canister-id"
text={`${rowData.universeId.toText()}`}
tagName="span"
splitLength={6}
tooltipTop
/>
{:else}
<div class="title-wrapper">
<h5 data-tid="project-name">{rowData.title}</h5>
{#if nonNullish(rowData.subtitle)}
<span data-tid="project-subtitle" class="description"
>{rowData.subtitle}</span
>
{/if}
</div>
{/if}
{#if importedToken}
<Tag testId="imported-token-tag">{$i18n.import_token.imported_token}</Tag>
{/if}
{#if isUserTokenFailed(rowData)}
<Tooltip
id="failed-imported-token"
text={$i18n.import_token.failed_tooltip}
>
<div class="failed-token-icon"><IconError size="20px" /></div>
</Tooltip>
{/if}
</div>

<style lang="scss">
Expand All @@ -40,11 +64,18 @@
display: flex;
align-items: center;
gap: var(--padding);
// Fix squashed logo for failed imported tokens caused by too many elements being displayed.
flex-wrap: wrap;

.title-wrapper {
display: flex;
flex-direction: column;
gap: var(--padding-0_5x);
}
}

.failed-token-icon {
color: var(--orange);
line-height: 0;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import type { Principal } from "@dfinity/principal";
import LinkToDashboardCanister from "$lib/components/tokens/LinkToDashboardCanister.svelte";

export let userToken: UserTokenData | UserTokenFailed;

let canisterId: Principal;
$: canisterId = userToken.universeId;
</script>

<LinkToDashboardCanister {canisterId} />
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts">
import type { UserTokenData } from "$lib/types/tokens-page";
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconRight } from "@dfinity/gix-components";

// svelte-ignore unused-export-let
export let userToken: UserTokenData;
export let userToken: UserTokenData | UserTokenFailed;
</script>

<span data-tid="go-to-detail-icon-component">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
<script lang="ts">
import { ActionType } from "$lib/types/actions";
import type { UserTokenData } from "$lib/types/tokens-page";
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconQRCodeScanner } from "@dfinity/gix-components";
import { createEventDispatcher } from "svelte";
import { isUserTokenData } from "$lib/utils/user-token.utils";

export let userToken: UserTokenData;
// The UserTokenFailed type was added to unify the action types.
// However, the Receive action is only applicable to UserTokenData due to the requirement for the account to be loaded.
export let userToken: UserTokenData | UserTokenFailed;

const dispatcher = createEventDispatcher();
</script>

<button
class="icon-only"
data-tid="receive-button-component"
on:click|preventDefault|stopPropagation={() => {
dispatcher("nnsAction", { type: ActionType.Receive, data: userToken });
}}
>
<IconQRCodeScanner />
</button>
{#if isUserTokenData(userToken)}
<button
class="icon-only"
data-tid="receive-button-component"
on:click|preventDefault|stopPropagation={() => {
dispatcher("nnsAction", { type: ActionType.Receive, data: userToken });
}}
>
<IconQRCodeScanner />
</button>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconBin } from "@dfinity/gix-components";
import { createEventDispatcher } from "svelte";
import { ActionType } from "$lib/types/actions";

// svelte-ignore unused-export-let
export let userToken: UserTokenData | UserTokenFailed;

const dispatcher = createEventDispatcher();
</script>

<button
class="icon-only remove-button"
data-tid="send-button-component"
on:click|preventDefault|stopPropagation={() => {
dispatcher("nnsAction", { type: ActionType.Remove, data: userToken });
}}
>
<IconBin />
</button>

<style lang="scss">
.remove-button {
color: var(--negative-emphasis);
}
</style>
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
<script lang="ts">
import { ActionType } from "$lib/types/actions";
import type { UserTokenData } from "$lib/types/tokens-page";
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconUp } from "@dfinity/gix-components";
import { createEventDispatcher } from "svelte";
import { isUserTokenData } from "$lib/utils/user-token.utils";

export let userToken: UserTokenData;
// The UserTokenFailed type was added to unify the action types.
// However, the Receive action is only applicable to UserTokenData due to the requirement for the account to be loaded.
export let userToken: UserTokenData | UserTokenFailed;

const dispatcher = createEventDispatcher();
</script>

<button
class="icon-only"
data-tid="send-button-component"
on:click|stopPropagation|preventDefault={() => {
dispatcher("nnsAction", { type: ActionType.Send, data: userToken });
}}
>
<IconUp />
</button>
{#if isUserTokenData(userToken)}
<button
class="icon-only"
data-tid="send-button-component"
on:click|stopPropagation|preventDefault={() => {
dispatcher("nnsAction", { type: ActionType.Send, data: userToken });
}}
>
<IconUp />
</button>
{/if}
Loading
Loading