Add Meta Mask to app store (#2650)
* Adds available apps * Adds App Model * WIP * Create meta mask app folder * Add description and images * Remove credential from installed apps page * Updates seeder script * Seeder fixes * lowercase categories * Upgrades prisma * WIP * WIP * Hopefully fixes circular deps * Type fixes * Fixes seeder * Adds migration to connect Credentials to Apps * Updates app store callbacks * Updates google credentials * Uses dirName from DB * Type fixes * Update reschedule.ts * Seeder fixes * Fixes categories listing * Update index.ts * Update schema.prisma * Updates dependencies * Renames giphy app * Uses dynamic imports for app metadata * Fixes credentials error * Uses dynamic import for api handlers * Dynamic import fixes * Allows for simple folder names in app store * Remove video adaptor * Squashes app migrations * seeder fixes * Renames to metamask * Updates metamask metadata * Fixes dyamic imports * Remove comments * Create migration.sql Co-authored-by: zomars <zomars@me.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
		
							parent
							
								
									2e6bc5e5b4
								
							
						
					
					
						commit
						000785c29f
					
				
					 18 changed files with 190 additions and 6 deletions
				
			
		|  | @ -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({ | ||||
|  |  | |||
|  | @ -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"), | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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")), | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										17
									
								
								packages/app-store/metamask/README.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/app-store/metamask/README.mdx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| --- | ||||
| items: | ||||
|   - /api/app-store/metamask/example1.png | ||||
|   - /api/app-store/metamask/example2.png | ||||
| --- | ||||
| 
 | ||||
| <Slider items={items} /> | ||||
| 
 | ||||
| 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) | ||||
							
								
								
									
										26
									
								
								packages/app-store/metamask/_metadata.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/app-store/metamask/_metadata.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
							
								
								
									
										39
									
								
								packages/app-store/metamask/api/add.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								packages/app-store/metamask/api/add.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -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"); | ||||
| } | ||||
							
								
								
									
										1
									
								
								packages/app-store/metamask/api/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/app-store/metamask/api/index.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| export { default as add } from "./add"; | ||||
							
								
								
									
										18
									
								
								packages/app-store/metamask/components/InstallAppButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								packages/app-store/metamask/components/InstallAppButton.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -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, | ||||
|       })} | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										1
									
								
								packages/app-store/metamask/components/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/app-store/metamask/components/index.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| export { default as InstallAppButton } from "./InstallAppButton"; | ||||
							
								
								
									
										2
									
								
								packages/app-store/metamask/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								packages/app-store/metamask/index.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| export * as api from "./api"; | ||||
