Skip to content

Commit

Permalink
Merge branch 'refs/heads/feature/support-v2'
Browse files Browse the repository at this point in the history
  • Loading branch information
jaensen committed Jun 21, 2024
2 parents a24b1e0 + 4ba0975 commit a943121
Show file tree
Hide file tree
Showing 24 changed files with 415 additions and 3,336 deletions.
3,256 changes: 0 additions & 3,256 deletions circles-app/package-lock.json

This file was deleted.

56 changes: 28 additions & 28 deletions circles-app/package.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
{
"name": "circles-app",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.4.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.3",
"ethers": "^6.12.1",
"@circles-sdk/data": "0.0.41",
"@circles-sdk/sdk": "0.0.41",
"@circles-sdk/utils": "0.0.41"
},
"type": "module"
"name": "circles-app",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.4.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.3",
"ethers": "^6.12.1",
"@circles-sdk/data": "0.0.45-preview-29",
"@circles-sdk/sdk": "0.0.45-preview-29",
"@circles-sdk/utils": "0.0.45-preview-29"
},
"type": "module"
}
11 changes: 11 additions & 0 deletions circles-app/src/lib/actions/inviteHuman.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {avatar} from "$lib/stores/avatar";
import {get} from "svelte/store";
import {ContractTransactionReceipt} from "ethers";

export async function inviteHuman(contactAddress: string): Promise<ContractTransactionReceipt> {
const avatarValue = get(avatar);
if (!avatarValue) {
throw new Error('Avatar is not initialized');
}
return await avatarValue.inviteHuman(contactAddress);
}
8 changes: 4 additions & 4 deletions circles-app/src/lib/chainConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type {ChainConfig} from "@circles-sdk/sdk";

export const chainConfig: ChainConfig = {
pathfinderUrl: 'https://pathfinder.aboutcircles.com',
circlesRpcUrl: "https://rpc.helsinki.aboutcircles.com",
v1HubAddress: "0x29b9a7fBb8995b2423a71cC17cf9810798F6C543",
v2HubAddress: "0xFFfbD3E62203B888bb8E09c1fcAcE58242674964",
migrationAddress: "0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f"
circlesRpcUrl: "https://chiado-rpc.aboutcircles.com",
v1HubAddress: "0xdbf22d4e8962db3b2f1d9ff55be728a887e47710",
v2HubAddress: "0x2066CDA98F98397185483aaB26A89445addD6740",
migrationAddress: "0x2A545B54bb456A0189EbC53ed7090BfFc4a6Af94"
};
48 changes: 43 additions & 5 deletions circles-app/src/lib/components/AccountInfoHeader.svelte
Original file line number Diff line number Diff line change
@@ -1,18 +1,56 @@
<script lang="ts">
import {avatar} from "$lib/stores/avatar";
import {onDestroy, onMount} from "svelte";
$: address = $avatar?.address;
$: balance = $avatar?.getTotalBalance();
$: mintableAmount = $avatar?.getMintableAmount();
let timeout: NodeJS.Timeout | undefined;
let unsubscribe: (() => void) | undefined;
$:console.log("AccountInfoHeader.svelte: $avatar", $avatar?.avatarInfo);
onMount(() => {
timeout = setTimeout(() => {
mintableAmount = $avatar?.getMintableAmount();
}, 60000);
unsubscribe = $avatar?.events.subscribe(async event => {
if (event.$event !== "CrcV2_TransferBatch"
&& event.$event !== "CrcV2_TransferSingle"
&& event.$event !== "CrcV1_Transfer"
&& event.$event !== "CrcV1_HubTransfer") {
return;
}
balance = $avatar?.getTotalBalance();
mintableAmount = $avatar?.getMintableAmount();
});
});
onDestroy(() => {
clearTimeout(timeout);
unsubscribe?.();
});
</script>

