Skip to content

Commit

Permalink
Merge pull request #25 from InterwebAlchemy/feature/github-api
Browse files Browse the repository at this point in the history
fix: attempt to get profile info from GitHub public API
  • Loading branch information
ericrallen authored Feb 9, 2022
2 parents 90b211b + 8c8cb8e commit c9f7882
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 100 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ concurrency:
cancel-in-progress: true

jobs:
# borrowed from: https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/20
fail_if_pull_request_is_draft:
if: github.event.pull_request.draft == true
runs-on: ubuntu-latest
steps:
- name: Fail in order to indicate that Pull Request needs to be marked as Ready for Review before CI/CD pipeline can run.
run: exit 1

deploy:
runs-on: ubuntu-latest
steps:
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,22 @@
"commit:retry": "npm run commit -- --retry",
"dev": "npm run dev:next",
"dev:next": "next dev",
"lint:app": "next lint",
"prepare": "is-ci || npm run prepare:local",
"prepare:env": "copyfiles ./.env.example ./.env.local",
"prepare:husky": "husky install",
"prepare:local": "npm run prepare:env && npm run prepare:husky && mnpm run prepare:supabase",
"prepare:supabase": "npm run supabase:init",
"start": "next start",
"supabase:init": "supabase init",
"test": "npm run lint:app"
"test": "npm run test:eslint && npm run test:tsc",
"test:eslint": "eslint",
"test:tsc": "tsc -p ./ --noEmit"
},
"dependencies": {
"@chakra-ui/react": "^1.8.1",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@octokit/types": "^6.34.0",
"@supabase/supabase-js": "^1.29.4",
"@supabase/ui": "^0.36.3",
"cors": "^2.8.5",
Expand Down
15 changes: 10 additions & 5 deletions src/components/Nav/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Stack } from '@chakra-ui/react'
import { Box, Stack, Tag, Text } from '@chakra-ui/react'

