Skip to content

Commit

Permalink
feat: vercast 1.0.0 (#18)
Browse files Browse the repository at this point in the history
* feat: vercast 1.0.0

Signed-off-by: Matt Gleich <git@mattglei.ch>

* feat: make requested changes

Signed-off-by: Matt Gleich <git@mattglei.ch>

* feat: throw errors

Signed-off-by: Matt Gleich <git@mattglei.ch>

* chore(lint): remove unused import

Signed-off-by: Matt Gleich <git@mattglei.ch>

* feat: apply custom prettier and change description

Signed-off-by: Matt Gleich <git@mattglei.ch>
  • Loading branch information
gleich authored Oct 15, 2021
1 parent f2462e7 commit 8361840
Show file tree
Hide file tree
Showing 13 changed files with 676 additions and 0 deletions.
14 changes: 14 additions & 0 deletions extensions/vercast/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"root": true,
"env": {
"es2020": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
]
}
2 changes: 2 additions & 0 deletions extensions/vercast/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.DS_Store
4 changes: 4 additions & 0 deletions extensions/vercast/.prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
trailingComma: 'es5'
tabWidth: 2
semi: false
singleQuote: true
373 changes: 373 additions & 0 deletions extensions/vercast/LICENSE

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions extensions/vercast/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# vercast

🚥 View your vercel deployments with raycast

![example img](example.png)
Binary file added extensions/vercast/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added extensions/vercast/assets/icon@dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added extensions/vercast/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions extensions/vercast/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "vercast",
"title": "Search Vercel Deployments",
"description": "Search Vercel Deployments",
"icon": "icon.png",
"author": "matt",
"license": "MPL-2.0",
"commands": [
{
"name": "index",
"title": "Vercast",
"subtitle": "",
"description": "Search Vercel Deployments",
"mode": "view",
"preferences": [
{
"name": "token",
"type": "password",
"required": true,
"title": "Token",
"description": "Account Token",
"link": "https://vercel.com/account/tokens"
}
]
}
],
"dependencies": {
"@raycast/api": "^1.25.0",
"dayjs": "^1.10.7",
"node-fetch": "^2.6.1"
},
"devDependencies": {
"@types/node": "^16.4.3",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.15",
"@typescript-eslint/eslint-plugin": "^4.28.5",
"@typescript-eslint/parser": "^4.28.5",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"typescript": "^4.3.5"
},
"scripts": {
"build": "ray build -e dist",
"dev": "ray develop"
}
}
87 changes: 87 additions & 0 deletions extensions/vercast/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
render,
ActionPanel,
Color,
Icon,
List,
OpenInBrowserAction,
preferences,
showToast,
ToastStyle,
} from '@raycast/api'
import { useEffect, useState } from 'react'
import useInterval from './use-interval'
import {
Deployment,
DeploymentState,
fetchDeployments,
fetchUsername,
} from './vercel'

render(<Main />)

function Main(): JSX.Element {
const token = String(preferences.token.value)
if (token.length !== 24) {
showToast(ToastStyle.Failure, 'Invalid token detected')
throw new Error('Invalid token length detected')
}

const [username, setUsername] = useState('')
const [deployments, setDeployments] = useState<Deployment[]>()

useEffect(() => {
const call = async () => setUsername(await fetchUsername())
if (username === '') {
call()
}
})
useEffect(() => {
const call = async () => setDeployments(await fetchDeployments(username))
if (!deployments) {
call()
}
})

useInterval(async () => {
setDeployments(await fetchDeployments(username))
}, 1000)

return (
<List isLoading={!deployments}>
{deployments?.map((d) => {
let iconSource = Icon.Globe
let iconTintColor = Color.PrimaryText
switch (d.state) {
case DeploymentState.ready:
iconSource = Icon.Checkmark
iconTintColor = Color.Green
break
case DeploymentState.deploying:
iconSource = Icon.Hammer
iconTintColor = Color.Yellow
break
case DeploymentState.failed:
iconSource = Icon.XmarkCircle
iconTintColor = Color.Red
break
}
return (
<List.Item
key={d.id}
id={d.id}
title={d.project}
subtitle={d.domain}
accessoryTitle={d.time}
icon={{ tintColor: iconTintColor, source: iconSource }}
actions={
<ActionPanel>
<OpenInBrowserAction url={d.url} />
</ActionPanel>
}
/>
)
})}
</List>
)
}
39 changes: 39 additions & 0 deletions extensions/vercast/src/use-interval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// The code for this file is pulled from https://github.com/Hermanya/use-interval/blob/master/src/index.tsx
// This is done because of react dependency problems from the package.json of use-interval

import { useEffect, useRef } from 'react'

/* istanbul ignore next */
/** keep typescript happy */
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {}

export function useInterval(
callback: () => void,
delay: number | null | false,
immediate?: boolean
): void {
const savedCallback = useRef(noop)

// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback
})

// Execute callback if immediate is set.
useEffect(() => {
if (!immediate) return
if (delay === null || delay === false) return
savedCallback.current()
}, [immediate])

// Set up the interval.
useEffect(() => {
if (delay === null || delay === false) return undefined
const tick = () => savedCallback.current()
const id = setInterval(tick, delay)
return () => clearInterval(id)
}, [delay])
}

export default useInterval
90 changes: 90 additions & 0 deletions extensions/vercast/src/vercel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { preferences, showToast, ToastStyle } from '@raycast/api'
import fetch, { Headers } from 'node-fetch'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'

const headers = new Headers({
Authorization: 'Bearer ' + preferences.token.value,
})
const apiURL = 'https://api.vercel.com/'

export enum DeploymentState {
ready,
failed,
deploying,
}

export interface Deployment {
project: string
state: DeploymentState
time: string
id: string
url: string
domain: string
}

export async function fetchUsername(): Promise<string> {
try {
const response = await fetch(apiURL + 'www/user', {
method: 'get',
headers: headers,
})
const json = await response.json()
return json.user.username
} catch (err) {
console.error(err)
showToast(ToastStyle.Failure, 'Failed to fetch username')
throw new Error('Failed to fetch username')
}
return Promise.resolve('')
}

export async function fetchDeployments(
username: string
): Promise<Deployment[]> {
dayjs.extend(relativeTime)

try {
const response = await fetch(apiURL + 'v8/projects', {
method: 'get',
headers: headers,
})
const json = await response.json()
const deployments: Deployment[] = []
for (const project of json.projects) {
for (const deployment of project.latestDeployments) {
if (deployment.creator.username === username) {
let state: DeploymentState
switch (deployment.readyState.toUpperCase()) {
case 'READY':
state = DeploymentState.ready
break
case 'BUILDING':
case 'QUEUED':
state = DeploymentState.deploying
break
default:
state = DeploymentState.failed
break
}
deployments.push({
project: project.name,
state: state,
time: dayjs(deployment.createdAt).fromNow(),
id: deployment.id,
url: `https://vercel.com/${username}/${
project.name
}/${deployment.id.replace('dpl_', '')}`,
domain: deployment.alias[0],
})
break
}
}
}
return deployments
} catch (err) {
console.error(err)
showToast(ToastStyle.Failure, 'Failed to fetch deployments')
throw new Error('Failed to fetch deployments')
}
}
16 changes: 16 additions & 0 deletions extensions/vercast/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Node 16",
"include": ["src/**/*"],
"compilerOptions": {
"lib": ["es2020"],
"module": "commonjs",
"target": "es2020",
"strict": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react-jsx"
}
}

0 comments on commit 8361840

Please sign in to comment.