From 08ea8c24fb5ff3d61fd3a42116b751988ddd5ace Mon Sep 17 00:00:00 2001 From: harshbaz <106158714+harshbaz@users.noreply.github.com> Date: Sat, 27 Aug 2022 15:12:28 +0530 Subject: [PATCH] Add call to fetch user's canister (#239) * Update User template declaration in canister * Update declarations with types in frontend * Ignore declarations folders * Add dummy Ids of declared canisters * Add example env variable * Ignore implicit any errors from ts * Add userCanister to user store * Update canisterId if logged in * Update backend util to cache actor & use identity * Add userCanisterPrincipal to auth and authHelper --- .eslintignore | 1 + canister_ids.json | 6 +++ .../individual_user_template.did | 7 ++- .../individual_user_template.did.d.ts | 7 ++- .../individual_user_template.did.js | 11 ++-- .../declarations/user_index/index.js | 38 +++++++++++++ .../user_index/user_index.did.d.ts | 7 +++ .../declarations/user_index/user_index.did.js | 7 +++ packages/web-client/src/lib/authHelper.ts | 9 +++- packages/web-client/src/lib/backend.ts | 54 ++++++++++++++++--- packages/web-client/src/stores/auth.ts | 1 + packages/web-client/tsconfig.json | 1 + packages/web-client/vite.config.js | 3 +- 13 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 packages/web-client/packages/web-client/declarations/user_index/index.js create mode 100644 packages/web-client/packages/web-client/declarations/user_index/user_index.did.d.ts create mode 100644 packages/web-client/packages/web-client/declarations/user_index/user_index.did.js diff --git a/.eslintignore b/.eslintignore index 20dcb3c55..531c9bfb2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,3 +11,4 @@ package pnpm-lock.yaml package-lock.json yarn.lock +declarations diff --git a/canister_ids.json b/canister_ids.json index 94dcc646d..c62f51756 100644 --- a/canister_ids.json +++ b/canister_ids.json @@ -1,5 +1,11 @@ { "webclient": { "ic": "vyatz-hqaaa-aaaam-qauea-cai" + }, + "individual_user_template": { + "local": "rwlgt-iiaaa-aaaaa-aaaaa-cai" + }, + "user_index": { + "local": "ryjl3-tyaaa-aaaaa-aaaba-cai" } } diff --git a/packages/canisters/individual_user_template/individual_user_template.did b/packages/canisters/individual_user_template/individual_user_template.did index 73e31ed10..25ff3d87c 100644 --- a/packages/canisters/individual_user_template/individual_user_template.did +++ b/packages/canisters/individual_user_template/individual_user_template.did @@ -1,4 +1,9 @@ +type PostDetailsFromFrontend = record { + hashtags : vec text; + description : text; + video_url : text; +}; service : { - create_post : (text, vec text, text) -> (nat64); + create_post : (PostDetailsFromFrontend) -> (nat64); mark_post_as_ready_to_view : (nat64) -> (); } \ No newline at end of file diff --git a/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts b/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts index ddf06aeeb..1dbf229b8 100644 --- a/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts +++ b/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts @@ -1,7 +1,12 @@ import type { Principal } from '@dfinity/principal'; import type { ActorMethod } from '@dfinity/agent'; +export interface PostDetailsFromFrontend { + 'hashtags' : Array, + 'description' : string, + 'video_url' : string, +} export interface _SERVICE { - 'create_post' : ActorMethod<[string, Array, string], bigint>, + 'create_post' : ActorMethod<[PostDetailsFromFrontend], bigint>, 'mark_post_as_ready_to_view' : ActorMethod<[bigint], undefined>, } diff --git a/packages/web-client/declarations/individual_user_template/individual_user_template.did.js b/packages/web-client/declarations/individual_user_template/individual_user_template.did.js index b3d52410a..06f170d41 100644 --- a/packages/web-client/declarations/individual_user_template/individual_user_template.did.js +++ b/packages/web-client/declarations/individual_user_template/individual_user_template.did.js @@ -1,10 +1,11 @@ export const idlFactory = ({ IDL }) => { + const PostDetailsFromFrontend = IDL.Record({ + 'hashtags' : IDL.Vec(IDL.Text), + 'description' : IDL.Text, + 'video_url' : IDL.Text, + }); return IDL.Service({ - 'create_post' : IDL.Func( - [IDL.Text, IDL.Vec(IDL.Text), IDL.Text], - [IDL.Nat64], - [], - ), + 'create_post' : IDL.Func([PostDetailsFromFrontend], [IDL.Nat64], []), 'mark_post_as_ready_to_view' : IDL.Func([IDL.Nat64], [], []), }); }; diff --git a/packages/web-client/packages/web-client/declarations/user_index/index.js b/packages/web-client/packages/web-client/declarations/user_index/index.js new file mode 100644 index 000000000..d9938c16c --- /dev/null +++ b/packages/web-client/packages/web-client/declarations/user_index/index.js @@ -0,0 +1,38 @@ +import { Actor, HttpAgent } from "@dfinity/agent"; + +// Imports and re-exports candid interface +import { idlFactory } from './user_index.did.js'; +export { idlFactory } from './user_index.did.js'; +// CANISTER_ID is replaced by webpack based on node environment +export const canisterId = process.env.USER_INDEX_CANISTER_ID; + +/** + * + * @param {string | import("@dfinity/principal").Principal} canisterId Canister ID of Agent + * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig}} [options] + * @return {import("@dfinity/agent").ActorSubclass} + */ +export const createActor = (canisterId, options) => { + const agent = new HttpAgent(options ? { ...options.agentOptions } : {}); + + // Fetch root key for certificate validation during development + if (process.env.NODE_ENV !== "production") { + agent.fetchRootKey().catch(err => { + console.warn("Unable to fetch root key. Check to ensure that your local replica is running"); + console.error(err); + }); + } + + // Creates an actor with using the candid interface and the HttpAgent + return Actor.createActor(idlFactory, { + agent, + canisterId, + ...(options ? options.actorOptions : {}), + }); +}; + +/** + * A ready-to-use agent for the user_index canister + * @type {import("@dfinity/agent").ActorSubclass} + */ +export const user_index = createActor(canisterId); diff --git a/packages/web-client/packages/web-client/declarations/user_index/user_index.did.d.ts b/packages/web-client/packages/web-client/declarations/user_index/user_index.did.d.ts new file mode 100644 index 000000000..65ac99069 --- /dev/null +++ b/packages/web-client/packages/web-client/declarations/user_index/user_index.did.d.ts @@ -0,0 +1,7 @@ +import type { Principal } from '@dfinity/principal'; +import type { ActorMethod } from '@dfinity/agent'; + +export interface _SERVICE { + '__comment_this_reset_user_canisters' : ActorMethod<[], undefined>, + 'get_users_canister' : ActorMethod<[], Principal>, +} diff --git a/packages/web-client/packages/web-client/declarations/user_index/user_index.did.js b/packages/web-client/packages/web-client/declarations/user_index/user_index.did.js new file mode 100644 index 000000000..f3ad87695 --- /dev/null +++ b/packages/web-client/packages/web-client/declarations/user_index/user_index.did.js @@ -0,0 +1,7 @@ +export const idlFactory = ({ IDL }) => { + return IDL.Service({ + '__comment_this_reset_user_canisters' : IDL.Func([], [], []), + 'get_users_canister' : IDL.Func([], [IDL.Principal], []), + }); +}; +export const init = ({ IDL }) => { return []; }; diff --git a/packages/web-client/src/lib/authHelper.ts b/packages/web-client/src/lib/authHelper.ts index b6aa64b2f..89cd50bbd 100644 --- a/packages/web-client/src/lib/authHelper.ts +++ b/packages/web-client/src/lib/authHelper.ts @@ -1,6 +1,7 @@ import { AuthClient } from '@dfinity/auth-client'; import { get } from 'svelte/store'; import { auth } from '../stores/auth'; +import { userIndex } from './backend'; export async function initializeAuthClient(): Promise { let authStore = get(auth); @@ -13,20 +14,24 @@ export async function initializeAuthClient(): Promise { } const identity = authStore.client?.getIdentity(); const principal = await identity?.getPrincipal(); + const userCanisterPrincipal = await userIndex().get_users_canister(); + if (await authStore.client?.isAuthenticated()) { auth.set({ client: authStore.client, isLoggedIn: true, identity, principal, - showLogin: false + showLogin: false, + userCanisterPrincipal }); } else { auth.set({ client: authStore.client, isLoggedIn: false, principal, - showLogin: authStore.showLogin + showLogin: authStore.showLogin, + userCanisterPrincipal }); } } diff --git a/packages/web-client/src/lib/backend.ts b/packages/web-client/src/lib/backend.ts index b9f746a8d..304b996ba 100644 --- a/packages/web-client/src/lib/backend.ts +++ b/packages/web-client/src/lib/backend.ts @@ -1,10 +1,52 @@ -import { createActor, canisterId } from '../../declarations/backend'; +import { + createActor as createUserIndexActor, + canisterId as userIndexCanisterId +} from '$canisters/user_index'; +import { createActor as createIndividualUserActor } from '$canisters/individual_user_template'; +import type { _SERVICE as _USER_INDEX_SERVICE } from '$canisters/user_index/user_index.did'; +import type { _SERVICE as _INDIVIDUAL_USER_SERVICE } from '$canisters/individual_user_template/individual_user_template.did'; +import { auth } from '$stores/auth'; +import type { ActorSubclass } from '@dfinity/agent'; +import { get } from 'svelte/store'; export const host = process.env.NODE_ENV === 'development' ? 'http://localhost:8000' : 'https://ic0.app'; -export const backend = createActor(canisterId ?? '', { - agentOptions: { - host - } -}); +export type UserIndexActor = ActorSubclass<_USER_INDEX_SERVICE>; +export type IndividualUserCanister = ActorSubclass<_INDIVIDUAL_USER_SERVICE>; + +const userIndexCanister: { + actor?: UserIndexActor; + loginState: boolean; +} = { + loginState: false +}; + +const individualUserCanister: { + actor?: IndividualUserCanister; + loginState: boolean; +} = { + loginState: false +}; + +export function userIndex(): UserIndexActor { + const authStore = get(auth); + if (!userIndexCanister.actor || userIndexCanister.loginState != authStore.isLoggedIn) { + userIndexCanister.actor = createUserIndexActor(userIndexCanisterId as string, { + agentOptions: { identity: authStore?.identity, host } + }) as UserIndexActor; + userIndexCanister.loginState = authStore.isLoggedIn; + return userIndexCanister.actor; + } else return userIndexCanister.actor; +} + +export function individualUser(individualUserCanisterId: string): IndividualUserCanister { + const authStore = get(auth); + if (!individualUserCanister.actor || individualUserCanister.loginState != authStore.isLoggedIn) { + individualUserCanister.actor = createIndividualUserActor(individualUserCanisterId as string, { + agentOptions: { identity: authStore?.identity, host } + }) as IndividualUserCanister; + individualUserCanister.loginState = authStore.isLoggedIn; + return individualUserCanister.actor; + } else return individualUserCanister.actor; +} diff --git a/packages/web-client/src/stores/auth.ts b/packages/web-client/src/stores/auth.ts index 66ffcc907..f5f6ffa0a 100644 --- a/packages/web-client/src/stores/auth.ts +++ b/packages/web-client/src/stores/auth.ts @@ -9,6 +9,7 @@ export const auth = writable<{ identity?: Identity; principal?: Principal; showLogin: boolean; + userCanisterPrincipal?: Principal; }>({ client: undefined, isLoggedIn: false, diff --git a/packages/web-client/tsconfig.json b/packages/web-client/tsconfig.json index 85edcd005..05bd4c8b2 100644 --- a/packages/web-client/tsconfig.json +++ b/packages/web-client/tsconfig.json @@ -2,6 +2,7 @@ "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "composite": true, + "noImplicitAny": false, "paths": { "$lib": ["src/lib"], "$lib/*": ["src/lib/*"], diff --git a/packages/web-client/vite.config.js b/packages/web-client/vite.config.js index c033a9fd4..d577a852c 100644 --- a/packages/web-client/vite.config.js +++ b/packages/web-client/vite.config.js @@ -52,7 +52,8 @@ export default ({ mode }) => { // Here we can define global constants // This is required for now because the code generated by dfx relies on process.env being set ...canisterDefinitions, - 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production') + 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'), + 'import.meta.env.IS_DEV': isDev }, server: { fs: {