<header class="bg-blue-500 text-white p-4 flex items-center">
<img src="/logo.svg" alt="User Icon" class="w-12 h-12 rounded-full">
<div class="ml-4">
<h1 class="text-xl font-semibold">{address}</h1>
{#await balance}
<p class="text-sm">0 Circles</p>
{:then balance}
<p class="text-sm">{balance?.toFixed(2) ?? 0} Circles</p>
{/await}
<p class="text-sm">
{#await balance}
----.-- Circles
{:then balance}
{balance?.toFixed(2) ?? 0} Circles
{:catch error}
(Error loading balance)
{/await}

{#await mintableAmount}
{:then mintableAmount}
&nbsp;<span class="text-amber-200">({mintableAmount} Circles available to mint)</span>
{/await}
</p>
</div>
</header>
3 changes: 2 additions & 1 deletion circles-app/src/lib/components/ActionButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import {createEventDispatcher} from "svelte";
export let action: () => Promise<any>;
export let title: string = '';
export let doneStateDuration: number = 2000;
export let errorTransitory: boolean = true;
export let disabled: boolean = false;
Expand Down Expand Up @@ -68,7 +69,7 @@
</script>

<button on:click={executeAction}
title="{errorMessage}"
title="{errorMessage ?? title}"
class="ml-2 p-2 px-4 rounded-md {theme[state]} focus:outline-none transition">
{#if state === 'Working'}
<div class="loading-spinner inline-block border-t-2 border-b-2 border-gray-900 rounded-full w-4 h-4 animate-spin"></div>
Expand Down
13 changes: 13 additions & 0 deletions circles-app/src/lib/components/Footer.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<script lang="ts">
import {avatar} from "$lib/stores/avatar";
</script>

<nav class="fixed bottom-0 left-0 right-0 bg-white shadow-lg">
<div class="flex justify-around p-3">
<a href="/dashboard" class="btn bg-blue-500 text-white p-3 rounded-full">
Expand All @@ -21,6 +25,15 @@
d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
</svg>
</a>
{#if $avatar?.avatarInfo?.version === 2}
<a href="/invitations" class="bg-blue-500 text-white p-3 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round"
d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"/>
</svg>
</a>
{/if}
<a href="/settings" class="bg-blue-500 text-white p-3 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-6 h-6">
Expand Down
17 changes: 17 additions & 0 deletions circles-app/src/lib/components/UpdateBanner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">
import {goto} from "$app/navigation";
async function migrateToV2() {
await goto("/migrate-to-v2");
}
</script>
<div class="bg-blue-100 border-t-4 mb-4 border-blue-500 rounded-b text-blue-900 px-4 py-3 shadow-md cursor-pointer"
on:click={() => migrateToV2()}
role="alert">
<div class="flex">
<div>
<p class="font-bold">Circles V2 is here!</p>
<p class="text-sm">Migrate your avatar to Circles V2.</p>
</div>
</div>
</div>
15 changes: 15 additions & 0 deletions circles-app/src/lib/guards/canInvite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type {AvatarRow} from "@circles-sdk/data";

export function canInvite(avatar: AvatarRow | undefined): boolean {
// Invitations only work for human v2 avatars.
// If they have a v1 token, it must be stopped.
if (!avatar) {
return false;
}

return avatar.version === 2
&& avatar.type === "human"
&& (avatar.hasV1
? avatar.v1Stopped === true
: true);
}
19 changes: 19 additions & 0 deletions circles-app/src/lib/guards/canMigrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type {AvatarRow} from "@circles-sdk/data";

export function canMigrate(avatar: AvatarRow | undefined): boolean {
if (!avatar) {
return false;
}

// Pure v1 avatars can migrate
if (avatar.version === 1) {
return true;
}

// v2 avatars can migrate if they have a v1 token and it's not stopped
if (avatar.hasV1 && avatar.version === 2) {
return !avatar.v1Stopped ?? false;
}

return false;
}
4 changes: 2 additions & 2 deletions circles-app/src/lib/stores/avatar.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {writable} from "svelte/store";
import type {AvatarInterface} from "@circles-sdk/sdk";
import {Avatar} from "@circles-sdk/sdk";

/**
* A store that contains an Avatar instance or undefined.
*/
export const avatar = writable<AvatarInterface | undefined>();
export const avatar = writable<Avatar | undefined>();
6 changes: 6 additions & 0 deletions circles-app/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import DefaultHeader from "$lib/components/DefaultHeader.svelte";
import {avatar} from "$lib/stores/avatar";
import Footer from "$lib/components/Footer.svelte";
import {canMigrate} from "$lib/guards/canMigrate";
import UpdateBanner from "$lib/components/UpdateBanner.svelte";
import {page} from "$app/stores";
</script>

{#if $avatar}
Expand All @@ -14,6 +17,9 @@
{/if}

<main class="p-4">
{#if $avatar && canMigrate($avatar.avatarInfo) && $page.route.id !== "/migrate-to-v2"}
<UpdateBanner></UpdateBanner>
{/if}
<slot></slot>
</main>

Expand Down
12 changes: 12 additions & 0 deletions circles-app/src/routes/+layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {redirect} from "@sveltejs/kit";
import {wallet} from "$lib/stores/wallet";
import {get} from "svelte/store";

export const load = (params): void => {
// Redirect to 'connect wallet' if not connected
if (params.route.id !== '/connect-wallet'
&& params.route.id !== '/'
&& get(wallet) === undefined) {
redirect(302, '/connect-wallet');
}
};
15 changes: 4 additions & 11 deletions circles-app/src/routes/connect-wallet/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@
return new Sdk(chainConfig, $wallet!);
}
async function isCirclesWallet(address: string) {
// Use the data object from the circles sdk to check if the address
// is a registered Circles wallet.
const avatarRow = await $circles?.data.getAvatarInfo(address);
return !!avatarRow;
}
//
// Connects the wallet and initializes the Circles SDK.
//
Expand All @@ -46,14 +39,14 @@
$circles = await initializeSdk();
const walletAddress = await $wallet.getAddress();
const alreadyRegistered = await isCirclesWallet(walletAddress);
const avatarInfo = await $circles.data.getAvatarInfo(walletAddress);
// If the signer address is already a registered Circles wallet, go straight to the dashboard.
if (alreadyRegistered) {
if (avatarInfo) {
$avatar = await $circles.getAvatar(walletAddress);
goto("/dashboard");
await goto("/dashboard");
} else {
goto("/register");
await goto("/register");
}
}
</script>
Expand Down
43 changes: 39 additions & 4 deletions circles-app/src/routes/contacts/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,55 @@
<script context="module" lang="ts">
import type {TrustRelationRow} from "@circles-sdk/sdk";
export type TrustRelationRowWithInvitationFlag = TrustRelationRow & {
invitedByMe?: boolean;
};
</script>
<script lang="ts">
import ActionButton from "$lib/components/ActionButton.svelte";
import {goto} from "$app/navigation";
import List from "$lib/components/List.svelte";
import {onMount} from "svelte";
import {avatar} from "$lib/stores/avatar";
import type {TrustRelationRow} from "@circles-sdk/sdk";
import {type InvitationRow} from "@circles-sdk/data";
import ContactRow from "./components/ContactRow.svelte";
import {circles} from "$lib/stores/circles";
$: rows = <TrustRelationRow[]>[];
$: rows = <TrustRelationRowWithInvitationFlag[]>[];
onMount(async () => {
async function refresh() {
rows = await $avatar?.getTrustRelations() ?? [];
const invitationsQuery = $circles?.data?.getInvitations($avatar!.address, 1000);
let invitations: Record<string, InvitationRow> = {};
if (await invitationsQuery?.queryNextPage()) {
const invitationsResult = invitationsQuery?.currentPage?.results ?? [];
invitations = invitationsResult.reduce((p, c) => {
p[c.invited] = c;
return p;
}, <Record<string, any>>{});
}
for (const row of rows) {
row.invitedByMe = !!invitations[row.objectAvatar];
}
rows = rows;
}
onMount(() => {
refresh();
return $avatar?.events.subscribe(async event => {
if (event.$event !== "CrcV1_Trust"
&& event.$event !== "CrcV2_Trust"
&& event.$event !== "CrcV2_InviteHuman") {
return;
}
await refresh();
});
});
async function addContact() {
goto('/contacts/add');
await goto('/contacts/add');
}
</script>

Expand Down
Loading

0 comments on commit a943121

Please sign in to comment.