import UserMenu from '../UserMenu'
import InternalLink from '../InternalLink'
Expand All @@ -8,10 +8,15 @@ const Nav = (): React.ReactElement => {
return (
<Box w="100%" padding="10px" position="fixed">
<Stack as="nav" direction="row" alignItems="center" justifyContent="space-between">
{/* @ts-expect-error CSS Props not correctly inherited by chakraLinkProps Object */}
<InternalLink href="/" chakraLinkProps={{ fontWeight: 'bold' }}>
Work w/ Me
</InternalLink>
<Stack direction="row" alignItems="center">
{/* @ts-expect-error CSS Props not correctly inherited by chakraLinkProps Object */}
<InternalLink href="/" chakraLinkProps={{ fontWeight: 'bold' }}>
<Text>Work w/ Me</Text>
</InternalLink>
<Tag variant="outline" colorScheme="cyan" size="sm">
Beta
</Tag>
</Stack>
<Stack direction="row" alignItems="center" spacing={5}>
<ThemeToggle />
<UserMenu />
Expand Down
4 changes: 2 additions & 2 deletions src/components/ProfileCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const ProfileCard = ({
<UserAvatar
size={layout === 'full' ? 'xl' : 'md'}
name={profile?.username}
src={profile?.avatarurl}
src={profile?.avatarUrl}
/>
</InternalLink>
) : (
Expand All @@ -77,7 +77,7 @@ const ProfileCard = ({
<UserAvatar
size={layout === 'full' ? 'xl' : 'md'}
name={profile?.username}
src={profile?.avatarurl}
src={profile?.avatarUrl}
/>
</Link>
)}
Expand Down
5 changes: 2 additions & 3 deletions src/components/UserMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import useProfile from '../../hooks/useProfile'

const UserMenu = (): React.ReactElement => {
const router = useRouter()

const [loggedIn, setLoggedIn] = useState(false)

const { username, logIn, logOut } = useProfile()
const { username, logIn, logOut, avatarUrl } = useProfile()

const editProfile = async (): Promise<void> => {
await router.push('/profile')
Expand All @@ -31,7 +30,7 @@ const UserMenu = (): React.ReactElement => {
return (
<Menu>
<MenuButton aria-label="Menu" variant="">
<UserAvatar />
<UserAvatar src={avatarUrl} />
</MenuButton>
<MenuList>
<MenuItem icon={<GoPencil />} onClick={editProfile}>
Expand Down
2 changes: 2 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const APPLICATION_URL = ['production', 'local'].includes(process.env.NEXT

export const GITHUB_PROFILE_BASE_URL = 'https://github.com'

export const GITHUB_API_URL = 'https://api.github.com'

export const SHIELDS_IO_ENDPOINT = 'https://img.shields.io/endpoint'

export const PSYCHOMETRIC_URLS = {
Expand Down
23 changes: 16 additions & 7 deletions src/context/UserProfileContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
getUserProfile,
updateUserProfile,
deleteUserProfile,
getUserAvatar,
logIn,
logOut,
} from '../services/user'
Expand All @@ -14,6 +13,7 @@ import {
getPersonalityColors,
getEnneagramTypes,
} from '../services/psychometrics'
import { gitHubUserDetails } from '../services/github'

import type { definitions } from '../types/supabase'
import type { KeysToCamelCase } from '../types/utility'
Expand Down Expand Up @@ -146,12 +146,6 @@ export const UserProfileProvider = ({
setEnneagramTypeId(data.enneagram_type_id)
setPersonalityTypeId(data.personality_type_id)
setPersonalityColorId(data.personality_color_id)

const avatarUrl = await getUserAvatar(user.id)

if (avatarUrl !== null) {
setAvatarUrl(avatarUrl)
}
}
}
}
Expand All @@ -175,6 +169,21 @@ export const UserProfileProvider = ({
}
}, [user])

useEffect(() => {
if (typeof username !== 'undefined' && username !== null) {
gitHubUserDetails(username)
.then((gitHubProfile) => {
console.log(gitHubProfile)
const { avatar_url: avatarUrl } = gitHubProfile
// @ts-expect-error avatar_url is defined as a string; not sure why tsc isn't cooperating
setAvatarUrl(avatarUrl)
})
.catch((e) => {
console.error(e)
})
}
}, [username])

return (
<UserProfileContext.Provider
value={{
Expand Down
4 changes: 2 additions & 2 deletions src/pages/[...username].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ParsedUrlQuery } from 'querystring'
import Page from '../components/Page'
import ProfileCard from '../components/ProfileCard'
import ExternalLink from '../components/ExternalLink'
import { getFullUserProfile, getUserAvatar } from '../services/user'
import { getFullUserProfile } from '../services/user'

import type { definitions } from '../types/supabase'
import type { KeysToCamelCase } from '../types/utility'
Expand Down Expand Up @@ -109,7 +109,7 @@ export const getServerSideProps = async (
enneagram: data.enneagram,
personality: data.personality,
color: data.color,
avatarUrl: await getUserAvatar(data.id),
avatarUrl: data.avatar_url,
}

return {
Expand Down
21 changes: 21 additions & 0 deletions src/services/github/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import fetch from 'cross-fetch'

import type { Endpoints } from '@octokit/types'

import type { definitions } from '../../types/supabase'

import { GITHUB_API_URL } from '../../constants'

export const gitHubUserDetails = async (
username: definitions['profiles']['username']
): Promise<Endpoints['GET /users/{username}']['response']['data']> => {
return fetch(new URL(`/users/${username}`, GITHUB_API_URL).toString(), {
headers: {
Accept: 'application/vnd.github.v3+json',
},
})
.then(async (response) => response.json())
.catch((e) => {
console.error(e)
})
}
82 changes: 5 additions & 77 deletions src/services/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,10 @@
import fetch from 'cross-fetch'

import type { PostgrestSingleResponse, PostgrestResponse } from '@supabase/supabase-js'

import { supabase } from '../../adapters/supabase'
import { APPLICATION_URL } from '../../constants'
import type { definitions } from '../../types/supabase'
import type { FullUserProfile } from '../../types/user'

export const getUserAvatar = async (
userId: definitions['profiles']['id']
): Promise<string | null> => {
const { publicURL, error } = await supabase.storage
.from('avatars')
.getPublicUrl(`public/${userId}/avatar.png`)

if (error !== null) {
if (process.env.NEXT_PUBLIC_FEATURE__DEBUG_LOGS === 'ENABLED') {
console.error(error)
}

return null
}

return publicURL
}

export interface UpdateAvatarRequest {
userId: definitions['profiles']['id']
avatarUrl: string
}

export const updateUserAvatar = async ({
userId,
avatarUrl,
}: UpdateAvatarRequest): Promise<void> => {
if (typeof avatarUrl !== 'undefined') {
fetch(new URL(avatarUrl).toString())
.then(async (response) => await response.blob())
.then((blob) => {
supabase.storage
.from('avatars')
.upload(`public/${userId}/avatar.png`, blob, {
cacheControl: '3600',
upsert: true,
})
.then(({ error, data }) => {
if (error !== null && process.env.NEXT_PUBLIC_FEATURE__DEBUG_LOGS === 'ENABLED') {
console.error(error)
}
})
.catch((e) => {
if (process.env.NEXT_PUBLIC_FEATURE__DEBUG_LOGS === 'ENABLED') {
console.error(e)
}
})
})
.catch((e) => {
if (process.env.NEXT_PUBLIC_FEATURE__DEBUG_LOGS === 'ENABLED') {
console.error(e)
}
})
}
}

export const logOut = async (): Promise<void> => {
const { error } = await supabase.auth.signOut()

Expand All @@ -73,26 +16,11 @@ export const logOut = async (): Promise<void> => {
}

export const logIn = async (): Promise<void> => {
const { error, user } = await supabase.auth.signIn(
const { error } = await supabase.auth.signIn(
{ provider: 'github' },
{ redirectTo: `${process.env.NEXT_PUBLIC_APPLICATION_URL}/profile` }
{ redirectTo: `${APPLICATION_URL}/profile` }
)

if (user !== null) {
console.log(user)

const userId = user.id
const avatarUrl = user.identities?.[0].identity_data.avatar_url

console.log(avatarUrl)

updateUserAvatar({ userId, avatarUrl }).catch((e) => {
if (process.env.NEXT_PUBLIC_FEATURE__DEBUG_LOGS === 'ENABLED') {
console.error(e)
}
})
}

if (error != null && process.env.NEXT_PUBLIC_FEATURE__DEBUG_LOGS === 'ENABLED') {
console.error('Error logging in:', error.message)
}
Expand All @@ -104,7 +32,7 @@ export const getUserProfile = async (
return supabase
.from<definitions['profiles']>('profiles')
.select(
`username, website, communication_style, personality_type_id, personality_color_id, enneagram_type_id`
`username, website, avatar_url, communication_style, personality_type_id, personality_color_id, enneagram_type_id`
)
.match({ id: userId })
.single()
Expand All @@ -117,7 +45,7 @@ export const getFullUserProfile = async (
return supabase
.from<FullUserProfile>('profiles')
.select(
`id, username, website, communication_style, personality:personality_type_id(type, name, description, url), color:personality_color_id(name, description, url), enneagram:enneagram_type_id(name, number, description, url)`
`id, username, website, avatar_url, communication_style, personality:personality_type_id(type, name, description, url), color:personality_color_id(name, description, url), enneagram:enneagram_type_id(name, number, description, url)`
)
.match({ username })
.single()
Expand Down
3 changes: 1 addition & 2 deletions src/types/user.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { definitions } from './supabase'

export interface FullUserProfile
extends Pick<definitions['profiles'], 'id' | 'username' | 'website' | 'communication_style'> {
extends Pick<definitions['profiles'], 'id' | 'avatar_url' | 'username' | 'website' | 'communication_style'> {
personality: Pick<definitions['personality_types'], 'type' | 'name' | 'description' | 'url'>
color: Pick<definitions['personality_colors'], 'name' | 'description' | 'url'>
enneagram: Pick<definitions['enneagram_types'], 'name' | 'number' | 'description' | 'url'>
avatarUrl?: string
}

1 comment on commit c9f7882

@ericrallen
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for work-with-me ready!

✅ Preview
https://work-with-me-38zs2kcje-interweb-alchemy.vercel.app
https://work-with-me.vercel.app

Built with commit c9f7882.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.