Install

Depending of your dapp technology, you can import 1,2 or the 3 libraries

npm i @marigold-dev/tezos-community
npm i @marigold-dev/tezos-community-reactcontext
npm i @marigold-dev/tezos-community-reactcontext-ionic

Here below is described the full solution for Ionic React.

You can start with the base library and adapt the code for an Angular context or Ionic Angular with no problem. If requested we could publish libraries for other web frameworks

Set environment variables

Library will search for 2 variables :

  • VITE_TZCOMMUNITY_CONTRACT_ADDRESS : the TzCommunity deployed smartcontract
  • VITE_TZCOMMUNITY_BACKEND_URL :the TzCommunity backend API url

Ghostnet

VITE_TZCOMMUNITY_CONTRACT_ADDRESS=KT1KpqE5kfor9fmoDTNMAjoKeLpKRv4ooEZw
VITE_TZCOMMUNITY_BACKEND_URL=https://back.tezos-community.gcp-npr.marigold.dev

Mainnet

VITE_TZCOMMUNITY_CONTRACT_ADDRESS=KT1D95aUDDfCQnh1Ay7riNaTDaWwX63dGY6X
VITE_TZCOMMUNITY_BACKEND_URL=https://back.tezos-community.gcp.marigold.dev

These values might possibly change in the future, to be sure to use the correct configuation, contact Marigold Team

Connect / Disconnect to TzCommunity backend

TzCommunity uses SIWT to authentify to a web2 backend with your wallet TzCommunity overrides default web local storage with Ionic one : import { Storage as LocalStorage } from "@ionic/storage"; . this way it works both for web and Ionic mobile app on native devices

Connect and fetch your own user Profile

Import libs

import {
  TzCommunityReactContext,
  TzCommunityReactContextType,
} from "@marigold-dev/tezos-community-reactcontext";
import { address } from "../type-aliases";
import { getUserProfile } from "@marigold-dev/tezos-community";

Import the React context

const {
  setUserProfile,
  connectToWeb2Backend,
  localStorage,
  setUserProfiles,
  userProfiles,
} = React.useContext(TzCommunityReactContext) as TzCommunityReactContextType;

Connect to TzCommunity backend just after your logged with your wallet on Beacon. You can use setUserProfile to set your profile to a global context like below

