version 2 of my website
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

changed from api route to server actions

+86 -82
+7 -1
astro.config.mjs
··· 5 5 import react from '@astrojs/react'; 6 6 import netlify from '@astrojs/netlify'; 7 7 8 + import node from '@astrojs/node'; 9 + 8 10 // https://astro.build/config 9 11 export default defineConfig({ 10 12 server: { ··· 13 15 vite: { 14 16 plugins: [tailwindcss()] 15 17 }, 16 - adapter: netlify(), 18 + output: 'server', 19 + adapter: process.env.NODE_ENV === 'production' ? 20 + netlify() : node({ 21 + mode: 'standalone' 22 + }), 17 23 integrations: [mdx(), react()] 18 24 });
+29
bun.lock
··· 7 7 "dependencies": { 8 8 "@astrojs/mdx": "5.0.2", 9 9 "@astrojs/netlify": "^7.0.4", 10 + "@astrojs/node": "^10.0.4", 10 11 "@astrojs/react": "5.0.1", 11 12 "@lucide/astro": "^0.563.0", 12 13 "@nanostores/react": "^1.0.0", ··· 43 44 "@astrojs/mdx": ["@astrojs/mdx@5.0.2", "", { "dependencies": { "@astrojs/markdown-remark": "7.0.1", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^6.0.0" } }, "sha512-0as6odPH9ZQhS3pdH9dWmVOwgXuDtytJiE4VvYgR0lSFBvF4PSTyE0HdODHm/d7dBghvWTPc2bQaBm4y4nTBNw=="], 44 45 45 46 "@astrojs/netlify": ["@astrojs/netlify@7.0.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/underscore-redirects": "1.0.2", "@netlify/blobs": "^10.7.0", "@netlify/functions": "^5.1.2", "@netlify/vite-plugin": "^2.10.3", "@vercel/nft": "^1.3.2", "esbuild": "^0.27.3", "tinyglobby": "^0.2.15", "vite": "^7.3.1" }, "peerDependencies": { "astro": "^6.0.0" } }, "sha512-t9dCAr1YfwC1AOZ2heYlT7yvMFO7XkvUww4XTmipymIYnTc9N4qILynSd6TNh69GG1LzpLn2MQ1J3lWqPZsWoA=="], 47 + 48 + "@astrojs/node": ["@astrojs/node@10.0.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "send": "^1.2.1", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^6.0.0" } }, "sha512-7pVgiVSscQHRC2WqjlXcnbbcKMYp2GXrYpmuvdGg5zgA8J1lFm2vmwVhHZFuZK3Ik5PzoxiDROaEgoDGLbfhLw=="], 46 49 47 50 "@astrojs/prism": ["@astrojs/prism@4.0.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ=="], 48 51 ··· 760 763 761 764 "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], 762 765 766 + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], 767 + 763 768 "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], 764 769 765 770 "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], ··· 814 819 815 820 "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], 816 821 822 + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], 823 + 817 824 "electron-to-chromium": ["electron-to-chromium@1.5.217", "", {}, "sha512-Pludfu5iBxp9XzNl0qq2G87hdD17ZV7h5T4n6rQXDi3nCyloBV3jreE9+8GC6g4X/5yxqVgXEURpcLtM0WS4jA=="], 818 825 819 826 "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], ··· 821 828 "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], 822 829 823 830 "enabled": ["enabled@2.0.0", "", {}, "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="], 831 + 832 + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], 824 833 825 834 "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], 826 835 ··· 854 863 855 864 "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 856 865 866 + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 867 + 857 868 "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 858 869 859 870 "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], ··· 942 953 943 954 "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], 944 955 956 + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], 957 + 945 958 "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 946 959 947 960 "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], ··· 1031 1044 "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], 1032 1045 1033 1046 "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], 1047 + 1048 + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], 1034 1049 1035 1050 "http-shutdown": ["http-shutdown@1.2.2", "", {}, "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw=="], 1036 1051 ··· 1450 1465 1451 1466 "omit.js": ["omit.js@2.0.2", "", {}, "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg=="], 1452 1467 1468 + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], 1469 + 1453 1470 "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], 1454 1471 1455 1472 "one-time": ["one-time@1.0.0", "", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="], ··· 1547 1564 "quote-unquote": ["quote-unquote@1.0.0", "", {}, "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg=="], 1548 1565 1549 1566 "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], 1567 + 1568 + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], 1550 1569 1551 1570 "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], 1552 1571 ··· 1654 1673 1655 1674 "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], 1656 1675 1676 + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], 1677 + 1678 + "server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="], 1679 + 1657 1680 "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], 1658 1681 1659 1682 "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], 1660 1683 1661 1684 "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], 1685 + 1686 + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 1662 1687 1663 1688 "sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], 1664 1689 ··· 1703 1728 "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], 1704 1729 1705 1730 "stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="], 1731 + 1732 + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], 1706 1733 1707 1734 "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], 1708 1735 ··· 1775 1802 "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="], 1776 1803 1777 1804 "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 1805 + 1806 + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], 1778 1807 1779 1808 "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], 1780 1809
+1
package.json
··· 11 11 "dependencies": { 12 12 "@astrojs/mdx": "5.0.2", 13 13 "@astrojs/netlify": "^7.0.4", 14 + "@astrojs/node": "^10.0.4", 14 15 "@astrojs/react": "5.0.1", 15 16 "@lucide/astro": "^0.563.0", 16 17 "@nanostores/react": "^1.0.0",
+40
src/actions/index.ts
··· 1 + import type { RecentTracks, Track, TrackResponse } from "@/types/lastfm"; 2 + import { ActionError, defineAction } from "astro:actions"; 3 + import { subDays } from "date-fns"; 4 + 5 + export const server = { 6 + getRecentTrack: defineAction({ 7 + handler: async () => { 8 + const opts = { 9 + apiKey: import.meta.env.LAST_FM_API_KEY, 10 + baseUrl: import.meta.env.LAST_FM_BASE_URL, 11 + user: "dustycode", 12 + method: 'user.getrecenttracks', 13 + nowplaying: 'true', 14 + limit: 1, 15 + format: 'json', 16 + from: subDays(Date.now(), 1) 17 + } 18 + 19 + const responseRaw = await fetch(`${opts.baseUrl}?method=${opts.method}&user=${opts.user}&api_key=${opts.apiKey}&limit=${opts.limit}&from=${opts.from}&nowplaying=${opts.nowplaying}&format=${opts.format}`) 20 + if (responseRaw.status !== 200) { 21 + throw new ActionError({ 22 + code: "INTERNAL_SERVER_ERROR", 23 + message: "Could not fetch track" 24 + }) 25 + } 26 + 27 + const responseJson: RecentTracks = await responseRaw.json(); 28 + const track: Track = responseJson.recenttracks.track[0]; 29 + const response: TrackResponse = { 30 + name: track.name, 31 + artist: track.artist["#text"], 32 + nowplaying: track["@attr"]?.nowplaying ?? false, 33 + url: track.url, 34 + image: track.image[1]["#text"] 35 + } 36 + return response; 37 + 38 + } 39 + }) 40 + }
src/assets/edmund_fitzgerald.png

This is a binary file and will not be displayed.

src/components/hooks/yooper.ts

This is a binary file and will not be displayed.

+9 -11
src/components/lastfm.tsx
··· 1 1 import clsx from 'clsx'; 2 2 import { useEffect, useState } from "react"; 3 3 import type { TrackResponse } from '../types/lastfm.ts'; 4 + import { actions } from 'astro:actions'; 4 5 5 6 export default function LastFM() { 6 - const [track, setTrack] = useState<TrackResponse>(); 7 + const [track, setTrack] = useState<TrackResponse | undefined>(); 7 8 const [loading, setLoading] = useState(true); 8 9 const [error, setError] = useState(null); 9 10 10 11 useEffect(() => { 11 - fetch('/api/lastfm/recentTracks') 12 - .then(res => res.json()) 13 - .then(data => { 14 - setTrack(data); 15 - setLoading(false); 16 - }) 17 - .catch(err => { 18 - setError(err.message); 19 - setLoading(false); 20 - }) 12 + actions.getRecentTrack().then((data) => { 13 + setTrack(data.data); 14 + setLoading(false); 15 + }).catch(err => { 16 + setError(err.message); 17 + setLoading(false); 18 + }) 21 19 }, []) 22 20 23 21 if (loading) return (
-25
src/components/yooperButton.tsx
··· 1 - import { useState } from "react" 2 - 3 - export function YooperButton() { 4 - 5 - const [isPlaying, setIsPlaying] = useState(false); 6 - 7 - 8 - return ( 9 - <div className="inline"> 10 - <button 11 - className="text-blue hover:cursor-pointer" 12 - onClick={() => { 13 - setIsPlaying(true); 14 - }} 15 - >Upper Peninsula</button 16 - > 17 - <audio id="audioPlayer" preload="auto"> 18 - <source 19 - src="https://ia801608.us.archive.org/31/items/gl_20230503/02%20The%20Wreck%20of%20the%20Edmund%20Fitzgerald.mp3" 20 - type="audio/mp3" 21 - /> 22 - </audio> 23 - </div> 24 - ) 25 - }
-45
src/pages/api/lastfm/recentTracks.ts
··· 1 - import type { RecentTracks, Track, TrackResponse } from "@/types/lastfm" 2 - import type { APIRoute } from "astro" 3 - import { subDays } from "date-fns" 4 - export const prerender = false; 5 - 6 - export const GET: APIRoute = async () => { 7 - 8 - const opts = { 9 - apiKey: import.meta.env.LAST_FM_API_KEY, 10 - baseUrl: import.meta.env.LAST_FM_BASE_URL, 11 - user: "dustycode", 12 - method: 'user.getrecenttracks', 13 - nowplaying: 'true', 14 - limit: 1, 15 - format: 'json', 16 - from: subDays(Date.now(), 1) 17 - } 18 - 19 - const responseRaw = await fetch(`${opts.baseUrl}?method=${opts.method}&user=${opts.user}&api_key=${opts.apiKey}&limit=${opts.limit}&from=${opts.from}&nowplaying=${opts.nowplaying}&format=${opts.format}`) 20 - if (responseRaw.status !== 200) { 21 - return new Response(JSON.stringify({ 22 - message: "Error fetching recent track" 23 - }), { 24 - status: responseRaw.status 25 - }) 26 - } 27 - 28 - const responseJson: RecentTracks = await responseRaw.json(); 29 - const track: Track = responseJson.recenttracks.track[0]; 30 - const response: TrackResponse = { 31 - name: track.name, 32 - artist: track.artist["#text"], 33 - nowplaying: track["@attr"]?.nowplaying ?? false, 34 - url: track.url, 35 - image: track.image[1]["#text"] 36 - } 37 - return new Response( 38 - JSON.stringify(response), 39 - { 40 - headers: { 41 - 'Content-Type': 'application/json' 42 - } 43 - } 44 - ) 45 - }