├── .env.example ├── app ├── globals.css ├── favicon.ico ├── actions.ts ├── layout.tsx ├── page.tsx └── api │ └── podcast │ └── route.js ├── public └── images │ └── podcast.jpeg ├── next.config.mjs ├── postcss.config.mjs ├── tailwind.config.ts ├── utils └── types.ts ├── .gitignore ├── tsconfig.json ├── package.json ├── components ├── footer.tsx └── story.tsx └── README.md /.env.example: -------------------------------------------------------------------------------- 1 | UPSTASH_REDIS_REST_URL= 2 | UPSTASH_REDIS_REST_TOKEN= -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upstash/radio-hackernews-web/master/app/favicon.ico -------------------------------------------------------------------------------- /public/images/podcast.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upstash/radio-hackernews-web/master/public/images/podcast.jpeg -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 7 | ], 8 | theme: { 9 | extend: { 10 | fontFamily: { 11 | sans: ["var(--font-inter)"], 12 | }, 13 | }, 14 | }, 15 | plugins: [], 16 | }; 17 | export default config; 18 | -------------------------------------------------------------------------------- /utils/types.ts: -------------------------------------------------------------------------------- 1 | export interface Story { 2 | id: string; 3 | title: string; 4 | url: string; 5 | score: number; 6 | summaryAudioDuration: number; 7 | summaryAudio: string; 8 | readableTime: string; // Added this field 9 | } 10 | 11 | export enum ResultCode { 12 | Success = "SUCCESS", 13 | UnknownError = "UNKNOWN_ERROR", 14 | } 15 | 16 | export interface Result { 17 | code: ResultCode; 18 | data: Story[]; 19 | } 20 | -------------------------------------------------------------------------------- /app/actions.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { Redis } from "@upstash/redis"; 4 | import { Result, ResultCode } from "@/utils/types"; 5 | 6 | const redis = Redis.fromEnv(); 7 | 8 | export async function getTopStories(): Promise { 9 | try { 10 | const data: [] = await redis.zrange("stories", 0, 50, { rev: true }); 11 | 12 | return { 13 | code: ResultCode.Success, 14 | data, 15 | }; 16 | } catch (error) { 17 | return { code: ResultCode.UnknownError, data: [] }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | .idea 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import Footer from "@/components/footer"; 5 | 6 | const defaultFont = Inter({ 7 | variable: "--font-inter", 8 | subsets: ["latin"], 9 | }); 10 | 11 | export const metadata: Metadata = { 12 | title: "Radio Hackernews", 13 | description: 14 | "Audio Recap of Top Hackernews Stories - Open Source Project by Upstash", 15 | }; 16 | 17 | export default function RootLayout({ 18 | children, 19 | }: Readonly<{ 20 | children: React.ReactNode; 21 | }>) { 22 | return ( 23 | 24 | 25 | {children} 26 |