const connectWallet = async (): Promise<void> => {

    ...

    try {
      await wallet.requestPermissions({
        network: {
          type: NetworkType[
            import.meta.env.VITE_NETWORK.toUpperCase() as keyof typeof NetworkType
          ],
          rpcUrl: import.meta.env.VITE_TEZOS_NODE,
        },
      });
      // gets user's address
      const userAddress = await wallet.getPKH();
      await setup(userAddress);

      //connect to TzCommunity
      await connectToWeb2Backend(
        wallet,
        userAddress,
        (
          await wallet.client.getActiveAccount()
        )?.publicKey!,
        localStorage
      );

      //try to load your user profile
      try {
        const newUserProfile = await getUserProfile(userAddress, localStorage);
        setUserProfile(newUserProfile!);

        setUserProfiles(
          userProfiles.set(userAddress as address, newUserProfile!)
        );
      } catch (error) {
        console.warn(
          "User " +
            userAddress +
            " has no social account profile link on TzCommunity"
        );
      }

Disconnect

On your disconnect function, remove SIWT tokens from local storage

  const disconnectWallet = async (): Promise<void> => {

    //TzCommunity
    if (localStorage.initialized) {
      console.log("localStorage is initialized, removing access tokens");
      await localStorage.remove(LocalStorageKeys.access_token); //remove SIWT tokens
      await localStorage.remove(LocalStorageKeys.id_token); //remove SIWT tokens
      await localStorage.remove(LocalStorageKeys.refresh_token); //remove SIWT tokens
    } else {
      console.warn("localStorage not initialized, cannot remove access tokens");
    }
    //End TzCommunity

On App.tsx, Load user profiles, and cache results

Initialize your local storage at Application startup

useEffect(() => {
  //TzCommunity
  (async () => {
    await localStorage.initStorage();
  })();
  //End TzCommunity
}, []);

Try to feth all user profile once you have connected

//tzCommunity
const [userProfiles, setUserProfiles] = useState<Map<address, UserProfile>>(
  new Map()
);

const [userProfile, setUserProfile] = useState<UserProfile | undefined>();
const [localStorage, setLocalStorage] = useState<CachingService>(
  new CachingService(new LocalStorage())
);

useEffect(() => {
  //only try to load if userProfile, it means you are logged with TzCommunity
  (async () => {
    if (userProfile || userProfile === null) {
      try {
        setUserProfiles(
          await loadUserProfiles(Tezos, userAddress!, localStorage)
        );
      } catch (error) {
        console.log(error);

        if (error instanceof TzCommunityError) {
          switch (error.type) {
            case TzCommunityErrorType.ACCESS_TOKEN_NULL: {
              console.warn("Cannot refresh token, disconnect");
              disconnectWallet();
              break;
            }
            case TzCommunityErrorType.ACCESS_TOKEN_EXPIRED: {
              console.warn(
                "Access token expired, try to fetch from refresh token.."
              );
              await refreshToken(userAddress!, localStorage);
              const userProfile = await getUserProfile(
                userAddress!,
                localStorage
              );
              if (userProfile) setUserProfile(userProfile);
              setUserProfiles(
                await loadUserProfiles(Tezos, userAddress!, localStorage)
              );
              break;
            }
          }
        } else {
          //nada
        }
      }
    } else {
      //nada
    }
  })();
}, [userProfile]);

Wrap your application with TzCommunity React context, so you can inject the context everywhere on your pages

return (
  <IonApp>
    <TzCommunityReactContext.Provider
      value={{
        userProfiles,
        setUserProfiles,
        userProfile,
        setUserProfile,
        localStorage,
        connectToWeb2Backend: connectToWeb2Backend,
      }}
    >
      ....
    </TzCommunityReactContext.Provider>
  </IonApp>
);

In case of HARD page refresh, lost of session and you want to reload your user profile

const reloadUser = async (): Promise<string | undefined> => {

const activeAccount = await wallet.client.getActiveAccount();

if (activeAccount) {
    let userAddress = activeAccount!.address;
    setUserAddress(userAddress);

...

//try to load your user profile
try {
  const newUserProfile = await getUserProfile(userAddress, localStorage);
  setUserProfile(newUserProfile!);

  setUserProfiles(userProfiles.set(userAddress as address, newUserProfile!));
} catch (error) {
  if (error instanceof TzCommunityError) {
    switch (error.type) {
      case TzCommunityErrorType.ACCESS_TOKEN_NULL: {
        console.warn("Cannot refresh token, disconnect");
        disconnectWallet();
        break;
      }
      case TzCommunityErrorType.ACCESS_TOKEN_EXPIRED: {
        console.warn("Access token expired, try to fetch from refresh token..");
        await refreshToken(userAddress!, localStorage);
        const userProfile = await getUserProfile(userAddress!, localStorage);
        if (userProfile) setUserProfile(userProfile);
        setUserProfiles(
          await loadUserProfiles(Tezos, userAddress!, localStorage)
        );
        break;
      }
    }
  } else {
          console.warn(
            "User " +
              userAddress +
              " has no social account profile link on TzCommunity"
          );
        }
      }

      return userAddress;
    } else {
      return undefined;
    }
  };

On a Page, Replace tz1 display on your UI by this custom IonChip

Import libs, fetch profiles from context and use the Chip component (replacing alice tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb address by your own variable)

import {
  TzCommunityReactContext,
  TzCommunityReactContextType,
} from "@marigold-dev/tezos-community-reactcontext";
import { TzCommunityIonicUserProfileChip } from "@marigold-dev/tezos-community-reactcontext-ionic";
import { address, int } from "../type-aliases";

...


 //TZCOM CONTEXT
  const { userProfiles } = React.useContext(
    TzCommunityReactContext
  ) as TzCommunityReactContextType;

...

return  <TzCommunityIonicUserProfileChip
                userProfiles={userProfiles}
                address="tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb"
                key="tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb"
              ></TzCommunityIonicUserProfileChip>


Here is the end of the magic story