| export { metadata } from "./_metadata"; | ||||
							
								
								
									
										14
									
								
								packages/app-store/metamask/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								packages/app-store/metamask/package.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -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": "*" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								packages/app-store/metamask/static/example1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								packages/app-store/metamask/static/example1.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 88 KiB | 
							
								
								
									
										
											BIN
										
									
								
								packages/app-store/metamask/static/example2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								packages/app-store/metamask/static/example2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 515 KiB | 
							
								
								
									
										61
									
								
								packages/app-store/metamask/static/icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								packages/app-store/metamask/static/icon.svg
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.1" id="Layer_1" xmlns:ev="http://www.w3.org/2001/xml-events" | ||||
| 	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 318.6 318.6" | ||||
| 	 style="enable-background:new 0 0 318.6 318.6;" xml:space="preserve"> | ||||
| <style type="text/css"> | ||||
| 	.st0{fill:#E2761B;stroke:#E2761B;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st1{fill:#E4761B;stroke:#E4761B;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st2{fill:#D7C1B3;stroke:#D7C1B3;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st3{fill:#233447;stroke:#233447;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st4{fill:#CD6116;stroke:#CD6116;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st5{fill:#E4751F;stroke:#E4751F;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st6{fill:#F6851B;stroke:#F6851B;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st7{fill:#C0AD9E;stroke:#C0AD9E;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st8{fill:#161616;stroke:#161616;stroke-linecap:round;stroke-linejoin:round;} | ||||
| 	.st9{fill:#763D16;stroke:#763D16;stroke-linecap:round;stroke-linejoin:round;} | ||||
| </style> | ||||
| <polygon class="st0" points="274.1,35.5 174.6,109.4 193,65.8 "/> | ||||
| <g> | ||||
| 	<polygon class="st1" points="44.4,35.5 143.1,110.1 125.6,65.8 	"/> | ||||
| 	<polygon class="st1" points="238.3,206.8 211.8,247.4 268.5,263 284.8,207.7 	"/> | ||||
| 	<polygon class="st1" points="33.9,207.7 50.1,263 106.8,247.4 80.3,206.8 	"/> | ||||
| 	<polygon class="st1" points="103.6,138.2 87.8,162.1 144.1,164.6 142.1,104.1 	"/> | ||||
| 	<polygon class="st1" points="214.9,138.2 175.9,103.4 174.6,164.6 230.8,162.1 	"/> | ||||
| 	<polygon class="st1" points="106.8,247.4 140.6,230.9 111.4,208.1 	"/> | ||||
| 	<polygon class="st1" points="177.9,230.9 211.8,247.4 207.1,208.1 	"/> | ||||
| </g> | ||||
| <g> | ||||
| 	<polygon class="st2" points="211.8,247.4 177.9,230.9 180.6,253 180.3,262.3 	"/> | ||||
| 	<polygon class="st2" points="106.8,247.4 138.3,262.3 138.1,253 140.6,230.9 	"/> | ||||
| </g> | ||||
| <polygon class="st3" points="138.8,193.5 110.6,185.2 130.5,176.1 "/> | ||||
| <polygon class="st3" points="179.7,193.5 188,176.1 208,185.2 "/> | ||||
| <g> | ||||
| 	<polygon class="st4" points="106.8,247.4 111.6,206.8 80.3,207.7 	"/> | ||||
| 	<polygon class="st4" points="207,206.8 211.8,247.4 238.3,207.7 	"/> | ||||
| 	<polygon class="st4" points="230.8,162.1 174.6,164.6 179.8,193.5 188.1,176.1 208.1,185.2 	"/> | ||||
| 	<polygon class="st4" points="110.6,185.2 130.6,176.1 138.8,193.5 144.1,164.6 87.8,162.1 	"/> | ||||
| </g> | ||||
| <g> | ||||
| 	<polygon class="st5" points="87.8,162.1 111.4,208.1 110.6,185.2 	"/> | ||||
| 	<polygon class="st5" points="208.1,185.2 207.1,208.1 230.8,162.1 	"/> | ||||
| 	<polygon class="st5" points="144.1,164.6 138.8,193.5 145.4,227.6 146.9,182.7 	"/> | ||||
| 	<polygon class="st5" points="174.6,164.6 171.9,182.6 173.1,227.6 179.8,193.5 	"/> | ||||
| </g> | ||||
| <polygon class="st6" points="179.8,193.5 173.1,227.6 177.9,230.9 207.1,208.1 208.1,185.2 "/> | ||||
| <polygon class="st6" points="110.6,185.2 111.4,208.1 140.6,230.9 145.4,227.6 138.8,193.5 "/> | ||||
| <polygon class="st7" points="180.3,262.3 180.6,253 178.1,250.8 140.4,250.8 138.1,253 138.3,262.3 106.8,247.4 117.8,256.4  | ||||
| 	140.1,271.9 178.4,271.9 200.8,256.4 211.8,247.4 "/> | ||||
| <polygon class="st8" points="177.9,230.9 173.1,227.6 145.4,227.6 140.6,230.9 138.1,253 140.4,250.8 178.1,250.8 180.6,253 "/> | ||||
| <g> | ||||
| 	<polygon class="st9" points="278.3,114.2 286.8,73.4 274.1,35.5 177.9,106.9 214.9,138.2 267.2,153.5 278.8,140 273.8,136.4  | ||||
| 		281.8,129.1 275.6,124.3 283.6,118.2 	"/> | ||||
| 	<polygon class="st9" points="31.8,73.4 40.3,114.2 34.9,118.2 42.9,124.3 36.8,129.1 44.8,136.4 39.8,140 51.3,153.5 103.6,138.2  | ||||
| 		140.6,106.9 44.4,35.5 	"/> | ||||
| </g> | ||||
| <polygon class="st6" points="267.2,153.5 214.9,138.2 230.8,162.1 207.1,208.1 238.3,207.7 284.8,207.7 "/> | ||||
| <polygon class="st6" points="103.6,138.2 51.3,153.5 33.9,207.7 80.3,207.7 111.4,208.1 87.8,162.1 "/> | ||||
| <polygon class="st6" points="174.6,164.6 177.9,106.9 193.1,65.8 125.6,65.8 140.6,106.9 144.1,164.6 145.3,182.8 145.4,227.6  | ||||
| 	173.1,227.6 173.3,182.8 "/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.9 KiB | 
|  | @ -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'; | ||||
|  | @ -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"], { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Joe Au-Yeung
						Joe Au-Yeung