> For the complete documentation index, see [llms.txt](https://docs.hollowdb.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hollowdb.xyz/use-cases/calendar.md).

# Calendar

Perma-Calendar is a demo app for HollowDB. It is client-side only, and every user deploys their contract & their keys. It does not use proofs and only uses whitelisting. \
\
The [FullCalendar](https://fullcalendar.io/) framework is used for calendar rendering. \
\
You can check out the live demo [here](https://hollowdb-nextjs-calendar.vercel.app/).

Head to the [GitHub](https://github.com/firstbatchxyz/hollowdb-nextjs-calendar) repo to see the implementation or jump to the [#code-snippets](#code-snippets "mention") section to have a quick look.&#x20;

## The Calendar App

As HollowDB supports both ZK-based inputs and whitelists, it can be used like any other database. This simple example demonstrates how it can be used as a calendar application only using client side code.

<figure><img src="/files/QyzsPI6ql3T2uLeJOxLQ" alt=""><figcaption><p>Initial View</p></figcaption></figure>

### Connect Wallet

Thanks to Warp contract's [Injected Ethereum Signer,](https://academy.warp.cc/docs/sdk/advanced/plugins/deployment#ethereum) the calendar supports both [Arweave wallet](https://arweave.app/) and [Metamask](https://metamask.io/)

<figure><img src="/files/bWr4ZVqowECrJ4GUU2eu" alt="" width="210"><figcaption><p>Wallet Options</p></figcaption></figure>

<figure><img src="/files/qCbZGZ6gUEMMcYnccDGs" alt="" width="375"><figcaption><p>Arweave Wallet</p></figcaption></figure>

<figure><img src="/files/Jb9s016Zm3I61Uhq3jlz" alt="" width="336"><figcaption><p>Metamask Wallet</p></figcaption></figure>

### Deploy a Contract

As mentioned, for a calendar app every user has to deploy their own HollowDB contract. Users' wallet is added to the whitelist automatically, meaning only they can do write operations. \
\
After you connect your wallet, you will see either a **Deploy** or **Redeploy** button depending on if a previously deployed contract exists or not.

<figure><img src="/files/2cINvNDPGZfwwuJ8h7Jj" alt="" width="356"><figcaption><p>A contract exists</p></figcaption></figure>

<figure><img src="/files/hVQLRx7xDu52MayFWX0k" alt="" width="371"><figcaption><p>Contract not found</p></figcaption></figure>

### Add Calendar Events

You can click on any date to create an event. After submitting the event, the user receives a transaction to write the event to the HollowDB contract. After a successful event creation, the event will be shown on the calendar.

#### Enter Event Name

![](/files/DqUx4dRfPtesSingAA5h)

#### Sign the Transaction

![](/files/NzdltjrzGaiMnQKkNoc3)

#### Ta-da!

![](/files/9pArbN1knOJjZyumTYLo)

#### Delete Event

To delete an event, simply click on the event and sign the transaction.

## Code Snippets

The folder structure of the application is as follows:

```
. 
├─ src 
│       ├── components (header and layout)
│       ├── constants (source tx id of the contract to be deployed)
│       ├── context (wallet connection and contract deploy logic)
│       ├── pages (home page and react/nextjs defaults)
│ 
└── ...
```

#### Contract Deployment

{% code overflow="wrap" %}

```typescript
// srcTx type is created, it requires srcTxID, deployer address and signer
// signer for metamask is retrieved from InjectedEthereumSigner and window.ethereum
// signer for arweave is retrieved from InjectedArweaveSigner

const srcTx: FromSrcTxContractData = generateContractInfo(
      HOLLOWDB_SRCTXID,
      address,
      userSigner
    );

// HollowDB deployed
const deployTx = await warp.deployFromSourceTx(srcTx);

// HollowDB instance is created using SDK and deploy tx information
const hollowdb = new SDK("use_wallet", deployTx.contractTxId, warp);

// The instance is stored and shared accross the web-app
setHollowdb(hollowdb);
```

{% endcode %}

#### Put Operation&#x20;

```typescript
async function put(key: string, value: {}) {
    /* ... some checks ... */
    await hollowdb?.put(key, JSON.stringify(value));
  }

const handleDateSelect = (selectInfo: DateSelectArg) => {
    /* ... some checks ... */

    let title = prompt("Please enter a new title for your event");
    let calendarApi = selectInfo.view.calendar;

    // The event is added to both calendarApi and HollowDB
    if (title) {
      const eventId = createEventId();
      const start = selectInfo.startStr;
      const end = selectInfo.endStr;
      const allDay = selectInfo.allDay;
      calendarApi.addEvent({
        id: eventId,
        title,
        start: start,
        end: end,
        allDay: allDay,
      });
      put(eventId, { title: title, start: start, end: end, allDay: allDay });
    }
  };
```

#### Remove Operation

```typescript
async function remove(key: string) {
    if (!isConnected) {
      return;
    }
    // Empty event object
    // We check for empty events using empty string
    const emptyValue = JSON.stringify({
      title: "",
      start: "",
      end: "",
      allDay: "",
    });
    await hollowdb?.update(key, emptyValue);
  }

const handleEventClick = (clickInfo: EventClickArg) => {
    /* ... some checks ... */
    
    // The event is removed from both calendarApi and HollowDB
    remove(clickInfo.event.id);
    clickInfo.event.remove();
    
  };
```

#### Handling Previously Deployed Contracts

```typescript
const checkPrevEvents = async () => {
      if (hollowdb?.contractTxId == "" || !isConnected) return;

      // clean up the calendar (helps with redeployment)
      removeAll();

      let oldEventKeys: Array<string> = [];
      let oldEvents: Array<any> = [];

      // get all existing keys on the hollowdb contract
      await hollowdb?.getAllKeys().then((keys: []) => {
        if (keys) {
          oldEventKeys = Array.from(keys.values());
        }
      });

      // get all existing values on the hollowdb contract then convert it to an array
      const eventValues = await hollowdb?.getStorageValues(oldEventKeys);
      const mappedEvents = eventValues?.cachedValue;
      if (mappedEvents) {
        oldEvents = Array.from(mappedEvents.values());
      }

      // filter empty events
      oldEvents = oldEvents.filter((elements) => {
        return elements !== null;
      });

      // Obtain a calendar api instance to add previous events to the calendar
      const calendarApi = CalendarRef.current.getApi();
      for (let i = 0; i < oldEvents.length; i++) {
        oldEvents[i] = await JSON.parse(oldEvents[i]);
        const eventId = createEventId();
        if (oldEvents[i].title != "")
          calendarApi.addEvent({
            id: eventId,
            title: oldEvents[i].title,
            start: new Date(oldEvents[i].start),
            end: new Date(oldEvents[i].end),
            allDay: new Date(oldEvents[i].allDay),
          });
      }
    };
```

**Congrats!** You have successfully learned how to deploy and use an HollowDB contract.
