From 971e448130e9d210cbbc086376dd7f09f3a9d7ec Mon Sep 17 00:00:00 2001 From: Maria Torrente <64274966+mariatorrentedev@users.noreply.github.com> Date: Wed, 3 Jul 2024 21:56:06 -0400 Subject: [PATCH] Develop (#45) * Some clean up, added Spotify icon for offline/loading state * Minor clean up in Mobile view * Updating about text. * Fixing max width for the Tab Panel * P-18: fixed bug when type is not track * P-20: Add real time spotify streaming progress bar. (#21) * P-23: Added experience section. (#24) * P-26: Added placeholder projects section. (#27) * Wip footer. * develop: some footer updates, brb. * develop: simple footer. * P-11 (#31) * P-11: added vitest, react testing library config for unit testing. * P-11: add Projects unit test. * P-11: fix bg card color. * P-11 (#32) * P-11: added vitest, react testing library config for unit testing. * P-11: add Projects unit test. * P-11: fix bg card color. * P-11: almost done, missing Github actions and TODOS in test. * Update src/components/Navigation/Navigation.test.tsx * theme-mode: add dark/light mode. (#38) * animation: add simple typing animation and set initial theme mode based on user system pref. (#41) * cloudinary: replace png images, missing svgs (#44) --- .github/workflows/deploy.yml | 11 +++--- package-lock.json | 57 +++++++++++++++++++++++++++++ package.json | 1 + src/assets/mt-dark.png | Bin 8027 -> 0 bytes src/assets/mt-light.png | Bin 7637 -> 0 bytes src/components/CloudinaryImage.tsx | 18 +++++++++ src/components/Footer/Footer.tsx | 7 ++-- src/components/index.ts | 1 + src/config.ts | 17 +++++---- src/services/cloudinary.ts | 24 ++++++++++++ src/services/spotify.ts | 18 ++++----- 11 files changed, 127 insertions(+), 27 deletions(-) delete mode 100644 src/assets/mt-dark.png delete mode 100644 src/assets/mt-light.png create mode 100644 src/components/CloudinaryImage.tsx create mode 100644 src/services/cloudinary.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 74de096..dd37d82 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,7 +4,7 @@ name: Deploy static content to Pages on: # Runs on pushes targeting the default branch push: - branches: ['master'] + branches: ["master"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -17,7 +17,7 @@ permissions: # Allow one concurrent deployment concurrency: - group: 'pages' + group: "pages" cancel-in-progress: true jobs: @@ -25,12 +25,13 @@ jobs: deploy: environment: name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest env: VITE_APP_SPOTIFY_CLIENT_ID: ${{ secrets.VITE_APP_SPOTIFY_CLIENT_ID }} VITE_APP_SPOTIFY_CLIENT_SECRET: ${{ secrets.VITE_APP_SPOTIFY_CLIENT_SECRET }} VITE_APP_SPOTIFY_REFRESH_TOKEN: ${{ secrets.VITE_APP_SPOTIFY_REFRESH_TOKEN }} + VITE_APP_CLOUD_NAME: ${{ secrets.VITE_APP_CLOUD_NAME }} steps: - name: Checkout uses: actions/checkout@v4 @@ -38,7 +39,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 21 - cache: 'npm' + cache: "npm" - name: Install dependencies run: npm ci - name: Build @@ -52,7 +53,7 @@ jobs: uses: actions/upload-pages-artifact@v3 with: # Upload dist folder - path: './dist' + path: "./dist" - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/package-lock.json b/package-lock.json index 8960a9d..28018c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@mui/lab": "^5.0.0-alpha.170", "axios": "^1.6.8", "buffer": "^6.0.3", + "cloudinary-build-url": "^0.2.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.8.2", @@ -429,6 +430,14 @@ "statuses": "^2.0.1" } }, + "node_modules/@cld-apis/utils": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@cld-apis/utils/-/utils-0.2.0.tgz", + "integrity": "sha512-WBhOZ+wz93G/vsqkfmIBsPYf7FH4i5ZKN3QYlUfg9Ni5A2E09CCfsNG6RUuGeLjDPaFXFbta1rkNf3+2hFlavQ==", + "dependencies": { + "snake-case": "^3.0.4" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -2947,6 +2956,14 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/cloudinary-build-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/cloudinary-build-url/-/cloudinary-build-url-0.2.4.tgz", + "integrity": "sha512-Wi0aZPHOa90xDCCC4k9eoTVPIm3aWNyycO+rJUXvOyusQqmkPSprHSuJh3s2zNxEtuGj24aCM13iLDpilWMpSw==", + "dependencies": { + "@cld-apis/utils": "^0.2.0" + } + }, "node_modules/clsx": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", @@ -3176,6 +3193,15 @@ "csstype": "^3.0.2" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.736", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz", @@ -4340,6 +4366,14 @@ "get-func-name": "^2.0.1" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4639,6 +4673,15 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -5364,6 +5407,15 @@ "node": ">=8" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -5598,6 +5650,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 97312e1..20d74cf 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@mui/lab": "^5.0.0-alpha.170", "axios": "^1.6.8", "buffer": "^6.0.3", + "cloudinary-build-url": "^0.2.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.8.2", diff --git a/src/assets/mt-dark.png b/src/assets/mt-dark.png deleted file mode 100644 index 7cc07f307bbbb6c09845eddfe4c99a652d2ba27d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8027 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 Q`o}Bg_(=nro^#lL0IFJNZvX%Q diff --git a/src/components/CloudinaryImage.tsx b/src/components/CloudinaryImage.tsx new file mode 100644 index 0000000..d52407f --- /dev/null +++ b/src/components/CloudinaryImage.tsx @@ -0,0 +1,18 @@ +import { getCloudinaryUrl } from "../services/cloudinary"; + +type CloudinaryImageProps = { + publicId: string; + alt: string; + options?: Record; + className?: string; +}; + +export default function CloudinaryImage({ + publicId, + alt, + options, + className, +}: CloudinaryImageProps) { + const imageUrl = getCloudinaryUrl(publicId, options); + return {alt}; +} diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 5c56a2b..5763a71 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,8 +1,7 @@ import * as React from "react"; import { Typography, Container, Stack, IconButton } from "@mui/material"; -import MTLogoDark from "../../assets/mt-dark.png"; -import MTLogoLight from "../../assets/mt-light.png"; import { ThemeContext } from "../../mui/theme-context"; +import { CloudinaryImage } from "../../components"; export default function Footer() { const { theme } = React.useContext(ThemeContext); @@ -11,8 +10,8 @@ export default function Footer() { - Maria Torrente diff --git a/src/components/index.ts b/src/components/index.ts index 37e75ad..9e1ea07 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -4,3 +4,4 @@ export { default as Experience } from "./Experience/Experience"; export { default as Projects } from "./Projects/Projects"; export { default as SpotifyNowPlaying } from "./SpotifyNowPlaying/SpotifyNowPlaying"; export { default as Footer } from "./Footer/Footer"; +export { default as CloudinaryImage } from "./CloudinaryImage"; diff --git a/src/config.ts b/src/config.ts index 9ce4154..cc0e818 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,10 @@ -export const CLIENT_ID = import.meta.env.VITE_APP_SPOTIFY_CLIENT_ID; -export const CLIENT_SECRET = import.meta.env.VITE_APP_SPOTIFY_CLIENT_SECRET; -export const REFRESH_TOKEN = import.meta.env.VITE_APP_SPOTIFY_REFRESH_TOKEN; -export const ENV = import.meta.env.VITE_APP_NODE_ENV; -export const TOKEN_ENDPOINT = "https://accounts.spotify.com/api/token"; -export const NOW_PLAYING_ENDPOINT = - "https://api.spotify.com/v1/me/player/currently-playing"; +export const config = { + CLIENT_ID: import.meta.env.VITE_APP_SPOTIFY_CLIENT_ID, + CLIENT_SECRET: import.meta.env.VITE_APP_SPOTIFY_CLIENT_SECRET, + REFRESH_TOKEN: import.meta.env.VITE_APP_SPOTIFY_REFRESH_TOKEN, + ENV: import.meta.env.VITE_APP_NODE_ENV, + CLOUD_NAME: import.meta.env.VITE_APP_CLOUD_NAME, + TOKEN_ENDPOINT: "https://accounts.spotify.com/api/token", + NOW_PLAYING_ENDPOINT: + "https://api.spotify.com/v1/me/player/currently-playing", +}; diff --git a/src/services/cloudinary.ts b/src/services/cloudinary.ts new file mode 100644 index 0000000..17a8e10 --- /dev/null +++ b/src/services/cloudinary.ts @@ -0,0 +1,24 @@ +import { buildUrl } from "cloudinary-build-url"; +import { config } from "../config"; + +type TransformationOptions = { + [key: string]: unknown; +}; + +export function getCloudinaryUrl( + publicId: string, + options?: TransformationOptions +): string { + if (!config.CLOUD_NAME) { + throw new Error( + "Cloudinary cloud name is required to compose the image url." + ); + } + + return buildUrl(publicId, { + cloud: { + cloudName: config.CLOUD_NAME, + }, + transformations: options, + }); +} diff --git a/src/services/spotify.ts b/src/services/spotify.ts index e42313f..4f60119 100644 --- a/src/services/spotify.ts +++ b/src/services/spotify.ts @@ -1,13 +1,7 @@ import type { NowPlayingItem } from "../types/spotify"; import axios from "axios"; import { Buffer } from "buffer"; -import { - TOKEN_ENDPOINT, - NOW_PLAYING_ENDPOINT, - CLIENT_ID as clientId, - CLIENT_SECRET as clientSecret, - REFRESH_TOKEN as refreshToken, -} from "../config"; +import { config } from "../config"; export type GetAccessTokenResponse = { access_token: string; @@ -18,17 +12,19 @@ export type GetAccessTokenResponse = { }; export const getAccessToken = async () => { - const basic = Buffer.from(`${clientId}:${clientSecret}`).toString("base64"); + const basic = Buffer.from( + `${config.CLIENT_ID}:${config.CLIENT_SECRET}` + ).toString("base64"); const bodyParams = new URLSearchParams({ grant_type: "refresh_token", - refresh_token: refreshToken, + refresh_token: config.REFRESH_TOKEN, }); const body = bodyParams.toString(); try { const response = await axios.post( - TOKEN_ENDPOINT, + config.TOKEN_ENDPOINT, body, { headers: { @@ -57,7 +53,7 @@ export const getNowPlaying = async (accessToken?: string) => { throw "No Access Token available."; } const response = await axios.get( - NOW_PLAYING_ENDPOINT, + config.NOW_PLAYING_ENDPOINT, { headers: { Authorization: `Bearer ${accessToken}`,