# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hollowdb.xyz/use-cases/calendar.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
