diff --git a/apps/web/server/routers/viewer.tsx b/apps/web/server/routers/viewer.tsx
index 2894d3ce..8dcc1bf1 100644
--- a/apps/web/server/routers/viewer.tsx
+++ b/apps/web/server/routers/viewer.tsx
@@ -532,15 +532,10 @@ const loggedInViewerRouter = createProtectedRouter()
});
if (web3Credential) {
- return ctx.prisma.credential.update({
+ return ctx.prisma.credential.delete({
where: {
id: web3Credential.id,
},
- data: {
- key: {
- isWeb3Active: !(web3Credential.key as JSONObject).isWeb3Active,
- },
- },
});
} else {
return ctx.prisma.credential.create({
diff --git a/packages/app-store/apiHandlers.tsx b/packages/app-store/apiHandlers.tsx
index dffe83e9..c8535136 100644
--- a/packages/app-store/apiHandlers.tsx
+++ b/packages/app-store/apiHandlers.tsx
@@ -13,6 +13,7 @@ export const apiHandlers = {
wipemycalother: import("./wipemycalother/api"),
jitsivideo: import("./jitsivideo/api"),
huddle01video: import("./huddle01video/api"),
+ metamask: import("./metamask/api"),
giphy: import("./giphy/api"),
};
diff --git a/packages/app-store/components.tsx b/packages/app-store/components.tsx
index 5728fe91..39ebc962 100644
--- a/packages/app-store/components.tsx
+++ b/packages/app-store/components.tsx
@@ -23,6 +23,7 @@ export const InstallAppButtonMap = {
wipemycalother: dynamic(() => import("./wipemycalother/components/InstallAppButton")),
jitsivideo: dynamic(() => import("./jitsivideo/components/InstallAppButton")),
huddle01video: dynamic(() => import("./huddle01video/components/InstallAppButton")),
+ metamask: dynamic(() => import("./metamask/components/InstallAppButton")),
giphy: dynamic(() => import("./giphy/components/InstallAppButton")),
};
diff --git a/packages/app-store/index.ts b/packages/app-store/index.ts
index 52e5cbaa..60e0f17e 100644
--- a/packages/app-store/index.ts
+++ b/packages/app-store/index.ts
@@ -8,6 +8,7 @@ import * as googlevideo from "./googlevideo";
import * as hubspotothercalendar from "./hubspotothercalendar";
import * as huddle01video from "./huddle01video";
import * as jitsivideo from "./jitsivideo";
+import * as metamask from "./metamask";
import * as office365calendar from "./office365calendar";
import * as office365video from "./office365video";
import * as slackmessaging from "./slackmessaging";
@@ -33,6 +34,7 @@ const appStore = {
tandemvideo,
zoomvideo,
wipemycalother,
+ metamask,
giphy,
};
diff --git a/packages/app-store/metadata.ts b/packages/app-store/metadata.ts
index 3725f85e..210531a4 100644
--- a/packages/app-store/metadata.ts
+++ b/packages/app-store/metadata.ts
@@ -7,6 +7,7 @@ import { metadata as googlevideo } from "./googlevideo/_metadata";
import { metadata as hubspotothercalendar } from "./hubspotothercalendar/_metadata";
import { metadata as huddle01video } from "./huddle01video/_metadata";
import { metadata as jitsivideo } from "./jitsivideo/_metadata";
+import { metadata as metamask } from "./metamask/_metadata";
import { metadata as office365calendar } from "./office365calendar/_metadata";
import { metadata as office365video } from "./office365video/_metadata";
import { metadata as slackmessaging } from "./slackmessaging/_metadata";
@@ -31,6 +32,7 @@ export const appStoreMetadata = {
tandemvideo,
zoomvideo,
wipemycalother,
+ metamask,
giphy,
};
diff --git a/packages/app-store/metamask/README.mdx b/packages/app-store/metamask/README.mdx
new file mode 100644
index 00000000..fccc15c2
--- /dev/null
+++ b/packages/app-store/metamask/README.mdx
@@ -0,0 +1,17 @@
+---
+items:
+ - /api/app-store/metamask/example1.png
+ - /api/app-store/metamask/example2.png
+---
+
+
+
+Only book and allow bookings from people who share the same tokens, DAOs, or NFTs.
+
+Send a group scheduling link that only members of a DAO or token-based community can join.
+
+Share your booking link and be sure to only receive bookings from owners of your chosen tokens or coins.
+
+Provide office hours for other DAO members or outsiders who want to learn more from you.
+
+[Click here to learn more](https://cal.com/web3)
diff --git a/packages/app-store/metamask/_metadata.ts b/packages/app-store/metamask/_metadata.ts
new file mode 100644
index 00000000..784d5bd1
--- /dev/null
+++ b/packages/app-store/metamask/_metadata.ts
@@ -0,0 +1,26 @@
+import type { App } from "@calcom/types/App";
+
+import _package from "./package.json";
+
+export const metadata = {
+ name: "MetaMask",
+ description: _package.description,
+ installed: true,
+ category: "web3",
+ // If using static next public folder, can then be referenced from the base URL (/).
+ imageSrc: "/api/app-store/metamask/icon.svg",
+ logo: "/api/app-store/metamask/icon.svg",
+ publisher: "Cal.com",
+ rating: 5,
+ reviews: 69,
+ slug: "metamask",
+ title: "Meta Mask",
+ trending: true,
+ type: "metamask_web3",
+ url: "https://cal.com/",
+ variant: "other",
+ verified: true,
+ email: "help@cal.com",
+} as App;
+
+export default metadata;
diff --git a/packages/app-store/metamask/api/add.ts b/packages/app-store/metamask/api/add.ts
new file mode 100644
index 00000000..2ecde7d2
--- /dev/null
+++ b/packages/app-store/metamask/api/add.ts
@@ -0,0 +1,39 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+
+import prisma from "@calcom/prisma";
+
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ if (!req.session?.user?.id) {
+ return res.status(401).json({ message: "You must be logged in to do this" });
+ }
+
+ const appType = "metamask_web3";
+ try {
+ const alreadyInstalled = await prisma.credential.findFirst({
+ where: {
+ type: appType,
+ userId: req.session.user.id,
+ },
+ });
+ if (alreadyInstalled) {
+ throw new Error("Already installed");
+ }
+ const installation = await prisma.credential.create({
+ data: {
+ type: appType,
+ key: { isWeb3Active: true },
+ userId: req.session.user.id,
+ appId: "metamask",
+ },
+ });
+ if (!installation) {
+ throw new Error("Unable to create user credential for metamask");
+ }
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ return res.status(500).json({ message: error.message });
+ }
+ return res.status(500);
+ }
+ return res.redirect("/apps/installed");
+}
diff --git a/packages/app-store/metamask/api/index.ts b/packages/app-store/metamask/api/index.ts
new file mode 100644
index 00000000..4c0d2ead
--- /dev/null
+++ b/packages/app-store/metamask/api/index.ts
@@ -0,0 +1 @@
+export { default as add } from "./add";
diff --git a/packages/app-store/metamask/components/InstallAppButton.tsx b/packages/app-store/metamask/components/InstallAppButton.tsx
new file mode 100644
index 00000000..44098b83
--- /dev/null
+++ b/packages/app-store/metamask/components/InstallAppButton.tsx
@@ -0,0 +1,18 @@
+import useAddAppMutation from "../../_utils/useAddAppMutation";
+import { InstallAppButtonProps } from "../../types";
+
+export default function InstallAppButton(props: InstallAppButtonProps) {
+ // @ts-ignore TODO: deprecate App types in favor of DB slugs
+ const mutation = useAddAppMutation("metamask");
+
+ return (
+ <>
+ {props.render({
+ onClick() {
+ mutation.mutate("");
+ },
+ loading: mutation.isLoading,
+ })}
+ >
+ );
+}
diff --git a/packages/app-store/metamask/components/index.ts b/packages/app-store/metamask/components/index.ts
new file mode 100644
index 00000000..0d6008d4
--- /dev/null
+++ b/packages/app-store/metamask/components/index.ts
@@ -0,0 +1 @@
+export { default as InstallAppButton } from "./InstallAppButton";
diff --git a/packages/app-store/metamask/index.ts b/packages/app-store/metamask/index.ts
new file mode 100644
index 00000000..5d372ced
--- /dev/null
+++ b/packages/app-store/metamask/index.ts
@@ -0,0 +1,2 @@
+export * as api from "./api";
+export { metadata } from "./_metadata";
diff --git a/packages/app-store/metamask/package.json b/packages/app-store/metamask/package.json
new file mode 100644
index 00000000..53accf78
--- /dev/null
+++ b/packages/app-store/metamask/package.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json.schemastore.org/package.json",
+ "private": true,
+ "name": "@calcom/metamask",
+ "version": "0.0.0",
+ "main": "./index.ts",
+ "description": "Only book and allow bookings from people who share the same tokens, DAOs, or NFTs.",
+ "dependencies": {
+ "@calcom/prisma": "*"
+ },
+ "devDependencies": {
+ "@calcom/types": "*"
+ }
+}
diff --git a/packages/app-store/metamask/static/example1.png b/packages/app-store/metamask/static/example1.png
new file mode 100644
index 00000000..5a4869ba
Binary files /dev/null and b/packages/app-store/metamask/static/example1.png differ
diff --git a/packages/app-store/metamask/static/example2.png b/packages/app-store/metamask/static/example2.png
new file mode 100644
index 00000000..5ddeb1b7
Binary files /dev/null and b/packages/app-store/metamask/static/example2.png differ
diff --git a/packages/app-store/metamask/static/icon.svg b/packages/app-store/metamask/static/icon.svg
new file mode 100644
index 00000000..6cb41ba9
--- /dev/null
+++ b/packages/app-store/metamask/static/icon.svg
@@ -0,0 +1,61 @@
+
+
+
diff --git a/packages/prisma/migrations/20220502154345_adds_metamask_giphy/migration.sql b/packages/prisma/migrations/20220502154345_adds_metamask_giphy/migration.sql
new file mode 100644
index 00000000..6de4fe41
--- /dev/null
+++ b/packages/prisma/migrations/20220502154345_adds_metamask_giphy/migration.sql
@@ -0,0 +1,3 @@
+-- Connects each saved Credential to their respective App
+UPDATE "Credential" SET "appId" = 'metamask' WHERE "type" = 'metamask_web3';
+UPDATE "Credential" SET "appId" = 'giphy' WHERE "type" = 'giphy_other';
diff --git a/packages/prisma/seed-app-store.ts b/packages/prisma/seed-app-store.ts
index 4b5368ac..85741e7a 100644
--- a/packages/prisma/seed-app-store.ts
+++ b/packages/prisma/seed-app-store.ts
@@ -77,6 +77,7 @@ async function main() {
}
// Web3 apps
await createApp("huddle01", "huddle01video", ["web3", "video"]);
+ await createApp("metamask", "metamask", ["web3"]);
// Messaging apps
if (process.env.SLACK_CLIENT_ID && process.env.SLACK_CLIENT_SECRET && process.env.SLACK_SIGNING_SECRET) {
await createApp("slack", "slackmessaging", ["messaging"], {