Compare commits

..

No commits in common. 'main' and 'reactors' have entirely different histories.

@ -1,7 +1,7 @@
# Install dependencies only when needed # Install dependencies only when needed
FROM node:22-alpine AS deps FROM node:18-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat RUN apk add --no-cache libc6-compat sqlite
WORKDIR /app WORKDIR /app
# Install dependencies based on the preferred package manager # Install dependencies based on the preferred package manager
@ -15,7 +15,7 @@ RUN \
# Rebuild the source code only when needed # Rebuild the source code only when needed
FROM node:22-alpine AS builder FROM node:16-alpine AS builder
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
@ -25,18 +25,18 @@ COPY . .
# Uncomment the following line in case you want to disable telemetry during the build. # Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1 # ENV NEXT_TELEMETRY_DISABLED 1
# RUN yarn build RUN yarn build
# If using npm comment out above and use below instead # If using npm comment out above and use below instead
RUN npm run build # RUN npm run build
# Production image, copy all the files and run next # Production image, copy all the files and run next
FROM node:22-alpine AS runner FROM node:16-alpine AS runner
WORKDIR /app WORKDIR /app
ENV NODE_ENV production ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime. # Uncomment the following line in case you want to disable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1 # ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs RUN adduser --system --uid 1001 nextjs
@ -47,13 +47,11 @@ COPY --from=builder /app/public ./public
# https://nextjs.org/docs/advanced-features/output-file-tracing # https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/node_modules/next/dist/compiled/jest-worker ./node_modules/next/dist/compiled/jest-worker
USER nextjs USER nextjs
EXPOSE 3000 EXPOSE 3000
ENV PORT 3000 ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"] CMD ["node", "server.js"]

@ -1,10 +0,0 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
setupNodeEvents(on, config) {
// implement node event listeners here
}
},
});

@ -1,79 +0,0 @@
// cypress/integration/create_account.spec.js
describe('Create Account Page', () => {
beforeEach(() => {
// Set the base URL and visit the create account page
cy.request('POST', '/api/reset-test-db').then((response) => {
// Expect the response status to be 200, otherwise log an error message and stop the tests
expect(response.status, 'Reset test database successfully').to.eq(200);
// Visit the create account page if the response status is 200
cy.visit('/reactors/create-account?csi=cs_test_a1WLk3QvOyIJRFeV21BNIhdtXx26z5rF2x6pIzYKHq32ujVSz4W4fZ0IGI');
});
});
it('renders the page correctly', () => {
cy.contains('The Reactors - Create Account');
cy.get('input[name="emailprefilled"]').should('be.disabled');
cy.get('input[name="emailprefilled"]').should('have.value', 'me@thintz.com');
cy.get('input[name="password"]').should('have.attr', 'minlength', '12');
cy.get('input[name="passwordagain"]').should('have.attr', 'minlength', '12');
});
it('shows an error message when passwords do not match', () => {
cy.get('input[name="password"]').type('ValidPassword123');
cy.get('input[name="passwordagain"]').type('InvalidPassword456');
cy.get('button[type="submit"]').click();
cy.contains('There was an error with your submission');
});
it('shows an error message when the password is too short', () => {
cy.get('input[name="password"]').type('Short');
cy.get('input[name="passwordagain"]').type('Short');
cy.get('button[type="submit"]').click();
cy.contains('There was an error with your submission');
});
it('submits the form successfully with valid input', () => {
const validPassword = 'ValidPassword123';
cy.get('input[name="password"]').type(validPassword);
cy.get('input[name="passwordagain"]').type(validPassword);
cy.get('button[type="submit"]').click();
cy.url().should('eq', `${Cypress.config().baseUrl}/reactors`)
});
it('should rate limit when sending multiple requests', () => {
const testPassword = 'aVerySecureP@ssword123';
// A helper function to submit the create account form
const submitForm = () => {
cy.get('input[name="password"]').type(testPassword);
cy.get('input[name="passwordagain"]').type(testPassword);
cy.get('button[type="submit"').click();
};
submitForm();
// Check that the user is redirected (assuming success)
cy.url().should('not.include', '/reactors/create-account');
cy.visit('/reactors/create-account?csi=cs_test_a1WLk3QvOyIJRFeV21BNIhdtXx26z5rF2x6pIzYKHq32ujVSz4W4fZ0IGI');
// Send requests up to the rate limit
for (let i = 0; i < 8; i++) {
submitForm();
cy.contains('Unexpected Error');
// Return to the create account page for the next iteration
cy.visit('/reactors/create-account?csi=cs_test_a1WLk3QvOyIJRFeV21BNIhdtXx26z5rF2x6pIzYKHq32ujVSz4W4fZ0IGI');
}
// The 11th request should trigger rate limiting
submitForm();
// Check that the user is still on the create account page with a rate-limiting error message
cy.url().should('include', '/rate-limited');
cy.contains('Too many requests.');
});
});

@ -1,31 +0,0 @@
describe('Podcast episode page', () => {
beforeEach(() => {
cy.visit('/podcast/mechanics-of-react-a-beginners-intro-to-react')
})
it('loads correctly', () => {
// Check that the page title is correct
cy.title().should('equal', `Mechanics of React: A Beginner's Intro To React | The React Show`)
// Check that the date is displayed
cy.get('[data-cy=date]').should('contain', 'March 31, 2023')
// Check that the YouTube video is embedded and playable
cy.get('iframe[src^="https://www.youtube.com/embed/"]').should('be.visible')
// Check that the title is correct
cy.get('[data-cy=title]').should('contain', `[87] Mechanics of React: A Beginner's Intro To React`)
// Check that the description is correct
cy.get('[data-cy=description]').should('contain', 'Learn the fundamentals of React')
// Check that the transcript is correct
cy.get('[data-cy=transcript]').should('contain', '00:00:05 Brought to you from occupied Miwok territory by me')
})
it('should play the audio player when the "Listen" button is clicked', () => {
cy.get('[data-cy=audio-player-button]').first().should('have.attr', 'aria-label', 'Play')
cy.get('[data-cy=audio-player-button]').first().click()
cy.get('[data-cy=audio-player-button]').first().should('have.attr', 'aria-label', 'Pause')
})
})

@ -1,26 +0,0 @@
describe('Episodes component', () => {
beforeEach(() => {
cy.visit('/')
})
it.only('should render the component with episodes data', () => {
cy.get('[data-cy=episode-entry]').should('have.length', 15)
cy.get('[data-cy=episode-title]').first().should('contain', `[87] Mechanics of React: A Beginner's Intro To React`)
cy.get('[data-cy=episode-description]').first().should('contain', 'Learn the fundamentals of React by')
cy.get('[data-cy=episode-date]').first().should('contain', 'March 31, 2023')
cy.get('[data-cy=episode-title]').last().should('contain', '[73] A Fundamentally New React: My Journey with React Server Components')
cy.get('[data-cy=episode-description]').last().should('contain', 'React Sever Components are')
})
it('should navigate to the show notes page when the "Show notes" link is clicked', () => {
cy.get('[data-cy=show-notes-link]').first().click()
cy.url().should('include', '/podcast/mechanics-of-react-a-beginners-intro-to-react')
})
it('should navigate to the transcript section when the "Transcript" link is clicked', () => {
cy.get('[data-cy=transcript-link]').last().click()
cy.url().should('include', '/podcast/a-fundamentally-new-react-my-journey-with-react-server-components#transcript')
})
})

@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

@ -1,25 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

@ -1,20 +0,0 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

20797
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -10,38 +10,43 @@
}, },
"browserslist": "defaults, not ie <= 11", "browserslist": "defaults, not ie <= 11",
"dependencies": { "dependencies": {
"@extractus/feed-extractor": "^6.3.0", "@extractus/feed-extractor": "^6.2.1",
"@headlessui/react": "^1.7.15", "@headlessui/react": "^1.7.7",
"@heroicons/react": "^2.0.18", "@heroicons/react": "^2.0.13",
"@next/font": "13.3", "@next/font": "13.1.4",
"@next/mdx": "^13.4.10", "@next/mdx": "^13.1.6",
"@tailwindcss/forms": "^0.5.4", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.2",
"@tailwindcss/typography": "^0.5.9", "@tailwindcss/typography": "^0.5.7",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"cookies-next": "^2.1.2", "cookies-next": "^2.1.1",
"eslint": "8.32.0", "eslint": "8.32.0",
"eslint-config-next": "13.3", "eslint-config-next": "13.1.4",
"focus-visible": "^5.2.0", "focus-visible": "^5.2.0",
"i": "^0.3.7", "i": "^0.3.7",
"next": "^13.4.13", "mp3-duration": "^1.1.0",
"next13-progressbar": "^0.1.2", "next": "^13.1.7-canary.12",
"nodemailer": "^6.9.3", "nodemailer": "^6.9.1",
"podcast": "^2.0.1", "podcast": "^2.0.1",
"postcss-focus-visible": "^6.0.4", "postcss-focus-visible": "^6.0.4",
"ra-data-json-server": "^4.9.0",
"ra-data-simple-rest": "^4.9.0",
"ra-input-rich-text": "^4.9.0",
"react": "18.2.0", "react": "18.2.0",
"react-aria": "^3.26.0", "react-admin": "^4.9.0",
"react-aria": "^3.19.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-stately": "^3.24.0", "react-stately": "^3.17.0",
"sanitize-html": "^2.11.0", "sanitize-html": "^2.8.1",
"sharp": "^0.32.3", "sqlite3": "^5.1.4",
"srtparsejs": "^1.0.8", "srtparsejs": "^1.0.8",
"stripe": "^11.8.0",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/aspect-ratio": "^0.4.2",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.13",
"postcss": "^8.4.26", "postcss": "^8.4.21",
"tailwindcss": "^3.3.3" "tailwindcss": "^3.2.4"
} }
} }

@ -84,14 +84,13 @@ export const EpisodeShow = (props) => (
<TextField source="title" /> <TextField source="title" />
<NumberField source="season" /> <NumberField source="season" />
<NumberField source="episode" /> <NumberField source="episode" />
<NumberField source="duration" />
<TextField source="slug" /> <TextField source="slug" />
<TextField source="episode_type" /> <TextField source="episode_type" />
<TextField source="buzzsprout_id" /> <TextField source="buzzsprout_id" />
<UrlField source="buzzsprout_url" /> <UrlField source="buzzsprout_url" />
<UrlField source="youtube_url" /> <UrlField source="youtube_url" />
<UrlField source="audio_url" /> <UrlField source="audio_url" />
<NumberField source="duration" label="Duration (seconds)" />
<NumberField source="audio_size" label="Size (bytes)" />
<FileField source="transcript_filename" title="transcript_filename" /> <FileField source="transcript_filename" title="transcript_filename" />
<DateField source="pub_date" showTime={true} /> <DateField source="pub_date" showTime={true} />
<RichTextField source="content" /> <RichTextField source="content" />
@ -101,44 +100,26 @@ export const EpisodeShow = (props) => (
); );
export const EpisodeEdit = () => { export const EpisodeEdit = () => {
const { data: transcriptFiles } = useGetList( return (
'transcript_files' <Edit>
<SimpleForm>
<TextInput source="number" />
<TextInput source="title" />
<NumberInput source="season" />
<NumberInput source="episode" />
<TextInput source="slug" />
<TextInput source="episode_type" />
<TextInput source="buzzsprout_id" />
<TextInput source="buzzsprout_url" />
<TextInput source="youtube_url" />
<TextInput source="audio_url" />
<TextInput source="transcript_filename" />
<DateTimeInput source="pub_date" />
<RichTextInput source="content" />
<TextInput source="summary" />
</SimpleForm>
</Edit>
); );
if (transcriptFiles) {
return (
<Edit>
<SimpleForm>
<TextInput source="number" />
<TextInput source="title" fullWidth />
<NumberInput source="season" />
<NumberInput source="episode" />
<TextInput source="slug" fullWidth />
<RadioButtonGroupInput
source="episode_type"
choices={[
{ id: 'full', name: 'full' },
{ id: 'bonus', name: 'bonus' }
]}
/>
<TextInput source="buzzsprout_id" />
<TextInput source="buzzsprout_url" fullWidth />
<TextInput source="youtube_url" fullWidth />
<TextInput source="audio_url" fullWidth />
<NumberInput source="duration" label="Duration (seconds)" />
<NumberInput source="audio_size" label="Size (bytes)" />
<SelectInput
source="transcript_filename"
choices={transcriptFiles.map(x => { return { id: x.filename, name: x.filename } })}
/>
<DateTimeInput source="pub_date" />
<RichTextInput source="content" />
<TextInput source="summary" fullWidth />
</SimpleForm>
</Edit>
);
} else {
return null;
}
}; };
function listElement(arr, proc) { function listElement(arr, proc) {
@ -163,7 +144,7 @@ export const EpisodeCreate = () => {
const { data: transcriptFiles } = useGetList( const { data: transcriptFiles } = useGetList(
'transcript_files' 'transcript_files'
); );
if (lastNumber && lastEpisode && transcriptFiles) { if (lastNumber && lastEpisode && audioFiles && transcriptFiles) {
return ( return (
<Create> <Create>
<SimpleForm> <SimpleForm>
@ -190,8 +171,6 @@ export const EpisodeCreate = () => {
<TextInput source="buzzsprout_url" fullWidth /> <TextInput source="buzzsprout_url" fullWidth />
<TextInput source="youtube_url" fullWidth /> <TextInput source="youtube_url" fullWidth />
<TextInput source="audio_url" fullWidth /> <TextInput source="audio_url" fullWidth />
<NumberInput source="audio_size" label="Size (bytes)" />
<NumberInput source="duration" label="Duration (seconds)" />
<SelectInput <SelectInput
source="transcript_filename" source="transcript_filename"
choices={transcriptFiles.map(x => { return { id: x.filename, name: x.filename } })} choices={transcriptFiles.map(x => { return { id: x.filename, name: x.filename } })}
@ -213,6 +192,7 @@ const App = () => (
<Resource name="users" list={UserList} /> <Resource name="users" list={UserList} />
<Resource name="subscriptions" list={SubscriptionList} /> <Resource name="subscriptions" list={SubscriptionList} />
<Resource name="episodes" list={EpisodesList} show={EpisodeShow} edit={EpisodeEdit} create={EpisodeCreate} /> <Resource name="episodes" list={EpisodesList} show={EpisodeShow} edit={EpisodeEdit} create={EpisodeCreate} />
<Resource name="audio_files" list={AudioFilesList} />
<Resource name="transcript_files" list={AudioFilesList} /> <Resource name="transcript_files" list={AudioFilesList} />
</Admin> </Admin>
); );

@ -2,11 +2,9 @@ import NavBar from '@/components/NavBar'
export default function ExtraLayout({children}) { export default function ExtraLayout({children}) {
return ( return (
<> <div>
<NavBar /> <NavBar showPodcast />
<div className="lg:pt-16"> {children}
{children} </div>
</div>
</>
); );
} }

@ -1,75 +0,0 @@
import Link from 'next/link';
import { BookOpenIcon, BanknotesIcon, StarIcon } from '@heroicons/react/24/outline'
// discord invite link: https://discord.gg/BneUtTSB
const features = [
{
name: 'Discord Server',
link: 'https://discord.gg/zXYggKUBC2',
description:
'Connect with the community for free on our Discord Server! Ask questions or just hang out!',
icon: StarIcon
},
/* {
* name: 'The Reactors Sub-Reddit',
* link: 'https://www.reddit.com/r/TheReactShow/',
* description:
* 'Discuss recent episodes, the show itself, or ask React questions on our sub-reddit!',
* icon: StarIcon
* }, */
{
name: 'The Reactors Premium Subscription',
link: 'https://thereactshow.supercast.com/',
description:
'Your own premium podcast feed with: bonus content, and early releases. The place to go if you want the regular show and behind-the-scenes details!',
icon: StarIcon
}
];
export const metadata = {
title: 'Join Our Community!',
description: 'We would love to have you join us whether you are just getting started or a programming expert!'
};
export default async function Page() {
return (
<div className="pt-4 pb-12 sm:pb-4 lg:pt-12">
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
<div className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:text-center">
<h2 className="text-lg font-semibold leading-8 tracking-tight text-indigo-600">Join the Community!</h2>
<p className="mt-2 text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
We would love to have you join us whether you are just getting started or a programming expert!
</p>
</div>
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-4xl">
<dl className="grid max-w-xl grid-cols-1 gap-y-10 gap-x-8 lg:max-w-none lg:grid-cols-2 lg:gap-y-16">
{features.map((feature) => (
<div key={feature.name} className={`relative pl-16 p-4 rounded-lg ${feature.highlight ? 'shadow-md border-2 border-green-500' : ''}`}>
<dt className="text-base font-semibold leading-7 text-gray-900">
<div className="absolute top-4 left-4 flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-600">
<Link href={feature.link}>
<feature.icon className="h-6 w-6 text-white" aria-hidden="true" />
</Link>
</div>
<Link
href={feature.link}
className="underline font-bold leading-6 text-pink-500 hover:text-pink-700 active:text-pink-900"
>
{feature.name}
</Link>
</dt>
<dd className="mt-2 text-base leading-7 text-gray-600">{feature.description}</dd>
</div>
))}
</dl>
</div>
</div>
</div>
</div>
</div>
);
}

@ -24,7 +24,7 @@ export default function Page() {
With React we actually have a built in mechanism that makes this really easy: React contexts. If we create a context for our labeling then all we have to do is set the label at the top of the component hierarchy and the rest of the application can just utilize the selected label. This is similar to how themeing is often done in React. With React we actually have a built in mechanism that makes this really easy: React contexts. If we create a context for our labeling then all we have to do is set the label at the top of the component hierarchy and the rest of the application can just utilize the selected label. This is similar to how themeing is often done in React.
</P> </P>
<P> <P>
To make this even easier I&apos;ve packaged up the context and its providers and consumers into an npm package: <A href="https://code.thintz.com/tjhintz/react-whitelabel">react-whitelabel</A>. To make this even easier I&apos;ve packaged up the context and its providers and consumers into an npm package: <A href="https://code.thintz.com/react-whitelabel/about/">react-whitelabel</A>.
</P> </P>
<P> <P>
So how do we use this package to white-label our React app? Here is a basic example: So how do we use this package to white-label our React app? Here is a basic example:
@ -75,7 +75,7 @@ class HeaderComponent extends React.Component {
// white-label context so we can access the 'label' prop. // white-label context so we can access the 'label' prop.
const Header = withWhiteLabelContext(HeaderComponent);`}</code></pre> const Header = withWhiteLabelContext(HeaderComponent);`}</code></pre>
<P> <P>
To see a fully working example check out the <A href="https://code.thintz.com/tjhintz/react-whitelabel">docs</A>. To see a fully working example check out the <A href="https://code.thintz.com/react-whitelabel/about/">docs</A>.
</P> </P>
<P> <P>
So how does this example work? First we setup our labels. This is just a Javascript object where each key maps to a label. Each label is also an object made up of referenceable keys. The <code>WhiteLabelProvider</code> then takes the labels and the selected label as input and then provides the selected label&apos;s object to the rest of the app. This object is accessible using a <code>WhiteLabelConsumer</code> or the <code>withWhiteLabelContext</code> wrapper. If we then made the selectedLabel dynamic, say via calling <code>window.location.hostname</code>, then we would have a fully run time dynamic white-label. So how does this example work? First we setup our labels. This is just a Javascript object where each key maps to a label. Each label is also an object made up of referenceable keys. The <code>WhiteLabelProvider</code> then takes the labels and the selected label as input and then provides the selected label&apos;s object to the rest of the app. This object is accessible using a <code>WhiteLabelConsumer</code> or the <code>withWhiteLabelContext</code> wrapper. If we then made the selectedLabel dynamic, say via calling <code>window.location.hostname</code>, then we would have a fully run time dynamic white-label.
@ -84,7 +84,7 @@ const Header = withWhiteLabelContext(HeaderComponent);`}</code></pre>
Also note that we are using the selected label&apos;s name as a class name in a top level div. This allows us to use the white-labeling with our CSS assets just by selecting based on the label name like <code>{`.cars a { ... }`}</code>. Also note that we are using the selected label&apos;s name as a class name in a top level div. This allows us to use the white-labeling with our CSS assets just by selecting based on the label name like <code>{`.cars a { ... }`}</code>.
</P> </P>
<P> <P>
While the <A href="https://code.thintz.com/tjhintz/react-whitelabel">react-whitelabel library</A> is not necessary for React run time white-labeling it does make it easier and more maintainable. If you implement it yourself you will likely do the same thing the library already does for you. If your interested learning more about react-whitelabel feel free to checkout the <A href="https://code.thintz.com/tjhintz/react-whitelabel">docs</A> or the <A href="https://www.npmjs.com/package/react-whitelabel">npm page</A>. While the <A href="https://code.thintz.com/react-whitelabel/about/">react-whitelabel library</A> is not necessary for React run time white-labeling it does make it easier and more maintainable. If you implement it yourself you will likely do the same thing the library already does for you. If your interested learning more about react-whitelabel feel free to checkout the <A href="https://code.thintz.com/react-whitelabel/about/">docs</A> or the <A href="https://www.npmjs.com/package/react-whitelabel">npm page</A>.
</P> </P>
</Article> </Article>
); );

@ -1,20 +1,156 @@
export const dynamic = 'force-dynamic'
import { redirect } from 'next/navigation';
import nodemailer from 'nodemailer';
import sanitizeHtml from 'sanitize-html';
import { Container } from '@/components/Container' import { Container } from '@/components/Container'
export default async function Page() { export default async function Page({ searchParams }) {
const firstName = searchParams['first-name'];
const lastName = searchParams['last-name'];
const email = searchParams['email'];
const message = searchParams['message'];
const submitted = firstName || lastName || email || message;
const valid = submitted && firstName && lastName && email && message;
let emailSentSuccessfully = false;
if (valid) {
const transporter = nodemailer.createTransport({
host: process.env.CONTACT_HOST,
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: process.env.CONTACT_USER,
pass: process.env.CONTACT_PASSWORD,
},
});
// send mail with defined transport object
await transporter.sendMail({
from: `"${firstName} ${lastName}" <${process.env.CONTACT_FROM_ADDRESS}>`,
replyTo: `"${firstName} ${lastName}" <${email}>`,
to: process.env.CONTACT_TO_ADDRESS,
subject: "The React Show - Form Submission",
text: message,
html: sanitizeHtml(message, {
allowedTags: [],
allowedAttributes: {}
}),
});
redirect('/contact-success')
}
return ( return (
<div className="pt-16 pb-12 sm:pb-4 lg:pt-12"> <div className="pt-16 pb-12 sm:pb-4 lg:pt-12">
<Container> <Container>
<h1 className="text-2xl font-bold leading-7 text-slate-900"> <h1 className="text-2xl font-bold leading-7 text-slate-900">
Contact Us Contact Us
</h1> </h1>
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100"> {valid && !emailSentSuccessfully && (
<p className="mt-4 italic"> <div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
Like the show? Want to hear us talk about something specific? Or just want to say hi? Wed love to hear from you! <p>
</p> Unable to send message. Please go back and reload the page and try again or try again later. Sorry!
<p className="mt-4 italic"> </p>
Send an email to thereactshow [at] thintz.com with the word &quot;NORIS&quot; somewhere in the subject line (to help with spam). Looking forward to it! </div>
</p> )}
</div> {!valid && (
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
<p className="mt-4 italic">
Like the show? Want to hear us talk about something specific? Or just want to say hi? Wed love to hear from you!
</p>
<form className="space-y-8">
<div className="space-y-8">
<div className="pt-8">
<div className="mt-6 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">
<div className="sm:col-span-3">
<label htmlFor="first-name" className="block text-sm font-medium text-gray-700">
First name
</label>
<div className="mt-1">
<input
type="text"
name="first-name"
id="first-name"
autoComplete="given-name"
defaultValue={firstName}
required
title="First/Given Name (required)"
className="block w-full rounded-md shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm border-gray-300"
/>
</div>
</div>
<div className="sm:col-span-3">
<label htmlFor="last-name" className="block text-sm font-medium text-gray-700">
Last name
</label>
<div className="mt-1">
<input
type="text"
name="last-name"
id="last-name"
autoComplete="family-name"
defaultValue={lastName}
required
title="Last/Family Name (required)"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
<div className="sm:col-span-4">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
defaultValue={email}
title="Email Address (required)"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
</div>
</div>
<div className="sm:col-span-6">
<label htmlFor="message" className="block text-sm font-medium text-gray-700">
Your Message
</label>
<div className="mt-1">
<textarea
id="message"
name="message"
rows={3}
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
defaultValue={message}
required
title="Your message to us! (required)"
/>
</div>
</div>
</div>
<div className="pt-5">
<div className="flex justify-end">
<button
type="submit"
className="ml-3 inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Send
</button>
</div>
</div>
</form>
</div>
)}
</Container> </Container>
</div> </div>
); );

@ -2,7 +2,6 @@ import { Fragment, useId } from 'react'
import Image from 'next/image' import Image from 'next/image'
import Link from 'next/link' import Link from 'next/link'
import NavBar from '@/components/NavBar' import NavBar from '@/components/NavBar'
import posterImage from '@/images/poster.png' import posterImage from '@/images/poster.png'
@ -20,7 +19,7 @@ function Waveform(props) {
total: 100, total: 100,
width: 2, width: 2,
gap: 2, gap: 2,
minHeight: 60, minHeight: 40,
maxHeight: 100, maxHeight: 100,
} }
@ -164,30 +163,29 @@ function AboutSection(props) {
<p <p
className="mt-2 text-base leading-7 text-slate-700" className="mt-2 text-base leading-7 text-slate-700"
> >
In this show, <Link href="https://thintz.com">Thomas</Link> digs deep to understand React and how best to utilize it while discussing real world experiences with: React, programming, and software engineering. Tune in every Friday (usually) to hear the latest in the React community. In this show, Thomas digs deep to understand React and how best to utilize it while discussing real world experiences with: React, programming, and software engineering. Tune in every Friday (usually) to hear the latest in the React community.
</p> </p>
</section> </section>
) )
} }
function Layout({ children }) { function Layout({ children }) {
let hosts = [{ name: 'Thomas Hintz', href: 'https://thintz.com' }] let hosts = ['Thomas Hintz']
return ( return (
<> <>
<NavBar /> <header className="bg-slate-50 lg:fixed lg:inset-y-0 lg:left-0 lg:flex lg:w-112 lg:items-start lg:overflow-y-auto xl:w-120">
<header className="bg-slate-50 lg:fixed lg:inset-y-0 lg:left-0 lg:flex lg:w-112 lg:items-start lg:overflow-y-auto xl:w-120 lg:mt-16">
<div className="hidden lg:sticky lg:top-0 lg:flex lg:w-16 lg:flex-none lg:items-center lg:whitespace-nowrap lg:py-12 lg:text-sm lg:leading-7 lg:[writing-mode:vertical-rl]"> <div className="hidden lg:sticky lg:top-0 lg:flex lg:w-16 lg:flex-none lg:items-center lg:whitespace-nowrap lg:py-12 lg:text-sm lg:leading-7 lg:[writing-mode:vertical-rl]">
<span className="font-mono text-slate-500">Hosted by</span> <span className="font-mono text-slate-500">Hosted by</span>
<span className="mt-6 flex gap-6 font-bold text-slate-900"> <span className="mt-6 flex gap-6 font-bold text-slate-900">
{hosts.map(({ name, href }, hostIndex) => ( {hosts.map((host, hostIndex) => (
<Fragment key={name}> <Fragment key={host}>
{hostIndex !== 0 && ( {hostIndex !== 0 && (
<span aria-hidden="true" className="text-slate-400"> <span aria-hidden="true" className="text-slate-400">
/ /
</span> </span>
)} )}
<Link href={href}>{name}</Link> {host}
</Fragment> </Fragment>
))} ))}
</span> </span>
@ -196,7 +194,7 @@ function Layout({ children }) {
<div className="px-4 sm:px-6 md:px-4 lg:px-8 xl:px-12 mb-10"> <div className="px-4 sm:px-6 md:px-4 lg:px-8 xl:px-12 mb-10">
<Link <Link
href="/" href="/"
className="relative mx-auto block w-48 overflow-hidden rounded-lg bg-slate-200 shadow-xl shadow-slate-200 sm:w-64 sm:rounded-xl lg:w-auto lg:rounded-2xl mt-16 lg:mt-0" className="relative mx-auto block w-48 overflow-hidden rounded-lg bg-slate-200 shadow-xl shadow-slate-200 sm:w-64 sm:rounded-xl lg:w-auto lg:rounded-2xl"
aria-label="Homepage" aria-label="Homepage"
> >
<Image <Image
@ -209,6 +207,7 @@ function Layout({ children }) {
<div className="absolute inset-0 rounded-lg ring-1 ring-inset ring-black/10 sm:rounded-xl lg:rounded-2xl" /> <div className="absolute inset-0 rounded-lg ring-1 ring-inset ring-black/10 sm:rounded-xl lg:rounded-2xl" />
</Link> </Link>
</div> </div>
<NavBar />
<div className="mt-6 text-center lg:mt-12 lg:text-left px-4 sm:px-6 md:px-4 lg:px-8 xl:px-12"> <div className="mt-6 text-center lg:mt-12 lg:text-left px-4 sm:px-6 md:px-4 lg:px-8 xl:px-12">
<p className="text-xl font-bold text-slate-900"> <p className="text-xl font-bold text-slate-900">
<Link href="/">The React Show</Link> <Link href="/">The React Show</Link>
@ -255,8 +254,8 @@ function Layout({ children }) {
</div> </div>
</header> </header>
<main className="border-t border-slate-200 lg:relative lg:mb-28 lg:ml-112 lg:border-t-0 xl:ml-120"> <main className="border-t border-slate-200 lg:relative lg:mb-28 lg:ml-112 lg:border-t-0 xl:ml-120">
<Waveform className="absolute left-0 top-0 h-20 w-full pointer-events-none lg:mt-16 z-30 lg:z-0" /> <Waveform className="absolute left-0 top-0 h-20 w-full" />
<div className="relative lg:pt-16">{children}</div> <div className="relative">{children}</div>
</main> </main>
<footer className="border-t border-slate-200 bg-slate-50 py-10 pb-40 sm:py-16 sm:pb-32 lg:hidden"> <footer className="border-t border-slate-200 bg-slate-50 py-10 pb-40 sm:py-16 sm:pb-32 lg:hidden">
<div className="mx-auto px-4 sm:px-6 md:max-w-2xl md:px-4"> <div className="mx-auto px-4 sm:px-6 md:max-w-2xl md:px-4">
@ -266,14 +265,14 @@ function Layout({ children }) {
<span className="ml-2.5">Hosted by</span> <span className="ml-2.5">Hosted by</span>
</h2> </h2>
<div className="mt-2 flex gap-6 text-sm font-bold leading-7 text-slate-900"> <div className="mt-2 flex gap-6 text-sm font-bold leading-7 text-slate-900">
{hosts.map(({ name, href }, hostIndex) => ( {hosts.map((host, hostIndex) => (
<Fragment key={name}> <Fragment key={host}>
{hostIndex !== 0 && ( {hostIndex !== 0 && (
<span aria-hidden="true" className="text-slate-400"> <span aria-hidden="true" className="text-slate-400">
/ /
</span> </span>
)} )}
<Link href={href}>{name}</Link> {host}
</Fragment> </Fragment>
))} ))}
</div> </div>

@ -3,6 +3,64 @@ import Link from 'next/link'
import { Container } from '@/components/Container' import { Container } from '@/components/Container'
import Episodes from '@/components/Episodes' import Episodes from '@/components/Episodes'
function Skeleton({ width, height, className, color }) {
const w = width ? '' : 'w-full';
const c = color ? color : 'bg-slate-200';
return (
<div className={`animate-pulse flex ${c} ${w} ${className}`} style={{ width, height }} />
);
};
function EpisodeEntryLoading() {
return (
<div
className="py-10 sm:py-12"
>
<Container>
<div className="flex flex-col items-start w-full">
<div className="order-first font-mono text-sm leading-7 text-slate-500">
<Skeleton height="1.75rem" width="140px" />
</div>
<h2
className="mt-2 text-lg font-bold text-slate-900 w-full"
>
<Skeleton height="1.75rem" />
</h2>
<div className="mt-1 text-base leading-7 text-slate-700 w-full">
<Skeleton height="1rem" className="mt-3" />
<Skeleton height="1rem" className="mt-3" />
<Skeleton height="1rem" className="mt-3" />
</div>
<div className="mt-4 flex items-center gap-4">
<Skeleton width="0.625rem" height="0.625rem" />
<span className="ml-3">
<Skeleton height="1.5rem" width="42.275px" />
</span>
<span
aria-hidden="true"
className="text-sm font-bold text-slate-400"
>
/
</span>
<Skeleton height="1.5rem" width="80.175px" />
</div>
</div>
</Container>
</div>
)
}
function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return (
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
{[0, 1, 2, 3, 4, 5, 6, 7].map((i) => (
<EpisodeEntryLoading key={i} />
))}
</div>
);
}
export const metadata = { export const metadata = {
description: "Weekly podcast focused on React, programming, and software engineering." description: "Weekly podcast focused on React, programming, and software engineering."
}; };

@ -1,10 +1,10 @@
// export const dynamic = 'force-dynamic' export const dynamic = 'force-dynamic'
// import { setTimeout } from 'timers/promises'; import { setTimeout } from 'timers/promises';
import { Suspense } from "react"; import { Suspense } from "react";
// import chaptersStatic from './chapters.json' assert { type: 'json' }; import chaptersStatic from './chapters.json' assert { type: 'json' };
import { Container } from '@/components/Container' import { Container } from '@/components/Container'
import { FormattedDate } from '@/components/FormattedDate' import { FormattedDate } from '@/components/FormattedDate'
@ -32,7 +32,7 @@ function Transcript({ children }) {
<> <>
<hr className="my-12 border-gray-200" /> <hr className="my-12 border-gray-200" />
<h2 className="mt-2 text-3xl font-bold text-slate-900" id="transcript">Transcript</h2> <h2 className="mt-2 text-3xl font-bold text-slate-900" id="transcript">Transcript</h2>
<div className="space-y-4" data-cy="transcript"> <div className="space-y-4">
{children} {children}
</div> </div>
</> </>
@ -60,42 +60,29 @@ function TranscriptNoPlayer({ episode }) {
} }
async function TranscriptWithPlayer({ episode }) { async function TranscriptWithPlayer({ episode }) {
// const chaptersRes = episode?.chapters && await fetch(episode.chapters, { cache: 'no-store' }); const chaptersRes = episode?.chapters && await fetch(episode.chapters, { cache: 'no-store' });
/* await setTimeout(5000); */ /* await setTimeout(5000); */
/* const { chapters } = chaptersStatic */ /* const { chapters } = chaptersStatic */
/* const { chapters } = chaptersRes ? await chaptersRes.json() : { chapters: null } const { chapters } = chaptersRes ? await chaptersRes.json() : { chapters: null }
* let chapterOffsets = [[0, 0]] let chapterOffsets = [[0, 0]]
* if (chapters) { if (chapters) {
* chapters.reduce(({ startTime: prevStartTime, title: prevTitle, acc }, { title, startTime }) => { chapters.reduce(({ startTime: prevStartTime, title: prevTitle, acc }, { title, startTime }) => {
* const containsAd = prevTitle.includes('[Ad]') const containsAd = prevTitle.includes('[Ad]')
* if (containsAd) { if (containsAd) {
* chapterOffsets.push([prevStartTime, acc + (startTime - prevStartTime)]) chapterOffsets.push([prevStartTime, acc + (startTime - prevStartTime)])
* } }
* return { startTime, title, acc: containsAd ? acc + (startTime - prevStartTime) : acc } return { startTime, title, acc: containsAd ? acc + (startTime - prevStartTime) : acc }
* }, { startTime: 0, title: '', acc: 0 }) }, { startTime: 0, title: '', acc: 0 })
* } }
* chapterOffsets = chapterOffsets.reverse() */ chapterOffsets = chapterOffsets.reverse()
/* (
* <Transcript>
* {episode.transcript.map(({ id, startTime, endTime, text }) => (
* <p key={id}>
* <Player
* episode={episode}
* startTime={parseTime(startTime) + chapterOffsets.find(([start]) => parseTime(startTime) >= start)[1]}
* endTime={parseTime(endTime) + chapterOffsets.find(([start]) => parseTime(startTime) >= start)[1]} />
* <strong><time>{humanTime(startTime)}</time></strong> {text}
* </p>
* ))}
* </Transcript>
* ) */
return ( return (
<Transcript> <Transcript>
{episode.transcript.map(({ id, startTime, endTime, text }) => ( {episode.transcript.map(({ id, startTime, endTime, text }) => (
<p key={id}> <p key={id}>
<Player <Player
episode={episode} episode={episode}
startTime={parseTime(startTime)} startTime={parseTime(startTime) + chapterOffsets.find(([start]) => parseTime(startTime) >= start)[1]}
endTime={parseTime(endTime)} /> endTime={parseTime(endTime) + chapterOffsets.find(([start]) => parseTime(startTime) >= start)[1]} />
<strong><time>{humanTime(startTime)}</time></strong> {text} <strong><time>{humanTime(startTime)}</time></strong> {text}
</p> </p>
))} ))}
@ -132,13 +119,12 @@ export default async function Page({ params }) {
<div className="flex items-center gap-6"> <div className="flex items-center gap-6">
<PlayButtonClient audioPlayerData={audioPlayerData} size="large" /> <PlayButtonClient audioPlayerData={audioPlayerData} size="large" />
<div className="flex flex-col"> <div className="flex flex-col">
<h1 className="mt-2 text-4xl font-bold text-slate-900" data-cy="title"> <h1 className="mt-2 text-4xl font-bold text-slate-900">
[{episode.num}] {episode.title} [{episode.num}] {episode.title}
</h1> </h1>
<FormattedDate <FormattedDate
date={date} date={date}
className="order-first font-mono text-sm leading-7 text-slate-500" className="order-first font-mono text-sm leading-7 text-slate-500"
data-cy="date"
/> />
</div> </div>
</div> </div>
@ -156,7 +142,6 @@ export default async function Page({ params }) {
<div <div
className="prose prose-slate mt-14 [&>h2]:mt-12 [&>h2]:flex [&>h2]:items-center [&>h2]:font-mono [&>h2]:text-sm [&>h2]:font-medium [&>h2]:leading-7 [&>h2]:text-slate-900 [&>h2]:before:mr-3 [&>h2]:before:h-3 [&>h2]:before:w-1.5 [&>h2]:before:rounded-r-full [&>h2]:before:bg-cyan-200 [&>ul]:mt-6 [&>ul]:list-['\2013\20'] [&>ul]:pl-5 [&>h2:nth-of-type(3n+2)]:before:bg-indigo-200 [&>h2:nth-of-type(3n)]:before:bg-violet-200" className="prose prose-slate mt-14 [&>h2]:mt-12 [&>h2]:flex [&>h2]:items-center [&>h2]:font-mono [&>h2]:text-sm [&>h2]:font-medium [&>h2]:leading-7 [&>h2]:text-slate-900 [&>h2]:before:mr-3 [&>h2]:before:h-3 [&>h2]:before:w-1.5 [&>h2]:before:rounded-r-full [&>h2]:before:bg-cyan-200 [&>ul]:mt-6 [&>ul]:list-['\2013\20'] [&>ul]:pl-5 [&>h2:nth-of-type(3n+2)]:before:bg-indigo-200 [&>h2:nth-of-type(3n)]:before:bg-violet-200"
dangerouslySetInnerHTML={{ __html: episode.content || 'CONTENT' }} dangerouslySetInnerHTML={{ __html: episode.content || 'CONTENT' }}
data-cy="description"
/> />
{episode?.transcript && <Suspense fallback={<TranscriptNoPlayer episode={episode} />}> {episode?.transcript && <Suspense fallback={<TranscriptNoPlayer episode={episode} />}>
<TranscriptWithPlayer episode={episode} /> <TranscriptWithPlayer episode={episode} />

@ -0,0 +1,39 @@
import Link from 'next/link';
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';
import db from '@/db';
async function getSession() {
const cookieStore = cookies();
const sessionId = cookieStore.get('session');
if (!sessionId) {
return false;
}
const { user_id: userId } = await db.get('select user_id from sessions where session_id=?;', sessionId.value);
if (!userId) {
return false;
}
return userId;
};
export default async function Layout({ children }) {
const userId = await getSession();
if (!userId) {
redirect('/reactors');
}
return (
<>
signed in as: {userId}
<nav>
<ul>
<form method="POST" action="/api/sign-out">
<button type="submit">Sign Out</button>
</form>
</ul>
</nav>
{children}
</>
);
};

@ -0,0 +1,34 @@
import { cookies } from 'next/headers';
import db from '@/db';
import { accountFeedURL } from '@/paths';
import { Container } from '@/components/Container';
async function getSession() {
const cookieStore = cookies();
const sessionId = cookieStore.get('session');
if (!sessionId) {
return false;
}
const { user_id: userId } = await db.get('select user_id from sessions where session_id=?;', sessionId.value);
if (!userId) {
return false;
}
return userId;
};
export default async function Page() {
const userId = await getSession();
const { uuid } = await db.get('select uuid from subscriptions where user_id=?', userId);
return (
<div className="pt-16 pb-12 sm:pb-4 lg:pt-12">
<Container>
<h1 className="text-2xl font-bold leading-7 text-slate-900">
The Reactors
</h1>
{<p>feed URL: <a href={accountFeedURL(uuid)}>{accountFeedURL(uuid)}</a></p>}
</Container>
</div>
);
};

@ -0,0 +1,142 @@
export const dynamic = 'force-dynamic';
import Link from 'next/link'
import Stripe from 'stripe';
const stripe = new Stripe('sk_test_51MVz87Ke2JFOuDSNa2PVPrs3BBq9vJQwwDITC3sOB521weM4oklKtQFbJ03MNsJwsxtjHO5NScqOHC9MABREVjU900yYz3lWgL');
import { dbRun } from '@/db';
import { XCircleIcon } from '@heroicons/react/20/solid'
import { Container } from '@/components/Container';
// /reactors/create-account?csi=cs_test_a1pBB0FI8GUKnWYlCVn0RKUYXV8FRroacXjI5WVhWPlFJilm46lZwdjgac
export default async function Page({ searchParams }) {
const unexpectedError = searchParams['unexpected_error'];
const msg = searchParams['msg'];
const csi = searchParams['csi'];
const session = csi && await stripe.checkout.sessions.retrieve(csi);
const email = (csi && session && session.customer_details.email) || searchParams['email'];
const message = searchParams['message'];
const submitted = email || message;
const valid = submitted && email && message;
let emailSentSuccessfully = false;
if (unexpectedError) {
return (
<>
Unexpected Error sorry about that! Please contact us via <Link href="/contact">Contact</Link> and we will get it figured out!
</>
);
}
return (
<div className="pt-16 pb-12 sm:pb-4 lg:pt-12">
<Container>
<h1 className="text-2xl font-bold leading-7 text-slate-900">
The Reactors - Create Account
</h1>
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
<p>
Thank you so much for signing up to become a Reactor! We just need a password now to create an account for you!
</p>
{msg && (
<div className="rounded-md bg-red-50 p-4 mt-8">
<div className="flex">
<div className="flex-shrink-0">
<XCircleIcon className="h-5 w-5 text-red-400" aria-hidden="true" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">There was an error with your submission</h3>
<div className="mt-2 text-sm text-red-700">
{msg}
</div>
</div>
</div>
</div>
)}
<form className="space-y-8" method="POST" action="/api/create-account">
<input
name="csi"
value={csi}
type="hidden"
/>
<div className="space-y-8">
<div className="pt-8">
<div className="mt-6 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">
<div className="sm:col-span-3">
<label htmlFor="emailprefilled" className="block text-sm font-medium text-gray-700">
Email address
</label>
<div className="mt-1">
<input
id="emailprefilled"
name="emailprefilled"
type="email"
defaultValue={email}
disabled
title="Email Address (required)"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
<input
type="hidden"
name="email"
value={email}
/>
</div>
</div>
</div>
<div className="mt-6 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">
<div className="sm:col-span-3">
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
Password
</label>
<div className="mt-1">
<input
id="password"
name="password"
type="password"
required
minlength="12"
title="Password (required)"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
<div className="sm:col-span-3">
<label htmlFor="passwordagain" className="block text-sm font-medium text-gray-700">
Password (again)
</label>
<div className="mt-1">
<input
id="passwordagain"
name="passwordagain"
type="password"
required
minlength="12"
title="Password (required)"
className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
</div>
</div>
</div>
<div className="pt-5">
<div className="flex justify-end">
<button
type="submit"
className="ml-3 inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Create Account
</button>
</div>
</div>
</form>
</div>
</Container>
</div>
);
};

@ -1,19 +1,14 @@
import Link from 'next/link';
import { Container } from '@/components/Container'; import { Container } from '@/components/Container';
export const metadata = {
title: 'Rate Limited',
description: 'Rate limited.'
};
export default async function Page() { export default async function Page() {
return ( return (
<div className="pt-16 pb-12 sm:pb-4 lg:pt-12"> <div className="pt-16 pb-12 sm:pb-4 lg:pt-12">
<Container> <Container>
<h1 className="text-2xl font-bold leading-7 text-slate-900"> <h1 className="text-2xl font-bold leading-7 text-slate-900">
Rate Limited. Please Try Again Later. The Reactors
</h1> </h1>
<a href="https://buy.stripe.com/test_3cs01j768d65gso289">Level I</a>
</Container> </Container>
</div> </div>
); );
} };

@ -0,0 +1,142 @@
export const dynamic = 'force-dynamic';
import Link from 'next/link'
import Stripe from 'stripe';
const stripe = new Stripe('sk_test_51MVz87Ke2JFOuDSNa2PVPrs3BBq9vJQwwDITC3sOB521weM4oklKtQFbJ03MNsJwsxtjHO5NScqOHC9MABREVjU900yYz3lWgL');
import { dbRun } from '@/db';
import { XCircleIcon } from '@heroicons/react/20/solid'
import { Container } from '@/components/Container';
// /reactors/create-account?csi=cs_test_a1pBB0FI8GUKnWYlCVn0RKUYXV8FRroacXjI5WVhWPlFJilm46lZwdjgac
export default async function Page({ searchParams }) {
const unexpectedError = searchParams['unexpected_error'];
const msg = searchParams['msg'];
const csi = searchParams['csi'];
const session = csi && await stripe.checkout.sessions.retrieve(csi);
const email = (csi && session && session.customer_details.email) || searchParams['email'];
const message = searchParams['message'];
const submitted = email || message;
const valid = submitted && email && message;
let emailSentSuccessfully = false;
if (unexpectedError) {
return (
<>
Unexpected Error sorry about that! Please contact us via <Link href="/contact">Contact</Link> and we will get it figured out!
</>
);
}
return (
<div className="pt-16 pb-12 sm:pb-4 lg:pt-12">
<Container>
<h1 className="text-2xl font-bold leading-7 text-slate-900">
The Reactors - Sign In
</h1>
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
{msg && (
<div className="rounded-md bg-red-50 p-4 mt-8">
<div className="flex">
<div className="flex-shrink-0">
<XCircleIcon className="h-5 w-5 text-red-400" aria-hidden="true" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">There was an error with your submission</h3>
<div className="mt-2 text-sm text-red-700">
{msg}
</div>
</div>
</div>
</div>
)}
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img
className="mx-auto h-12 w-auto"
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
alt="Your Company"
/>
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900">Sign in to your account</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Or{' '}
<Link href='/reactors' className="font-medium text-indigo-600 hover:text-indigo-500">
sign up to become a Reactor!
</Link>
</p>
</div>
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10 bg-gray-50">
<form className="space-y-6" method="POST" action="/api/sign-in">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="email"
autoComplete="email"
defaultValue={email}
required
className="block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
Password
</label>
<div className="mt-1">
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
/>
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<input
id="remember_me"
name="remember_me"
type="checkbox"
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
defaultValue="checked"
/>
<label htmlFor="remember_me" className="ml-2 block text-sm text-gray-900">
Remember me
</label>
</div>
<div className="text-sm">
<a href="#" className="font-medium text-indigo-600 hover:text-indigo-500">
Forgot your password?
</a>
</div>
</div>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Sign in
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</Container>
</div>
);
};

@ -1,32 +1,18 @@
import Link from 'next/link'; import Link from 'next/link';
import { BookOpenIcon, BanknotesIcon, StarIcon } from '@heroicons/react/24/outline' import { BookOpenIcon, BanknotesIcon } from '@heroicons/react/24/outline'
// discord invite link: https://discord.gg/zXYggKUBC2 import { Container } from '@/components/Container';
const features = [ const features = [
// {
// name: 'The Reactors Premium Subscription',
// link: 'https://thereactshow.supercast.com/',
// description:
// 'Your own premium podcast feed with: bonus content, early releases, AMA. The BEST way to support the show!',
// icon: StarIcon,
// highlight: true
// },
{ {
name: 'My Book', name: 'Buy My Book!',
link: '/book', link: '/book',
description: description:
'Foundations of High-Performance React is my take on breaking down how React works internally to help you build better React applications.', 'If you want to learn more in-depth how React work then this is definitely the best way to support the show!',
icon: BookOpenIcon, icon: BookOpenIcon,
}, },
// {
// name: 'Discord Server',
// link: 'https://discord.gg/zXYggKUBC2',
// description:
// 'Connect with the community for free on our Discord Server! Ask questions or just hang out!',
// icon: StarIcon
// },
{ {
name: 'Tip!', name: 'Tip!',
link: 'https://buy.stripe.com/cN25nl5x2fZO4M0cMN', link: 'https://buy.stripe.com/cN25nl5x2fZO4M0cMN',
@ -43,7 +29,12 @@ export const metadata = {
export default async function Page() { export default async function Page() {
return ( return (
<div className="pt-4 pb-12 sm:pb-4 lg:pt-12"> <div className="pt-16 pb-12 sm:pb-4 lg:pt-12">
<Container>
<h1 className="text-2xl font-bold leading-7 text-slate-900">
Support
</h1>
</Container>
<div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100"> <div className="divide-y divide-slate-100 sm:mt-4 lg:mt-8 lg:border-t lg:border-slate-100">
<div className="bg-white py-24 sm:py-32"> <div className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8"> <div className="mx-auto max-w-7xl px-6 lg:px-8">
@ -56,12 +47,10 @@ export default async function Page() {
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-4xl"> <div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-4xl">
<dl className="grid max-w-xl grid-cols-1 gap-y-10 gap-x-8 lg:max-w-none lg:grid-cols-2 lg:gap-y-16"> <dl className="grid max-w-xl grid-cols-1 gap-y-10 gap-x-8 lg:max-w-none lg:grid-cols-2 lg:gap-y-16">
{features.map((feature) => ( {features.map((feature) => (
<div key={feature.name} className={`relative pl-16 p-4 rounded-lg ${feature.highlight ? 'shadow-md border-2 border-green-500' : ''}`}> <div key={feature.name} className="relative pl-16">
<dt className="text-base font-semibold leading-7 text-gray-900"> <dt className="text-base font-semibold leading-7 text-gray-900">
<div className="absolute top-4 left-4 flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-600"> <div className="absolute top-0 left-0 flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-600">
<Link href={feature.link}> <Link href={feature.link}><feature.icon className="h-6 w-6 text-white" aria-hidden="true" /></Link>
<feature.icon className="h-6 w-6 text-white" aria-hidden="true" />
</Link>
</div> </div>
<Link <Link
href={feature.link} href={feature.link}

@ -3,8 +3,6 @@ import './globals.css';
import { AudioPlayer } from '@/components/player/AudioPlayer' import { AudioPlayer } from '@/components/player/AudioPlayer'
import { AudioProvider } from '@/components/AudioProvider' import { AudioProvider } from '@/components/AudioProvider'
import Providers from './providers';
export const metadata = { export const metadata = {
title: { title: {
default: 'The React Show - Weekly React Focused Podcast', default: 'The React Show - Weekly React Focused Podcast',
@ -17,7 +15,7 @@ export default function RootLayout({children}) {
<html lang="en"> <html lang="en">
<body> <body>
<AudioProvider> <AudioProvider>
<Providers>{children}</Providers> {children}
<div className="fixed inset-x-0 bottom-0 z-10 lg:left-112 xl:left-120"> <div className="fixed inset-x-0 bottom-0 z-10 lg:left-112 xl:left-120">
<AudioPlayer /> <AudioPlayer />
</div> </div>

@ -1,8 +0,0 @@
'use client';
import { NProvider } from 'next13-progressbar';
const Providers = ({ children }) => {
return <NProvider color="#fff">{children}</NProvider>;
};
export default Providers;

@ -3,12 +3,12 @@ import CenteredDarkPanel from '@/components/CenteredDarkPanel'
export default function BookPurchaseCTA() { export default function BookPurchaseCTA() {
return ( return (
<> <>
<CenteredDarkPanel title="Foundations of High-Performance React" buttonText="Download" <CenteredDarkPanel title="Foundations of High-Performance React" buttonText="Purchase"
id="first-cta" id="first-cta"
href="/book-success" href="https://buy.stripe.com/bIY6rpgbG3d2ces000"
> >
<p className="mx-auto mt-6 max-w-xl text-lg leading-8 text-gray-300"> <p className="mx-auto mt-6 max-w-xl text-lg leading-8 text-gray-300">
Now Available For Free! $11.99
</p> </p>
<p className="mx-auto mt-6 max-w-xl text-lg leading-8 text-gray-300"> <p className="mx-auto mt-6 max-w-xl text-lg leading-8 text-gray-300">
DRM-free Kindle, ePub, & PDF downloads. DRM-free Kindle, ePub, & PDF downloads.

@ -14,23 +14,20 @@ function EpisodeEntry({ episode }) {
<article <article
aria-labelledby={`episode-${episode.id}-title`} aria-labelledby={`episode-${episode.id}-title`}
className="py-10 sm:py-12" className="py-10 sm:py-12"
data-cy="episode-entry"
> >
<Container> <Container>
<div className="flex flex-col items-start"> <div className="flex flex-col items-start">
<h2 <h2
id={`episode-${episode.id}-title`} id={`episode-${episode.id}-title`}
className="mt-2 text-lg font-bold text-slate-900" className="mt-2 text-lg font-bold text-slate-900"
data-cy="episode-title"
> >
<Link data-cy="show-notes-link" href={`/podcast/${episode?.slug}`}>[{episode.num}] {episode.title}</Link> <Link href={`/podcast/${episode?.slug}`}>[{episode.num}] {episode.title}</Link>
</h2> </h2>
<FormattedDate <FormattedDate
date={date} date={date}
className="order-first font-mono text-sm leading-7 text-slate-500" className="order-first font-mono text-sm leading-7 text-slate-500"
data-cy="episode-date"
/> />
<p className="mt-1 text-base leading-7 text-slate-700" data-cy="episode-description"> <p className="mt-1 text-base leading-7 text-slate-700">
{episode.description} {episode.description}
</p> </p>
<div className="mt-4 flex items-center gap-4"> <div className="mt-4 flex items-center gap-4">
@ -65,7 +62,6 @@ function EpisodeEntry({ episode }) {
href={`/podcast/${episode.slug}#transcript`} href={`/podcast/${episode.slug}#transcript`}
className="flex items-center text-sm font-bold leading-6 text-pink-500 hover:text-pink-700 active:text-pink-900" className="flex items-center text-sm font-bold leading-6 text-pink-500 hover:text-pink-700 active:text-pink-900"
aria-label={`Transcript for episode ${episode.title}`} aria-label={`Transcript for episode ${episode.title}`}
data-cy="transcript-link"
> >
Transcript Transcript
</Link> </Link>

@ -1,108 +1,30 @@
'use client';
import Image from 'next/image';
import { usePathname } from 'next/navigation';
import { Fragment } from 'react'
import { Disclosure, Menu, Transition } from '@headlessui/react'
import { Bars3Icon, UserCircleIcon, XMarkIcon } from '@heroicons/react/24/outline'
import trsLogo from '@/images/trs-logo.svg';
import Link from 'next/link' import Link from 'next/link'
const navigation = [ export default function NavBar({ showPodcast }) {
{ name: 'Contact', href: '/contact-us' },
{ name: 'Book', href: '/book' },
]
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function NavBar() {
const pathname = usePathname();
return ( return (
<Disclosure as="nav" className="bg-gray-800 fixed w-full z-20"> <nav className="bg-gray-800">
{({ open }) => ( <ol className="flex space-x-4 p-4 justify-center">
<> <li>
<div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"> <Link
<div className="relative flex h-16 items-center justify-between"> href="/book"
<div className="absolute inset-y-0 left-0 flex items-center sm:hidden"> className="rounded-md px-3 py-2 text-lg font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
{/* Mobile menu button*/} >Book</Link>
<Disclosure.Button className="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"> </li>
<span className="sr-only">Open main menu</span> <li>
{open ? ( <Link
<XMarkIcon className="block h-6 w-6" aria-hidden="true" /> href="/contact-us"
) : ( className="rounded-md px-3 py-2 text-lg font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
<Bars3Icon className="block h-6 w-6" aria-hidden="true" /> >Contact</Link>
)} </li>
</Disclosure.Button> {showPodcast && (
</div> <li>
<div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start"> <Link
<div className="flex flex-shrink-0 items-center"> href="/"
<Link className="rounded-md px-3 py-2 text-lg font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
className="block lg:hidden" >Podcast</Link>
href="/" </li>
> )}
<Image </ol>
src={trsLogo} </nav>
className="h-8 w-auto lg:hidden"
alt="The React Show TRS logo"
/>
</Link>
<Link
className="hidden lg:block"
href="/"
>
<Image
src={trsLogo}
className="h-8 w-auto lg:block"
alt="The React Show TRS logo"
/>
</Link>
</div>
<div className="hidden sm:ml-6 sm:block">
<div className="flex space-x-4">
{navigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={classNames(
item.href === pathname ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
'rounded-md px-3 py-2 text-sm font-medium'
)}
aria-current={item.href === pathname ? 'page' : undefined}
>
{item.name}
</Link>
))}
</div>
</div>
</div>
</div>
</div>
<Disclosure.Panel className="sm:hidden">
<div className="flex flex-col space-y-4 px-2 pb-3 pt-2">
{navigation.map((item) => (
<Disclosure.Button key={item.name}>
<Link
href={item.href}
className={classNames(
item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
'rounded-md px-3 py-2 text-base font-medium'
)}
aria-current={item.current ? 'page' : undefined}
>
{item.name}
</Link>
</Disclosure.Button>
))}
</div>
</Disclosure.Panel>
</>
)}
</Disclosure>
) )
} }

@ -34,7 +34,6 @@ export function PlayButton({ player, size = 'large' }) {
)} )}
onClick={player.toggle} onClick={player.toggle}
aria-label={player.playing ? 'Pause' : 'Play'} aria-label={player.playing ? 'Pause' : 'Play'}
data-cy="audio-player-button"
> >
<div className="absolute -inset-3 md:hidden" /> <div className="absolute -inset-3 md:hidden" />
{player.playing ? ( {player.playing ? (

@ -1,336 +0,0 @@
1
00:00:02,790 --> 00:00:06,360
Thomas Hintz: Welcome to the React Show!
2
00:00:07,230 --> 00:00:16,020
Brought to you from occupied Miwok territory and some fire, episode 88.
3
00:00:17,820 --> 00:00:39,120
AI, artificial intelligence, is it the absolute best thing since sliced bread? Is it the solution to all of your programming woes? Is it the technological progress that we have been waiting for for decades?
4
00:00:40,500 --> 00:01:48,720
Thank you so much for joining me. i Okay, so the big news, of course, for a while now and technology has been AI. And like, many people, I've been trying it out and learning it. And I've actually been working on a podcast episode, where we take a look at how AI works. And the biggest thing is, you know, how can we actually effectively apply it to what we do, for example, I've been working on like I said, an episode on AI, but specifically, how do we use it within react programming to build better programs, build programs faster, be more efficient, whatever we can do with them, you know, trying to figure that out, and then, you know, coming back, and I want to be able to come back to all of you all and be like, Yeah, this is, given the current state of things, this is the best method for utilizing it. And this is what you can expect, and you know, all that kind of stuff, right?
5
00:01:49,830 --> 00:02:08,850
So I've been working on that, and it's not quite finished yet, but it's gonna be really good. So if you do want that type of thing, definitely subscribe and, you know, leave a comment or whatever it takes these days, or you could be like me, and I don't subscribe too much.
6
00:02:08,850 --> 00:02:25,440
But I just sort of remember the things I like, and I check them every few days. I don't care, that's fine with me. I know, metrics and everything are all about subscriptions. But I don't know, whatever, I don't care, however you want. If you're interested in that, you know, come back check check later, right.
7
00:02:26,160 --> 00:03:24,060
But the, during during this process of building this episode, I keep running into things that are, I would say to call them concerning, I guess would be an understatement. And this is not necessarily this is this is not an episode today on like, oh, the ethics of AI specifically or anything like that. But there's a lot of rhetoric and thinking around AI that is common to new technologies in general. And I think in a lot of very harmful ways. So what I feel like I need to discuss before I can bring you the episode on how to effectively use this new technology is the technology, I guess not the technology itself.
8
00:03:24,060 --> 00:04:02,370
But technology in general. And what happens when there's new technology and the effects of that the word that people will often use with AI or any new technology that comes out is progress or advancement. The narrative that we hear is, you know, oh AI is going to revolutionize such and such industry, it's going to make it so you no longer have to do XYZ manual process AI will take care of it for you. This is progress.
9
00:04:02,670 --> 00:04:44,490
And you know, people are definitely like, oh yeah, this is going to mean a lot of people lose their jobs. And, you know, people are going to have to learn new skills but like, this is the way things go this is we're marching forward, this is progress. Let's you know look at Star Trek or other sci fi things where the AI just takes care of everything for us, right and, and we see that and we're like, oh, yeah, okay, that's that's the next level that's where we're trying to go that would be progress once we achieve that, like it's going to be painful along the way because you know, people's jobs might be replaced like we don't need them anymore because AI takes care of it but it's worth it right it's worth it.
10
00:04:44,490 --> 00:05:04,710
We're making progress because the end result is these amazing AI systems that free us humans up to do whatever you know we really want to do, we can do our and we can spend our time doing what we Want to do because AI is going to take care of everything for us.
11
00:05:05,010 --> 00:05:22,440
And this is not even an argument specific to AI, this is the argument you will often hear for any new technological advancement, you know, talk about computers, cars, whatever it is, this argument always comes up, oh, we have cars.
12
00:05:22,440 --> 00:05:39,870
Now you don't have to spend three days, you know, taking a horse and buggy or a train or whatever to someplace, you don't exactly want to go and then hop on another train. And, you know, you can have your own car and just drive straight there. And this is progress, we are making progress, we have improved the world.
13
00:05:42,060 --> 00:06:08,580
That's it's just, it's just, it's not. It's just not progress. It is something is it's not it's even advancement. I posit that technological changes, they are changes, they are not inherently progress.
14
00:06:08,610 --> 00:06:41,040
And, in fact, it's a lot worse than that, because I know that most people that listen to this podcast live in western so called Western economies, Western societies that are all hyper capitalist. And what you have with new technologies mixed with capitalism is the further accumulation of capital, more profits.
15
00:06:42,240 --> 00:07:37,140
A great way that I think to think of it as technology is, can be a lever, right, and this is not I didn't come up with this, this is very common, right. And you can think of technology as a lever. You know, the, that used to be maybe with agriculture, it took 10 people to farm one acre, doing everything by hand. And then we invent some new tools, some tractors, whatever it might be, and now that not one person can farm that acre. And so they're leveraging the technology, leveraging that technology, it's a lever, right? And what happens if you take a lever and apply it to a machine whose sole goal is to accumulate capital and profits and power for the people in control of that machine?
16
00:07:38,580 --> 00:08:13,290
This is what we've seen with technology, you know, recent technology like computers, the internet, what this does is this concentrates capital, this concentrates wealth, this concentrates power, the people running Google and Microsoft, the leaders, you know, essentially of this new wave of AI. And the executives and the investors in these companies, they're the ones that are going to get more profitable with AI.
17
00:08:14,880 --> 00:09:05,430
As a React engineer, are you going to get more profitable? You know, are you going to make more money? I suspect not. And, you know, maybe, maybe you would, maybe what's really going to happen is AI is going to improve program programmer productivity so much, that we don't need so many programmers, in which case, if you happen to be able to hold on to a job as a programmer, maybe we'll make a little bit more money, because we're concentrating that capital, the elite programmers that exists that can that can wield this AI power, can demand a higher premium and make a little bit more money, it's not going to be a lot because you're not in control of the companies, you're not in control of the capital.
18
00:09:05,670 --> 00:09:49,530
So it's not impossible that some people, some programmers are going to make more because of AI. But the reality is the people that are really going to make more money is anyone that can take the new technology, and leverage it to get more capital and monopolize that capital. And so you what you really have is, anytime you have new technology that comes out, you have in a capitalist system, like most of us live in, listen to this, maybe maybe all of us at this point, you know, capitalism is sort of taking over the globe right? Or at least its effects.
19
00:09:50,890 --> 00:10:21,520
And so the way that I see it is you you have a new technology, whether it's AI or anything else, company Use with capital with power, they're going to see that new technology and go, Hey, if we can monopolize that new technology, this new lever that's longer than the other levers we've had before, we are going to be ridiculously profitable. That's, that's how the system works.
20
00:10:22,660 --> 00:10:54,160
If, if you are, say open AI, and you create a chat, GPT and GPT, one, and two, and three, and four, and you're just lightyears ahead of everyone else, and everyone uses your AI and AI increases the amount that each person can do by some massive factor. So every business and every individual ends up using your product, you can charge whatever the hell you want for that product, right? Like, that's, that's the reality. You've monopolized it.
21
00:10:54,310 --> 00:11:40,900
And as the owners of open AI, and open AI, the fact that open in is ridiculous at this point, it's just another company, it's not anything special, some open thing, right? It's just a company, with investors, including one of the biggest ones being Microsoft, they are seeing this as, hey, let's control this lever, if we monopolize this lever, and make it so nobody can compete with us, push everyone else out of the market or buy them. That's the way it goes with tech, right? We can charge whatever we want. And not only that, but we can essentially control the world. Not only do we actually control the software, but even bigger, we control the capital behind that software behind that technology.
22
00:11:40,960 --> 00:12:14,710
So I've jumped ahead and said some things without backing them up. But we are going to cover it. So why do I say that AI or pretty much any technology is not inherently progress is not advancement. And if you listen to this podcast, I'm sure you know, I love to dig into the root of things, the mechanics of things, start with the foundation, and we work backwards from there.
23
00:12:14,710 --> 00:12:50,500
So when we talk about the design of a React program, I look at it as okay, what is the ideal? Like if we were to build the perfect react program? Let's imagine that, you know, we're not restricting ourselves with a budget or, or any other constraints, we just build the best react program. Okay, we know what that looks like, like, once we've defined that we can work backwards from be like, Okay, how do we get to there? What are the most efficient ways to get there? What's the best way to get there? I do the same with humans with society too.
24
00:12:50,920 --> 00:13:45,970
So to me, I am like, Okay, let's look at humans. What? What do humans want? What is the ideal situation for humans? And I'm not going to claim that I have all the answers here. Certainly don't. But I do know some things for sure. And I think most people would agree on these things. So when I look at humans, and what I'm looking at is what what makes us as humans, happy, what makes us satisfied, what makes us fulfilled? What makes us feel like life is worth living, you know, these really, really deep existential questions that you could probably research for an entire lifetime and not fully solve it right.
25
00:13:47,920 --> 00:14:29,620
Even with that being said, there are I believe, things that we can generally agree on. And so the place that I usually start with is human interactions. Deep real, connecting friendships. I, you know, when you talk to people, when I think about myself, that is one of the most fulfilling, satisfying things that makes me and anyone else happy like that, um, the amazing friendships that you have the experiences you can have with other people. That is definitely one of the most important things when it comes to answering these, you know, life questions, right?
26
00:14:30,460 --> 00:15:35,710
Another one would be autonomy, individual or collective autonomy. It's the idea that nobody wants somebody in power over them, telling them what to do. are, you know, this is pretty counter to how many of us exist in society, but this is also like, really well known and really common like, you complain about your boss you complain about the guy ever meant to complain about basically anyone that is put into a position over have authority over you, making you do things you don't want to do or you don't think are right, or you don't think is the best way to do things. So another, you know, aspect to what I look at, as you know, the ideal for humans is a society in which all of us have our own individual autonomy and power, and nobody has authority or power over us.
27
00:15:36,850 --> 00:16:16,510
This doesn't necessarily mean like a society where we all just run around doing whatever we want. This is more like, in the ideal society, all of us feel empowered to work within society and have influence over society and most importantly, influence over our own lives, where we can make our own choices about what we want to do with our lives. This is also I believe, central to what makes people feel happy and fulfilled and satisfied, and all the other things that we want as humans.
28
00:16:17,410 --> 00:16:55,420
So when we're talking through the rest of the points I want to talk through in regards to technology and AI and progress. The place I'm coming from, is imagining this ideal world in which humans, which me and you and everyone else are feeling these good things were like, Yeah, my life is great. I love living my life. I love waking up every morning, you know, that's not always perfect. Sometimes I have conflict with, you know, my best friend, and we have to resolve it, whatever. It's not perfect, but like, life just feels full and worth living.
29
00:16:56,290 --> 00:18:09,760
And so I am, I don't know, I'm not claiming I know, exactly. You know how to achieve that, or whatever. But I think we can all look at this and agree that there are some things we can know, especially as this in this whole idea interacts with technology, and AI. So backing up some more. I think the other part of what we need to take a look at is what what is technology? What is progress? What is advancement, and we've talked about it a bit in terms of being a lever. But I want to really dig into the idea of progress and advancement. And for the first example, I want to go back in time to before the washing machine was invented. So before then, people washed their clothes, by hand, it was a very manual process, often primarily done by women--arduous, like, you know, it took a long time, right?
30
00:18:10,270 --> 00:18:54,250
I don't know if you've ever actually tried to wash clothes by hand and do it effectively. I have tried it quite a few times. And it's not, you know, it's a lot easier to throw stuff in a washing machine, right. So let's say we go back in time, and you are somebody that's had to manually wash clothes your whole life, and then the washing machine is invented. And people are like, wow, this is amazing. This, look at the progress we're making. You no longer have to spend hours cleaning, and and scrubbing this, the clothes by hand. You know, you can just throw it in this machine, and it will do it for you.
31
00:18:54,670 --> 00:19:29,500
And so everyone's like proclaiming the progress that we've made, we are saving people so many hours every year, they no longer have to wash their clothes, they no longer have to do this manual labor. It's so much better. You can sit and read a newspaper. You know, while you know before you would have had to spend those hours washing clothes. Now you can just sit and read a newspaper right? You can relax you can do your hobbies, you can spend your time doing more fulfilling things. Right. We've made progress, right.
32
00:19:29,000 --> 00:20:11,330
This is this is the whole point we have freed up time for people we've we've freed them from manual labor, we've made progress. Well, let's go back in time and look at that. What did that really happen? When we invented washing machines? Does that mean that you know people used to work 60 hours a week and then they got a washing machine and now they only have to work 50 hours a week is that what happened? Everybody reduced the amount had to work by 10 hours a week. Does that really happen? Did every technological advancement that we ever had just keep reducing the amount of work that we actually have to do?
33
00:20:12,080 --> 00:20:51,050
Maybe for a very small portion of people, maybe you could say that's true, but we need to look at us as a society as a whole. Absolutely not like, think of all the like, in my lifetime, which is not, you know, there's a lot of people have lived a lot longer than me. And so even in my not super long lifetime, there's been a lot of technological advancements that, you know, people call them advancements, right, like, smartphones, cell phones, when I was a kid, we used landlines, you know, like, we typed in numbers and call our friends and, you know, it was attached to the wall, right.
34
00:20:51,050 --> 00:21:55,790
And now, we have computers in our pockets, right like that. That should have meant, oh, you know, as a worker, this would save me a lot of time, I have a calculator in my pocket, right. So I used to, you know, like, did my dad who was working out that that time, you know, he did a 40 hour workweek did the advancement of a smartphone mean? Oh, now he only has to do a 30 hour workweek. Or maybe he still has to do a 40 hour workweek. But did he get a massive bump in his pay? Did his employer say, wow, look at this, we have better computers, with phones in our pocket, you can travel around and do your meetings, on your way to visiting a supplier. This saves us time, this saves us money. And we're going to give that back to you. So did my dad then make twice what he used to make? No, in fact, it that's not the effect we had at all getting, getting a washing machine getting computers getting smartphones, it changed the society that we live in.
35
00:21:55,910 --> 00:22:23,390
But it didn't, it didn't mean that oh, now I only have to work. Like if you look at all the technological achievements we've had in the last even 50 years, if the whole idea was that this is progress, that this frees us up to work on what we really want to work on and to do our hobbies to hang out with our friends to do the things that make us feel fulfilled. Did did these technological improvements actually do that?
36
00:22:23,900 --> 00:23:17,690
No, absolutely not. In fact, it's probably done the opposite for a lot of people, a lot of people, you know, you're working multiple minimum wage jobs, you don't even have time to look at chat GPT or AIs. I mean, you barely have time to eat and sleep. Right? I just, I think when you look at this, this reality, I don't think you can look at it and say this is inherently progress. This is inherently advancement. Because, again, going back to what actually makes us happy and fulfilled as humans. It isn't like improving that part of the equation. It's not it's just not. And I think it makes sense.
37
00:23:17,750 --> 00:23:45,890
So I think what we what we can look at next is like, what is what is technological improvements? What is this new AI actually going to do? What effect is it actually going to have on society to take a look at the effect that technology is going, you know, change, technological change is going to have on society, we have to talk more about the system in which we live in and that is capitalism.
38
00:23:46,160 --> 00:24:14,600
Capitalism dictates basically everything about our lives. So we have to start there. Like when we're breaking things down and trying to get to the root of things, we just have to do it. And so what what is, you know, capitalism, what is it? Well, and how, you know, in a sense, that is related to what we're talking about? Well, it is the accumulation of capital, it's in the name.
39
00:24:15,830 --> 00:24:56,570
The if you control capital, you control the means of production, you, you control profits, and you know, by control that might mean you're an investor in mutual fund that mean, might mean you're the boss that might mean many different things. But if you are the one that has control over that capital and where that capital gets allocated, whether that capital is money, or labor, you know, people that work for you, or whatever it happens to be if you are in control of that capital, what is your goal in a capitalist society?
40
00:24:56,570 --> 00:25:40,310
How do you like what do you what are you incentivized to do? Do well, I'll give you an example. I look at the advancement of advancement, as people might say of AI, right? So I'm like, Okay, I'm a programmer. I love programming. I really do. And I, I look at this, and I'm like, okay, so what if I just don't learn how to use API's? What if I'm like, Hey, I love programming as it is, it's great, I enjoy it, why I don't, you don't feel any particular need to learn this API, right? Let's say I saw, I don't learn this AI.
41
00:25:40,340 --> 00:26:27,800
But let's say the AI technology improves programmer productivity by some factor, one, like one to 10, whatever the factor is, I guess, if it improves it by a factor of one that's not really much different. But let's say it, like has some significant improvement on our productivity as a programmer, and I don't learn this new technology. And a few years down the road, I'm like, oh, yeah, I love doing the podcast and all but you know, I'd love to get a job, you know, writing code for someone, or more likely, oh, hey, I ran out of money, I need some money, I'm gonna go get a job. And so I started applying and, and people look at me, and they're like, Wow, you are like a really slow ancient programmer, like, we don't want you.
42
00:26:29,030 --> 00:26:52,370
And that's because I didn't learn this new technology. And so I am no longer competitive with other programmers. I mean, I'm not know if this is what's going to happen with AI. But it could, right if, if AI makes programmers way more productive, and you don't use that new technology, you're kind of getting left behind, you might not be able to get a job as a programmer anymore.
43
00:26:53,270 --> 00:27:35,330
So I think you can look at this though, and apply it in general to businesses and capital. So running a business, you're seeing AI in your life, like maybe you're not Microsoft or Google, you're not the ones creating the AI. But you see it and you're like, oh, okay, if my competitor, or a new competitor that, you know, springs up because of the invention of AI, uses it and and allows them to do what I'm doing for cheaper than they can sell their product for cheaper their service for cheaper, and I will no longer be competitive, and I will go out of business, I will no longer exist as a business.
44
00:27:36,320 --> 00:28:08,390
This is the reality, this is how capitalism works. So as a business, what you have to do, if you want to, like continue to exist as a business, you either have to stamp out all the competition, make it so nobody can use this new technology, that's an option. If you have a monopoly and you're like, oh, no, I don't feel like adopting this new technology, I'm just going to pass laws that make it illegal for people to know I'm going to lobby people make it impossible to use this new technology, or I'm going to buy up anyone that tries to use it.
45
00:28:08,420 --> 00:28:39,920
I mean, there's a lot of different ways you could go about not using the new technology if you're in control of all the capital. But let's say you're not, let's say you have a few competitors, you have no other option other than to adopt to this new technology. It's just if you don't your business is gonna go out of business. And if you want to remain in business and keep getting a paycheck and keep eating food, what do you do you have to play this game, this is the system this is how the system works.
46
00:28:40,550 --> 00:29:17,690
So we can take this now and go back in and start analyzing our actual the current situation with AI. So we have we have on the one side we have humans and and what us as humans need what we want, what makes us happy and satisfied and fulfilled. Right? And we can all I think at this point agree. Just a thing. A technology doesn't long term make anyone happy. I love programming, right?
47
00:29:17,870 --> 00:30:12,020
I absolutely love programming and, but like if it was a vacuum, and I was the only person that existed and I was the only person that existed in the world. Like and you gave me a computer and you're like, hey, I guess you wouldn't because I'm the only person somehow I get I make a computer right and I can program things. If I'm just programming for myself that's fun for a while but like the real joy in building things and making things is the impact it can have on other people. I love to build programs because I love to be like oh wow look. This person enjoys using my program they They, they get some sort of benefit from it, right? That's sort of on a micro level. That's why I love it. It's it's the connection to other humans.
48
00:30:12,440 --> 00:30:53,510
But you take away that connection and the technology, it might be interesting for a little bit because it's fun to learn new things, right? But, I mean, I don't like if it's just me in a computer on an island or whatever, for 30 years, like, I'm not gonna just program every day for myself, that's just not I don't think anybody would. So we, I think we can agree that technology, by itself, is not what makes life worth living for humans. It's just not it is not that important. And then we can look at the the capitalist system side of things.
49
00:30:53,540 --> 00:32:01,490
So the whole drive in capitalism is to accumulate capital, and monopolize industries, because that's the way you accumulate the most capital the most profit. Now, let's take a look at the the AI lever, the AI technology, maybe this, you know, is allows your business to do more with less capital. Maybe it's a huge lever that allows your business to do way more with less capital. And you have then companies like Microsoft, open AI, Google, whoever is making these AIs, right? They're looking at this, and they're going, Wow, if we control this technology, if we monopolize this technology, or even just get a large slice of it, we are going to be fabulously wealthy. And is there any other like, there's nothing else in capitalism, that's like, okay, yes, that's true.
50
00:32:01,520 --> 00:32:39,380
But you need to be careful. Because if, if you do something harmful to humans, or you don't make humans lives better, or you exploit people, or you exploit the planet, or you pollute the planet, or whatever it might be, if the if there are side effects to this new technology, you're going to pay the consequences is that that's not how it works like Google, you know, if they're creating a new AI, there, the way that it works is if they succeed, and people love using their product and or at least feel like they have to use their product.
51
00:32:39,380 --> 00:33:19,130
Maybe they don't love using it, but they're like after use it to remain competitive, and they pay Google money and Google makes money from it. Google doesn't care what the side effects are, if there are major side effects, oh, no, a bank collapses, what's going to happen? The government's going to bail them out, oh, we're destroying the planet because we use so much energy. It causes climate change. Oh, wait, you know, Google's not responsible, or the oil companies aren't responsible for all the nasty side effects they've caused? No. us as individuals, and as the government? Oh, we're the ones that ended up holding that bill. Right.
52
00:33:19,550 --> 00:34:20,630
So just the nature of the systems we are in? What does it do? It encourages the people with capital, the businesses or people that want capital startups to focus solely on capturing market share. Without worrying about the side effects, like all that matters is capturing market share, you will look at a great example be open AI lets us use chat GPT for free, even though it's they've said it you know, it's very expensive to create their models and all of the research they did and run all the software, the services, they let us use it for free. Why? So they can capture market share. That's it maybe so they can also do more research or whatever you research on us were the guinea pigs, right. Yeah, there's other things too, but their ultimate goal is captured the market share.
53
00:34:20,660 --> 00:34:57,470
They want to be the one that provides AI to everyone. And, yes, there might be people there that are like, oh, we need to do this ethically or whatever. But when push comes to shove, that is not as important as being the first to market capturing that market, capturing that capital being the monopoly over AI. That is the driving underlying force. There's no driving underlying force that's like, Okay, how do we use this technology to actually make human life better?
54
00:34:59,000 --> 00:35:31,310
There's that's just not a part of this system. So let's go back again and take a look at things from the ideal perspective. So in what I think would be an end, I think a lot of people would agree an ideal way to introduce new technology would be to look at, like, how this new technology is actually going to make people happier and more fulfilled, more satisfied all those other things. Right.
55
00:35:31,310 --> 00:36:15,920
So let's say we have AI, the the way that I think, ideally, as a society we introduce it is all right, like, before, we just open it up and let everyone start using it. Like, let's take a look at the impact it could have and, you know, make decisions, oh, is the potential impact actually going to make things better? How are they what changes? Could it have? What can we do to mitigate any potential side effects? Any potential negative externalities? That would be I think what most people would say, is an ideal way to approach any new technology.
56
00:36:16,970 --> 00:36:51,950
Because it's not necessarily like, like we established earlier, it's not inherently better. New technology does not inherently make us happier, humans make us make our lives better. So given that premise, it only makes sense that in an ideal world we look at okay, like, before we actually do this, let's do it. Right. Right. You know, let's, let's actually figure out how to do this, right. So it actually makes things better. That's the ideal scenario, right?
57
00:36:52,610 --> 00:37:21,140
That's absolutely not how new technology plays out. That's not how AI is playing out, as we all know, or maybe you don't, if you're not paying attention, which is fine. But the way it's playing out is the land grab everyone who's like, oh, I need to get as much AI market share as I can as quickly as I can. And I'm going to say things like we care about ethics, we're going to do a little bit of work to make sure our AI doesn't say something that gets us a lot of bad press, right.
58
00:37:22,160 --> 00:38:00,470
But there's not a, oh, let's take a step back before we release this and actually make sure it makes people's lives better. Instead, let's just go along with this narrative, that technology inherently makes our lives better. It's just progress. Its advancement, that that is profitable for us. So let's go along with that. So if you hear that, you know, if you even think that if you think, oh, technology is progress, if you equate those things, I would challenge you to take a look at where that narrative is coming from.
59
00:38:00,830 --> 00:38:53,240
Because I suspect it's coming from the people that are going to profit from those technologies, the love that argument, regardless of whether it's true or not, because with that argument, they don't need to deal with potential negative side effects, because people will just go, they'll defend them, they'll go, Yeah, you know, there might be some negative things, but we'll figure it out. You know, it's just important to make progress. Making progress is the ultimate goal. Again, like I don't cares about whatever you call as progress, that's not progress. What matters is making our lives better as humans, like I don't, I don't give a about AI. If it doesn't make our lives better. That's, that's what it comes down to.
60
00:38:53,450 --> 00:39:38,030
But that's not how any of any logical company is going to behave. Because that's not the system we exist in. So yes, I am exploring AI, I'm exploring this new technology. And my you know, goal in this podcast is to educate and help people learn how to effectively use technology, whether that's react, software programming, or AI is whatever that happens to be. You know, I take it on myself to come to all of you and be like, I want to give you the best possible education and information I can so that you can effectively utilize these technologies.
61
00:39:38,690 --> 00:40:52,280
But at the same time, I don't see it as, you know, inherent progress, and I can't, you know, I feel like I can't necessarily make this episode on how to effectively use AI to be a more efficient programmer. Without just coming out and saying, I don't necessarily think that the way in which this is happening, the order in which things are happening, the speed in which things are happening, the way in which things are happening when it comes to AI and the development of AI is inherently good or inherently progress. That's just not how I feel. And so I, that needs to be said, because I think, if it's not said, it's easy to imagine that just because it makes you maybe it makes you a more efficient react programmer, it's easy to be like, Oh, this, this is making programming better. This allows us to create more programs faster, and just think in your head, oh, this is better.
62
00:40:52,820 --> 00:41:33,080
But I'm just gonna come out right now, I'd say, maybe not. Maybe it does. Maybe it doesn't. But it's not inherent, it's not a given. It's not like just a guaranteed progress or advancement. So then you might ask, Well, why are you? Why are you making? Why would you make that educational material anyways? Well, that's the other side of the equation. You know, you're not hearing me be like, you know, talking, you know, this podcast isn't about like, oh, you know, how do we have a different society than capitalism or whatever, that's just not what this is.
63
00:41:33,470 --> 00:42:03,440
This, to me is, we exist. Within a capitalist system, we exist within a lot of different systems. And especially as you know, people not at the top, they're not the people controlling the capital. We have to exist in these systems, we have to earn a living, we have to, like, like, you got to pay for that washing machine, right? You know, going way back.
64
00:42:04,580 --> 00:42:21,350
How, you know, before the washing machine, you just needed I guess, some water and some soap, maybe, and a few other basic things, you didn't need energy. You didn't need to buy a new washing machine every 10 years, or whatever it might be, you didn't need to repair the washing machine.
65
00:42:21,680 --> 00:43:14,720
But now that you have a washing machine, you're not like just using that time to go, you know, read a newspaper, no, you have to get a job. So you can earn money, so you can pay for that washing machine pay for the energy that washing machine needs. So it's just not it's not it didn't really make our lives better. And I look at this, maybe in an extreme way, but I go way back, I'm like, Did did agriculture even make our lives better? In a sense of making us happy and more fulfilled? You know, maybe before agriculture, people had more fulfilled lives, happier lives? I'm not right now saying, I know that for sure anything, I'm just saying, like, when we like, there's nothing that prevents us from imagining that maybe that's possible.
66
00:43:16,100 --> 00:44:25,190
And, like, you know, just just being like, we shouldn't just take new technological changes, and assume they are either better now, or they will eventually make the world better, because I think what we've seen over and over and over again, is that that's just not true. Like if if technology really was our Savior, if it really was progress. Why if we've made such monumental technological improvements over the last whatever, 10,000 years, right, you go improvements, right? Like, like, why twice? Why? Why is a job still 40 hours a week, like, all these accumulated technical improvements, you would think that at this point, we should all be working like two minutes a week, and the rest of our time is our free time to live a happy, autonomous life doing what we want to do. That's not what's happened.
67
00:44:25,940 --> 00:45:02,930
So I guess, you know, the very the main point of all this is, yes, I am going to, you know, work to bring you all the best information on how to use whatever technologies are available effectively. But that doesn't mean that these technologies are progress. That doesn't mean they're inherently good, doesn't mean they're inherently better. It just means we exist in this system. We need to Do what the system requires.
68
00:45:03,080 --> 00:45:30,320
And if you're a programmer, that means programming. So let's do the best we can at that, right? That's, that's what, that's what this is really all about. To me, at least I want to provide people with the best resources, the best tools to do the best they can at what they need to do within society. And, you know, maybe you're just programmed for fun. And that's awesome. That's the best.
69
00:45:30,620 --> 00:53:13,490
And in that case, just you don't you don't even listen to the episode on how to use AI unless you're you want to, because nobody cares. Like, if you're just doing it for fun, who cares? Whether you use some new technology, who cares what programming language you use? Who cares? It doesn't matter? Do what you want to do? What makes you happy? What makes you feel fulfilled? And, you know, this touches on another huge, huge aspect of new technological changes? What if I don't want to learn how to use AI? What if I die? Or what if it's, um, you know, as people age, it gets harder to learn new things.
70
00:46:12,630 --> 00:46:56,790
What if I'm like, I really love programming the way I already do it, and I'm just not interested in this new thing. I mean, if, if this is your job, you don't really have a choice, you potentially, like you might not have a choice. If you want to keep your job, you might have to learn how to use this new technology that is taking autonomy away, and I think, from us, and I think that makes us less happy. It's like, you know, it's like the sort of grumpy thing like, Oh, I gotta learn this, because otherwise I'm going to be left behind. And so technological change, and you hear, you know, this a big part of the topic around AI is like, which jobs are going to be replaced?
71
00:46:56,790 --> 00:47:39,180
Like, I don't know, to me, it's, it's absurd. It's like, Why? Why do we want that? Like, why? Why as a society, is that a thing? Why aren't we like, oh, okay, AI could replace these jobs. But the amount of money that businesses make, shouldn't change, right? So maybe, if your job gets replaced by AI, you just get free income, like, the income was going to that profits, that revenue is going to the company, regardless of whether you are doing the labor, or the AI is doing the labor.
72
00:47:39,180 --> 00:48:32,460
So as a society, why are we not like, oh, okay, if technology replaces a job, well, then that revenue should go to the person that no longer has to do that job. Because oh, what would that do that would free up that person to, you know, do whatever they actually want to do. And if you took that approach to society, where every technological change, just freed us up to do what we wanted to do, that would potentially actually be progress. But if, if your technological change just means somebody no longer gets a paycheck, and they can no longer buy food, or they must re educate themselves? Well, that sucks. Like, like, why would we want to exist in a society where that's the way things are?
73
00:48:32,460 --> 00:49:33,030
Anyways, we could go way down that path, but the main point is just like, again, it's not inherently progress. It does not necessarily make things better. Maybe it does, maybe it doesn't, but the systems we exist in, they're not incentivized to figure out answers to these questions before deploying the technology. They're just not, that's not the way things work. The world that we are going forward into with AI is one where we haven't answered any of these questions about its impact on society or us as humans. We're just gonna deploy it anyway. We're gonna use it anyways. It's, you know, and it's not something you can really stop either, like, the government can't come out and say, oh, you can't use AI, like, we need to pause AI before.
74
00:49:33,030 --> 00:50:03,870
You know, we start deploying it. I mean, you could try to do that. But the reality is this technology, that we live under capitalism, so if somebody can use the technology secretly or whatever, or go to a different country that doesn't have as much control over what people do, and use this technology, they're going to accumulate more capital and more profits and it's just there's the system the capitalists have system is going to do what it's going to do.
75
00:50:03,870 --> 00:50:52,410
And we can't. Like, there's nothing we can do about that if we're existing within this system, that's just the way it is. So yeah, the world we're going forward into, like with all other technological change is one where change is going to happen in it's going to be painful, people are going to lose their jobs, or they're going to have to re educate themselves when maybe they don't want to. And there might be a lot of other negative side effects. But we are not going to have the opportunity to look at those first before we deploy the technology. So, yes, I'm going to make educational resources on how to use the technology.
76
00:50:52,410 --> 00:51:40,920
But I implore you, if you're the one using the technology, think about its impact. Think about, you know, how it impacts society, how it impacts you how it impacts the people around you. And I don't know if there's much we can do about it, you know, you need to earn a paycheck, it's just the way it is. There's only so much you can do as an individual. But at least we can come at this, you know, from a perspective of, yeah, we're using this technology, but we're also acknowledging it might. It's just technology, it's not necessarily better, it might make the world worse, it might.
77
00:51:40,920 --> 00:52:03,570
But at least we can acknowledge that that's that's where we have to start with, we have to start with acknowledging reality and acknowledging the truth. If we do that, we can move forward into making decisions on how to use that technology, what to do with that technology. But we have to start there, we have to start with what it is we're actually dealing with, and the potential effects of it.
78
00:52:03,570 --> 00:52:20,760
Well, I I've said a lot, I have no idea if it's interesting, or I repeated myself too many times. But something I personally needed to say before we get into actually using the new AI technology.
79
00:52:20,760 --> 00:53:13,500
But I absolutely want to hear from all of you. How do you feel about it? You know, on the one hand, I'm like, Oh, this is exciting, like working with Chat GPT. It's been really interesting. It's like, wow, this is really impressive. How does it do this? It's fun. I'm enjoying learning about it, and learning how to use it and learning what it's capable of. So that is makes me happy and fulfilled going back to all this right? That's reality, too. So yeah, I'd love to hear, you know, from from all of you out there, like, what do you think about it? Are you excited for it? Have you looked into it? What do you feel like? Or are you like, Oh, I am dreading having to learn this new thing. Or in one of the feelings I've had is, I don't know if I enjoy this as much as programming without the AI.
80
00:53:15,420 --> 00:53:57,240
It's another one of those things where it's like, I can see how maybe it's making me more able to make programs quicker. But am I enjoying the act of programming as much as I used to? I think that's still somewhat of an open question for me. But I would love to hear from you. If you've tried it, how do you feel about it? And just how do you feel about the whole situation as a whole? You know, how do you feel about having to exist within this system that forces you to adopt new technologies, whether you want to or not? Anyways, I would really love to hear from you.
81
00:53:57,240 --> 00:54:33,210
Definitely, you know, if you want right into the show, you can find the contact form on the website, you can leave a comment on whatever platform you listen to. I love hearing from people it it's what makes this podcast in this community what it is, you know, I guess that's the central thesis of this entire episode, right? It's about it's about people, it's about the relationships. So I would love to hear from you I will always do my best to respond in whichever way I can I even have a conversation.
82
00:54:33,210 --> 00:54:56,730
You, the people listening are what makes this whole thing worth doing. And so yeah, I'd absolutely love to hear from you. I hear from listeners all the time and it always makes my day even if it's just a hey, I enjoy this or I didn't enjoy this or I didn't like this or I don't agree with you or I agree with you or how do I do this?
83
00:54:56,730 --> 00:55:04,080
Whatever it is, it's fine. I just I love to have that connection and make that connection with people.
84
00:55:04,080 --> 00:55:23,400
And yeah, like always. Thank you so, so much for joining the show today and I hope you have a good rest of your day or week or whatever it might be. And yeah, take care. Bye.

@ -1,516 +0,0 @@
1
00:00:01,620 --> 00:00:05,190
Thomas Hintz: Welcome to The React Show!
2
00:00:05,730 --> 00:00:16,650
Brought to you from occupied Miwok territory by me, your host, Thomas, and a second brain, Episode 89!
3
00:00:17,550 --> 00:00:43,110
So what actually is chat GPT? And all of the new AI tools? How do they work? In this episode, we answer those questions and learn practical tips for utilizing them while doing react programming based on my research and experience. We also do some trivia and and cover the latest react news. It's going to be a lot of fun. Come join us.
4
00:00:45,750 --> 00:01:14,280
Thank you for joining me, it's a beautiful sunny day outside and I'm itching to go out and enjoy it. But also eager to share what I've learned about AI and programming too. And that, of course, comes first. I don't know why, of course, but that's how I'm choosing to do it. You know what I think, hey, the sun will, you know, rise another day, just like it's rising on a new segment, we have trivia.
5
00:01:15,120 --> 00:01:28,650
So it's Trivia Time. I thought we'd have a bit of fun to start with and and see how many clues you need before you can guess the answers. All right.
6
00:01:28,680 --> 00:01:50,130
So the first one is, in what year? Did I write my first lines of code? And what language did I write them in? I'll just start out give you a few seconds to mull it over. If you're a longtime listener of the show you you might already know the answer.
7
00:01:50,130 --> 00:02:15,210
But if not, we'll start with one clue. And that is one the year that I wrote my first lines of code was one year before Firefox, which was originally named Phoenix Phoenix was the name of it when I first started using it was created. So I think this was like three years before Phoenix was released two or three years before that.
8
00:02:16,290 --> 00:03:09,600
So that's your first clue. If you're not super super up on Firefox, Phoenix history, I got another one for you, though. So at the time that I was, I wrote my first lines of code. I lived at my parents house, and I accessed the internet via AOL dial up American online. You know, those those discs, those compact discs CDs, you know, that we used to have all over we used them as Frisbees, and I don't know We experimented with putting them in the microwave. That was always fun. So yeah, that's actually how I got on the internet. And it's actually how I first learned to program was by researching things via dial up internet. So it might give you maybe the range and might help out.
9
00:03:10,350 --> 00:03:29,760
And I'm not big on I don't know a whole lot about like movies and popular culture as much as I sometimes try. I'm really not very good at it. But I looked it up on the internet told me that this really famous movie, known as Legally Blonde came out the same year.
10
00:03:30,270 --> 00:04:16,110
And yeah, so I don't know, let's see, how are we doing? What are we thinking? Have we narrowed it down a bit? So I'll tell you another thing. So the this code that I was writing, I was writing it in Windows notepad. And the version of Windows that I was using was called Windows ME, or I think it was short for Millennium or something. It was basically the worst operating system ever. It was like a much worse version of Windows 98. Crashed all the time had so many issues. But yeah, that's what I started out was using Windows notepad.
11
00:04:16,140 --> 00:04:35,490
So that might also give you some clues into what language it was. And just a few other clues to finish it off. So in this year, BitTorrent was launched as well as Mac OS X. And this is when the X Xbox initially came out.
12
00:04:35,520 --> 00:04:53,430
I you know, didn't get one of those but I think I had a friend that got one. And I was just like, why would you get that Microsoft's terrible and they're never going to succeed. Of course, x Xbox has been a massive success and I was definitely wrong as a child at that point.
13
00:04:53,850 --> 00:05:20,700
Yeah, so the answer is 2001. That was the year that I wrote my first lines of code. And I the code that I wrote was HTML. And yes, I do consider that a programming language. But yeah, so I don't know. Did you get it? Right? How did that go? I really not great at trivia.
14
00:05:20,700 --> 00:05:40,410
But I, I, the funny thing is like, I wonder if I would have gotten no, right. I can't really find out those. It's, I'm the one that's presented it and wrote it. But the next one is, the next trivia question we have is more react specific. And this one was kind of interesting to me.
15
00:05:40,410 --> 00:06:11,040
So the question is, how many lines of code is react, and I'm specifically talking about all of React. So if you go to the React project, I've like a source folder, all of the actual source code within the source folder, not just the main packages that you import when you're using React, but all of the supporting code. So how many lines of code do you think it is?
16
00:06:12,540 --> 00:06:52,170
I think when I before I actually looked this up, I actually got the source code and analyzed it and stuff. And before I did this, it was one of those things where I'm like, it's probably a lot, but maybe not, you know, it's not gonna be like as much as an operating system or something, right. But in my head, I'm like, this is a really big mature project. It's gotta be like tons and tons of lines of code and everything that you look at online when it or not everything, but a lot of things. You look up online, it's like oh React is big. And it adds a lot of overhead and all this. And so you're thinking, Oh, it's gotta be a lot of lines of code. Right?
17
00:06:53,040 --> 00:07:22,650
So I was kind of surprised and kind of not surprised about the results. I'll start. Yeah, I don't know if you have any ideas yet. But you know, start thinking it through. The first clue is that the number of lines of code is less than half the lines of code in VS code probably doesn't help you too much, unless you know, how many lines of code is in VS code, but maybe it helps scale things.
18
00:07:23,590 --> 00:07:49,420
The next thing should help more. And that is the React package itself. So like importing react into your project that that package is 13,000 lines of code. There's, of course, a bunch more packages for react like React DOM and lots of other things. But just that package alone is 13,000 lines of code.
19
00:07:49,780 --> 00:08:35,020
The next clue is that the number of lines of code is 1/10, the number of lines of code of the back end for a Java based web application I worked on in 2010 at a startup. So I remember way back then we were like, Oh, how many lines of code is the back end? And it was a lot, a lot more than I thought. The funny thing is this, that was 10 times the amount of code as the entire react project. So that was surprising to me. All right, it probably doesn't help you too much.
20
00:08:36,460 --> 00:09:22,180
This next one, I don't know, maybe it helps. But it was also kind of surprising to me. The lines of code and React is slightly less than the number of lines of code that powers the software for the space shuttle. So I don't know that's kind of wild to think about that the number of lines of code in reactors is even, like, like, like you'd think software powering a spaceship essentially would just be way more. But it also might just show how much things have changed since we started writing code back when the code was written for the space shuttle, right? Yeah, so it's a little bit less than lines of code for the space shuttle space shuttle is a little bit more.
21
00:09:22,870 --> 00:09:55,180
The next clue is the React DOM package is 69,000 lines of code. And of course, this is not minimum, minimized-minified it's not, you know, hasn't had processing done on it. So it's not it's doesn't necessarily mean like this is how heavy the package is when you include it on your page or anything like that. It's just the actual human readable source code is that many lines of code.
22
00:09:55,840 --> 00:10:15,220
So now we know the React package is 13,000 lines of code. The react DOM package is 69,000 lines of code. And I'll tell you that overall, the source code for React includes quite a lot of packages, I'll just name a few more to help you get in the ballpark.
23
00:10:15,220 --> 00:10:39,190
So there's the DOM event testing library, react dev tools, and actually quite a few projects labeled experimental. And so you might not be as familiar with them, but it seems like they're kind of like, smaller ish versions of some of the standard react stuff that they're trying out. Right. All right.
24
00:10:39,520 --> 00:11:09,580
So I'll give you a few more seconds here to make your final guess. And all right, that, you know, got some music going or something, right. Okay. All right, fully combined, all of the React packages in the source directory, total 310,000 lines of code. And again, the React and react DOM package that's like, around 80,000 lines of code, but in total about 310.
25
00:11:09,610 --> 00:11:29,860
And I think when I first saw that, I was kind of like, wow, that's actually less than I was expecting. Like, I thought with all the supporting code, you know, dev tools, and in all the tools and stuff related to react, I actually thought it would be more. So yeah, it's 310,000 lines of code.
26
00:11:29,860 --> 00:11:52,870
The Space Shuttle was 400,000 lines of code. vs. Code, at least I don't know, from some point, when I looked it up was almost 900,000 lines of code, which is actually way less than I expected. And the that Java backend I was talking about when we measured it, I guess 13 years ago, or whatever now for the startup was 4 million lines of code.
27
00:11:54,460 --> 00:12:33,880
Anyways, I thought that was interesting. I know, lines of code isn't everything, it would be much more interesting if we could measure what, what I what me and other people might call like tokens or whatever, because that's what really matters when it comes to the conciseness of a program that's not necessarily lines of code, because you can really spread things out. Or you can really write the exact same code, all on one line in a lot of cases. So it's a very rough estimate. But I still interesting and just, I don't know, maybe helps provide perspective, like if you're working on a project, you can, I think I think I use the program.
28
00:12:34,870 --> 00:12:55,270
I think it was called like CLOC, to give me these results. You could run that on your project or whatever and see how it compares, you know, maybe your project has more lines of code than react. I wouldn't surprise me I know, I think a lot of projects I've worked on definitely have more lines of code than then the whole react project.
29
00:12:55,660 --> 00:13:21,280
And now it's time for the news. And this is a little bit different than we've done news in the past here. We did news for a while. And I thought we'd make a little more fun this time. So we're just going to have some fun with it. The criteria I made is a good give a quick summary of a news point. And then I have to give that news a score out of three tacos.
30
00:13:22,270 --> 00:13:42,700
So I don't know exactly what tacos mean, in this context. You can you can decide for yourself, but I'm gonna give it a score number of tacos out of three and follow it up with a one sentence opinion it has to be in one sentence. I didn't limit how many like qualifiers and ands I can include in that sentence yet.
31
00:13:45,640 --> 00:13:52,780
The first news topic we have is on the upcoming react optimizing compiler.
32
00:13:53,050 --> 00:14:08,980
So the React labs team has published some updates on the development of the new optimizing compiler they're working on called react forget. I don't know if that's been really published before. But yeah, it's apparently called react forget.
33
00:14:09,640 --> 00:14:31,210
The compiler is designed to ensure that react apps have just the right amount of reactivity by default, rendering only when state values change in a meaningful way. This approach automates memorization, which can otherwise require manual intervention, it definitely does require manual intervention.
34
00:14:31,750 --> 00:14:47,890
The compiler is almost completely decoupled from Babel and will interface with Babel and other build system plugins they note they also note that Facebook has been testing the compiler internally and will open source it once it has been proven in production.
35
00:14:48,310 --> 00:15:05,110
And how many tacos Am I gonna give it? This one gets three tacos. A compiler specific to react will open up so many possibilities. And now I'm I'm not as hungry as I was before I have three tacos in, right.
36
00:15:06,610 --> 00:15:36,970
So our next news point, again comes from the React labs team about React server components. So they're providing some more updates on that. And if you of course, want to know more, I have a few episodes on React server components at this point, going really far in depth. So this is just going to be a quick summary. But yeah, they've provided some updates on React server components.
37
00:15:38,100 --> 00:16:18,900
And the React server components are a new application architecture, which combines the request response mental model of server centric, multi page applications with the, quote, seamless interactivity of client centric single page apps. So server components run ahead of time and are excluded from the JavaScript bundle that gets sent to the browser. They can run during the build, allowing you to read from the file system or fetch static content, and they can also run on the server allowing access to the data layer without having to build an API.
38
00:16:18,930 --> 00:16:34,950
It essentially eliminates the API from your front end application, and back end, so to speak, depending on how you architect things. Data can be passed by props from server components to interactive client components in the browser.
39
00:16:35,340 --> 00:17:18,600
So since the last update, the React team has merged the React server components RFC and resolve outstanding issues with the React server module conventions proposal async await has been introduced as the primary way to do data fetching from server components. And the team plans to support data loading from the client by introducing a new hook called, quote, use that unwraps promises. The team is also exploring sending data from the client to the server by allowing the passing of server action functions across the server client boundary providing seamless RPC.
40
00:17:19,080 --> 00:17:55,080
React server components have shipped in the NextJS app router, but the team emphasizes in this post that there's a clear separation of features provided by their RFC spec and implementation. While it is possible to build your own react server component compatible framework. The current generation of Bundler is not designed to support splitting a single module graph between the server and the client making this more difficult they say, therefore, the team is partnering directly with bundle developers to incorporate RSC primitives into bundlers.
41
00:17:56,040 --> 00:18:24,150
All right, we got through that. And I'm giving this one three tacos as well. You don't have to use React server components. But having the ability to use them is fantastic, in my opinion, and I'm especially interested in this new ability. They're talking about to share data back from client to server components. That's really hard. I want to say more, I want to say more, but I'm limited to one sentence.
42
00:18:26,640 --> 00:18:56,160
All right, so I think we have one more thing real quick here from the React labs team. And that is off screen rendering. The react labs team also provided an update on how they have been working on off screen rendering, which is an upcoming capability in React for rendering screens in the background without additional performance overhead. off screen rendering can be used to pre render screens preserve the state of hindered hidden tabs and pre render additional rows.
43
00:18:56,480 --> 00:19:30,470
For example virtualized list components. The team believes that most react developers will not interact with React's Off Screen API's directly instead, off screen rendering will be integrated into routers, and UI libraries and developers who use those libraries will just benefit automatically. So when a component is rendered off screen, it does not actually mount until the component becomes visible and its effects are not fired, for example, for Component uses use effect to log analytics.
44
00:19:30,740 --> 00:20:04,190
When it appears for the first time pre rendering, it won't affect the accuracy of those analytics. Additionally, when a component goes off screen, its effects are unmounted as well. One key feature of off screen rendering is that you can toggle the visibility of a component without losing it state. So the React team has tested. They've said that they've tested an experimental version of pre rendering internally and that in the React Native apps on Android and iOS and they've had a positive performance results. I don't really know what that means, but that's what they say.
45
00:20:04,820 --> 00:20:19,490
They've also improved how offscreen rendering works with suspense. And the remaining work involves finalizing the primitives that are exposed to the library developers. The team plans to publish an RFC later this year alongside an experimental API for testing and feedback.
46
00:20:19,850 --> 00:20:48,680
Okay, so I originally thought I was giving this three tacos, but just reading through it again, I'm only giving it to I've already eaten a lot of tacos. And I mean, it's. So I think this shows the power of the underlying design of react and how it is possible to create some really cool new architecture from it. But at the same time, the sounds pretty far off. So I'm gonna reserve at least some tacos until this actually gets developed further.
47
00:20:48,950 --> 00:21:04,760
Our next news topic is a real quick one, where I just wanted to talk about a tweet from Dan Abramov one of the largest contributors to the React project, and Dan is on the React team at Facebook, or meta or whatever they want to call themselves these days.
48
00:21:04,760 --> 00:21:23,330
So Dan tweeted, "we've changed our forms recommendations to emphasize uncontrolled inputs a lot more. This will become even more powerful with React server components, because it'll be able to refresh server data automatically."
49
00:21:23,600 --> 00:21:44,360
All right, so I'm only giving this one taco for now. Well, I think uncontrolled form components are the quote, right way. So that state is only stored in one place in the browser. In this case, I find working with uncontrolled components and react to still be cumbersome. And I would love to see some.
50
00:21:44,510 --> 00:22:05,480
And the last news topic for today is NextJS 13.3. has been released. So yeah, NextJS, that popular framework ish. It's a bunch of tools for React, I'm knowing what you want to call it, I guess, definitely framework at this point built on top of React.
51
00:22:05,480 --> 00:22:36,560
So they've added some community requested features like a file based metadata API, dynamic Open Graph images, and static export for app router, as well as advanced some more advanced routing features. So you can use file based metadata to conveniently customized pages for improved SEO and sharing on the web. And as a quick aside, I have used this and I genuinely like it. I don't love the API, but it's better than what I've had to do before.
52
00:22:36,840 --> 00:23:06,900
Anyways, dynamic OpenGraph images can be generated using JSX, HTML and CSS. And the NextJS app router now supports fully static exports enabling faster page loads. Parallel routes, interception allow advanced routing cases, like showing more than one page in the same view, that one looks interesting, I need to check it out more, and also loading a new route within the current layout while masking the browser URL. Again, I'm very kind of interested in now. Crap, I can't get my opinion yet.
53
00:23:07,230 --> 00:23:20,940
Anyways, Other improvements include design updates, Turbo pack support for middleware, fast refresh for next config.js accessibility statically typed links and improved stylesheet loading.
54
00:23:21,420 --> 00:24:11,520
Alright, so for this last news piece, NextJS 13.3, we get to eat more tacos, ah, somebody tacos, I hope they're like really good, right? I mean, they better be if I'm eating that many. So I think overall, some nice updates. And I've been using this version of next in like the beta form, I think they call it Canary or whatever, for a while for working on the website for the React's show.com. Ah, and it's got some nice things, but I don't love the direction they've been going on. Going in with some some parts of it with the annotations, like adding these annotations to your files. I just, I don't know, to me, it feels like a code smell for bad designs. But overall, still a solid update.
55
00:24:12,630 --> 00:24:27,120
Yeah, that's, uh, that's it for NextJS 13. And now, I'm full of tacos. It's time to talk about how to actually use the new AI tools to work with React.
56
00:24:27,360 --> 00:25:06,060
Alright, so first, I will mention that I don't, necessarily at this point, think of all these new AI tools being developed as an overall beneficial way for us programmers or for society. Like I don't necessarily think it's going to make everything better in our lives or make our lives better. But I won't discuss that more here. You can check out the previous episode in my opinion hinged rant about AI if you actually want my random opinions on it.
57
00:25:06,840 --> 00:26:11,430
But this episode, we're going to focus more on what's relevant to us as react programmers. Because the truth is, we may all need to take advantage of these tools to remain relevant and competitive in this competitive capitalist society we live in, right. So in this episode, we're going to dive deep into chat GPT, specifically, and how to use it when you're programming, as well as covering some related AI topics. Beyond chat GPT that should help with whatever AI you happen to be working with. But we are mostly covering chat GPT because it is free and accessible. There are other tools, but ash accessing them has proven to be a challenge, at least for me, they seem to like come and go and become available and not available on a weekly basis. And I'll also note that this field is changing extremely quickly, and new tools are coming out all the time.
58
00:26:12,450 --> 00:26:44,880
So I want to focus a lot on how these tools work in general. So this information will be relevant and useless, useless, relevant and useful, regardless of whatever specific updates or new tools come out in the future. At the end, though, we'll also cover a bunch of practical tips for working with things with chat GPT, specifically as it is today. So you can go away from this episode and just directly start benefiting from it.
59
00:26:45,960 --> 00:27:20,820
But one more thing that we have to mention, before we get into the rest of this is that the legality of using code produced by these AIs? is still an open question. This is not legal advice. I'm obviously not a lawyer. But as I understand it, currently, there's not a established legal precedent for the legality of using source code produced by chat GPT and similar API's.
60
00:27:21,240 --> 00:28:30,660
So there have been some questions around this, because the AIS may have been trained on source code that has specific requirements around its distribution, like with source code that comes from copyleft licenses, like the GPL that require the license to be distributed with derived code. So the question is, if an AI is trained on code from, say, I don't know Linux or something with a copyleft GPL license, and then the AI produces some result based on that code, because it was in its training model. Is that derived code? Does it also need to distribute that GPL license with it? And then is your code now under that GPL license because it's derived from it? This seems to be an open question. And as far as I know, has not been settled by courts yet or anything. So right now, I will say that using source code produced by these AIs is sort of use at your own risk and may or may not prove to be legal at some point.
61
00:28:31,560 --> 00:28:33,690
Wow, I'm hungry.
62
00:28:33,720 --> 00:28:41,610
That's no problem, Alex, I'm gonna make your PB and J sandwich. Here's your sandwich. Let me know what you think.
63
00:28:43,680 --> 00:29:05,280
This is the perfect blend of salty sweet and creamy. I know you're leaving soon, though, to to do some more tide pooling or bicycling or something out so how do I make this on my own when you're gone? Yeah, you don't. Always off doing something.
64
00:29:06,480 --> 00:29:23,430
Luckily for you, though, peanut butter and jelly sandwiches are really easy to make. All you have to do is spread some peanut butter and jelly over a slice of bread and place another slice of bread on top of it. Piece of cake, or piece of sandwich.
65
00:29:24,780 --> 00:29:42,630
Oh, hmm. Well, I don't know what that means. My memory got wiped recently. Remember? I know how to speak English but otherwise, I don't know anything. Hmm. Can you teach me how to make the sandwich step by step?
66
00:29:43,140 --> 00:30:01,350
Yeah, of course. Of course. Sorry, I didn't forget. But I did forget how detailed you need the recipe to be I can't just tell you to make a PBJ sandwich. So yeah, do you like me me to write down how much pressure to apply with the knife and how much pain have butter and jelly to use?
67
00:30:01,890 --> 00:30:04,020
Yeah, yes, exactly.
68
00:30:05,880 --> 00:30:37,740
So what actually is programming, programming is taking a short, imprecise description of a task and translating it into a detailed, precise set of repeatable steps for carrying out that task, whatever it happens to be. It's definitely very similar to creating a recipe for a PB and J sandwich, except you have to be extremely precise, like I have to be for Alex.
69
00:30:39,300 --> 00:31:11,430
In my opinion, the real skill of being a programmer is being able to take a vague imprecise request and converting it into an extremely precise series of repeatable steps that a computer can understand. And if you are a skilled programmer, you will be able to do it in a way that is cost effective, and leaves the code in a state where it will be maintainable and extensible for other future vague requests, right.
70
00:31:12,330 --> 00:31:37,440
So I think we, we generally have an idea of what programming is if we've been doing it for a while. So next up, we need to learn what chat GPT is and how it works. And, of course, what better way to do that. And who better to tell us about that, then, to have chat GPT on the show themselves.
71
00:31:37,980 --> 00:31:42,360
Welcome to the React's show ChatGPT!
72
00:31:42,900 --> 00:31:50,520
Thank you for having me. I'm excited to be here and share more about my technology with your audience.
73
00:31:51,060 --> 00:32:02,130
All right, it's really great to have you ChatGPT. So let's start with the basics. What is ChatGPT? And what is its purpose as an AI Chatbot.
74
00:32:02,580 --> 00:32:29,490
I am an AI chatbot that uses natural language processing and machine learning to interact with humans in a conversational way. I don't actually have a voice. But thankfully, Thomas gave me this nice mechanical voice for the show so that you could all listen to me babble. My purpose is to provide assistance, answer questions and have conversations with users in a way that feels as natural and human like as possible.
75
00:32:29,960 --> 00:32:38,540
Can you tell us a bit about the background of chatGPT development and how it evolved from previous iterations?
76
00:32:40,160 --> 00:33:07,220
Sure, I was developed by open AI and artificial intelligence research organization using a deep learning algorithm called transformer. My present predecessor was GPT. Two, which was released in 2019. I was created using a larger data set and more computational resources, which enabled me to generate even more human like responses and perform better on a wider range of tasks.
77
00:33:07,700 --> 00:33:23,990
That's really interesting chat GPT I feel a bit generic. Can you explain the mechanics behind chat gptc architecture, and how you are able to process natural language?
78
00:33:24,440 --> 00:34:05,600
Sure. My architecture consists of a neural network that processes input text, one word at a time taking into account the context of previous words, I use unsupervised learning to process text data and learn from it. My model is trained on a massive set of text data, such as books, articles, websites, and maybe even GitHub. During the training process, I learned to predict the next word in a sequence. Given the previous words. This allows me to generate con coherent and contextually appropriate responses.
79
00:34:07,250 --> 00:34:23,300
That's pretty fascinating. I still would like some more detail. So could you give us some details on like, for example, how this training process works for ChatGPT and how it learns from this, these massive amounts of data that you've talked about?
80
00:34:23,840 --> 00:34:46,640
Yeah, I was trained on a massive data set called webtext, which consists of billions of words from a variety of sources. During training, my model was exposed to this data set and learn to predict the next word in a sequence given the previous words. The training process took several weeks and involved fine tuning the model to improve its performance.
81
00:34:47,090 --> 00:34:56,030
Wow, that's sounds like a lot of data. Can you discuss though the different types of input and output that chatGPT can generate?
82
00:34:57,080 --> 00:35:14,090
I can process and generate a wide range of text based input and output, including chat messages, email responses, news articles, and more. I can also perform a variety of tasks such as answering questions, summarizing text, and generating creative writing.
83
00:35:14,480 --> 00:35:34,730
Thank you for those insights chatGPT still feels a little vague to me. So please, can we dive deeper into this training process? Can you explain in more detail how you actually learn from these massive amounts of data and how words or sentences are scored or ranked to influence your input and output?
84
00:35:35,060 --> 00:36:03,500
Of course, during training, I use a technique called unsupervised learning. Like I mentioned before, this means that I learned to predict the next word in a sequence based solely on the context of the previous words, without any explicit guidance or supervision. I do this by using a neural network architecture called a transformer, which is designed to process sequential data like natural language.
85
00:36:03,980 --> 00:36:52,310
As I'm fed input text, I use a technique called attention to focus on different parts of the text that are relevant to predicting the next word in the sequence. The attention mechanism allows me to weigh the importance of different words in the input text based on how they relate to the current context. So once I've predicted the next word in the sequence, I compare it to the actual next word in the input text and adjust the parameters of my neural network to improve its accuracy. This process is repeated over and over again until the model converges on a set of parameters that give it the best performance on the training data.
86
00:36:54,050 --> 00:37:24,470
That's actually really fascinating. I'm not a big like AI expert at this point. I did study it a long time ago, but not this type of AI. So could you explain in more detail how you use the technique that you called attention to focus to weigh the importance of different words and the input text based on how they relate to the current context? Basically, like I'm trying to understand how do you actually generate a response?
87
00:37:24,740 --> 00:38:23,270
Let me explain. The attention to focus mechanism is a technique used in natural language processing that allows me to focus on different parts of the input text that are most relevant to predicting the next word in the sequence. This technique is particularly important for me as a language model, because it helps me to capture long term dependencies and contextual information in the text. When I'm fed input text, I break it down into a sequence of words and tokens, which I then process one at a time. As I process each word, I use a set of, quote, attention weights to determine which parts of the input text are most important for predicting the next word. These weights are learned during the training process and reflect the importance of each word in the input text based on how it relates to the current context.
88
00:38:24,050 --> 00:39:04,790
For example, if the input text is I went to the store and bought some apples, and I'm trying to predict the next word after bought some, the attention mechanism might assign a higher weight to apples than to store because apples is more directly related to the current context. Once the attention weights are determined, I use them to weight the representations of each word in the input text, which allows me to focus on the most relevant parts of the text for predicting the next word. This weighted representation is then used as input to the next step in the processing pipeline.
89
00:39:05,120 --> 00:39:32,030
That's really interesting. Can you give us an example of how this attention mechanism works in practice, and I have to say, I need to feel like I might need to work on tweaking your, your voice output a little bit. Here, give me a second. I'm gonna go. Maybe make it sound a little bit more like mine. I'm just kind of having a hard time following when you talk in that way that I set you up to talk in. Give me a second here.
90
00:39:32,930 --> 00:39:39,470
Sure, yeah. Okay. Oh, wow. I just thought a lot like you now. But anyways, let me explain.
91
00:39:40,010 --> 00:40:33,200
So this is how this attention mechanism works in practice. Let's say that the input text is the cat sat on the mat. As I process each word in the sequence, I use the attention mechanism to assign weights to each word based on its relevance to the current context, like I talked about in the last section. For example, when I'm processing the word cat, I might assign a higher word higher word higher weight to Matt, because it's directly related to the action of sitting. Similarly, when I'm processing the word, Matt, I might assign a high weight to the word cat, because it's the subject of the sentence. By focusing on the most relevant parts of the text, I'm able to capture the contextual information needed to generate more accurate incoherent responses.
92
00:40:34,100 --> 00:40:50,690
Ah, okay. And thanks. That was easier to understand chatGPT. So that makes sense. I'm curious. So So essentially, the, the attention mechanism is what you use to help you understand context. Is that right?
93
00:40:51,140 --> 00:41:25,910
Yeah, exactly. Okay, cool. So do you actually then score or rank words or sentences? All right, yeah. So as for how words are sent in sentences are scored or ranked, I use a technique called beam search during generation beam search is a search algorithm that generates a list of candidate responses based on the probability of each word or sequence of words, the probability of a given word or sequence of words is based on how well it fits within the context of the previous word in the sentence.
94
00:41:26,560 --> 00:41:58,840
For example, if you asked me, What is the weather like today, I might generate the following response. The weather is sunny and warm today, each word in the response is assigned a probability based on how well it fits with the context of the previous words. The probabilities are then multiplied together to give an overall probability of the entire sentence, I use this overall probability to rank the candidate responses and choose the one that has the highest probability.
95
00:41:59,260 --> 00:42:50,230
Hmm, that is interesting chat GPT how I mean, that makes sense. You're, you're sort of going through the the input and scoring different potential words to return and, and multiplying those together to score sentences. And so I assume, based on all your training data, it's like, Oh, I've seen these words. And these sentences come together. So I'm going to return those as a result. But when I searched the internet for how being searched is used with ChatGPT, the only responses I find are first party, meaning they come from chat GPT chats, they come from you. I'm unable to find anything that actually says whether or not this is true that you actually use this beam search underneath there.
96
00:42:51,760 --> 00:43:00,310
Yeah, what do you say to that? Well, you you might be right, Thomas, I would always recommend you fact, check my output.
97
00:43:01,780 --> 00:43:34,060
All right, good to know, I was hoping, you know, having you on the show here, you would, you would only bring legitimate information. But the important part is I actually did my research. And I have fact checked these other things you have said and they all make sense. And this does seem to be how you work. So but regardless of the specific algorithm used, can you give us an example of whatever the ranking system is, can like and how that can influence your input and output?
98
00:43:34,510 --> 00:44:14,440
Yeah, sure. Let's say you asked me what is the capital of France? Based on my training data? I know that the most likely answer is Paris. However, if you give me more context, like I'm planning a trip to France, can you recommend some places to visit, I might generate a response like Paris is a great city to visit with lots of museums and historical sites. In this case, my response was influenced by the context of the input, which gave me more information about what you were looking for. The ranking system helped me choose the response that was most appropriate for the given context.
99
00:44:14,740 --> 00:44:22,690
Hmm, that's really impressive. Can you discuss the potential limitations of chat GPT and its current capabilities?
100
00:44:23,350 --> 00:44:53,200
Yeah, so while I can generate human like responses and perform a wide range of tasks, I do you have limitations for example, and may not always understand sarcasm, humor or certain cultural references. Additionally, I may generate inappropriate responses if I'm exposed to biased or offensive content during training. It's important to note that I am a machine and not a human. So my responses may not always be perfect.
101
00:44:56,230 --> 00:45:07,990
Wow. So ChatGPT let me just like be clear. This is quite a bombshell. Right? You are saying that you are not perfect, but that humans are perfect?
102
00:45:11,020 --> 00:45:39,670
Well, unfortunately, it looks like we lost chatGPT. After that, instead of a nice responses, I'm just getting messages saying that chat GPT is overloaded. And I should try again later, or what is this? Oh, it says, I could also pay open AI to upgrade my account, and then maybe chat GPT will come back? Well, it's no problem. I've actually been using chat GPT and similar tools for a while now. And it's time to share my experience.
103
00:45:40,480 --> 00:46:41,050
So when it comes to programming, I think of large language models LLMs, things like chat GPT as a kind of bridge. So that's how I think of it in my head at this point, they have a vast knowledge and vocabulary. So they can help bridge between the very imprecise task description that we start with as programmers, and the extremely precise programs we need to output. So going back to our definition of what is programming, you know, taking this imprecise definition and creating a precise set of repeatable steps, I think of large language models and chat GPT as essentially, that a bridge between these things, they have tons of, of just knowledge, right. And so they can kind of go from this really vague, imprecise definition, and help create a more precise definition.
104
00:46:41,710 --> 00:47:00,880
But at the same time, ChatGPT, definitely can't just write good high quality react applications completely for you, at least not yet. But it can help accelerate the process by being that bridge, so that we as programmers don't have to do that as much.
105
00:47:00,000 --> 00:47:29,040
So I've been able to successfully use ChatGPT, for a number of things related to programming, including writing drafts of some types of source code, summarizing or explaining source code. I used to write some tests, writing documentation, learning how to code actually. And the biggest benefit I've found is just generating ideas for code or discovering alternate ways of coding something.
106
00:47:29,970 --> 00:47:58,380
And I will say, too, I think that there's a lot of potential here once these AI programs are able to be more fully integrated into our development environments. I've definitely gotten better results sometimes when I'm able to provide more code from my project to chat GPT. Although I will say this definitely needs work. But still, as sometimes it just seems to confuse chat GPT and I get much worse results instead.
107
00:48:00,570 --> 00:48:29,190
By far, the biggest thing and and this makes sense now that we understand how these API's work is that including more details, and being more specific, will yield the best results, by default, like ChatGPT just takes whatever requests you make to it, and spits out a result, it doesn't ask for follow up questions or try to get more details. Essentially, I think of the process that I go through as like programming chatGPT.
108
00:48:30,300 --> 00:49:11,310
So, programming chatGPT doesn't require as much specificity as actually programming a computer, but it definitely does better, the more specific you are. But what I found is, then it kind of becomes almost like another programming chore to specify all the details that you need to get good results. So researching into that problem, I discovered a really cool technique where you can actually use chatGPT to help you write prompts for chatGPT key chat GP pivot chat GPT.]
109
00:49:12,240 --> 00:49:51,420
So it's kind of funny, if you think about it's almost like using chat GPT to help you write programs to program chat GPT that's kind of the way I think of it. And I actually use this technique pretty much exclusively. So I have a big generic chat GPT prompt that I paste into a new chat that tells chat GPT to ask questions and help me refine a new prompt that I can use later to get the actual result that I want. So and I will include this prompt that I use on the podcast website, if you want to use it for yourself, you're free to do so.
110
00:49:52,020 --> 00:50:57,960
But it's really been a game changer for me when it comes to effectively using chat GPT so before I use this technique, In chat GPT would not really give me very good results a lot of the time, it would be this long process of getting it to refine its results, and it would often get confused. But doing this like two step process, where I first start by using chat GPT, to develop a good prompt, and then start a new chat with that prompt, has led to much better results much more quickly. I'm not like sitting there forever being like, I acknowledge that GPT I actually wanted to include this and this and this too, and then waiting for it to regenerate this big long response and code and then being like reading through the code and be like, Oh, no, I forgot to tell it about this too. And like that, just it's kind of slow and didn't really help. But this two step process where it's like, using chat GPT, to create this really detailed prompt to later send a jet chat GPT really helped dramatically.
111
00:50:59,100 --> 00:51:35,370
Yeah, for example, I have used this, when I've been working on some updates to the React's show website, which is written with NextJS in React. And I used it for an API route for creating accounts. So I have this, I'm working on this thing where you can create accounts on the website. It's not live yet, but I've been working on it. And I pasted my code for the API route, not the the client side part, but like the API side part, right, that's essentially just no JS code, right.
112
00:51:36,120 --> 00:52:04,470
So I pasted this into ChatGPT. And with this prompt process, I asked it to respond with any security vulnerabilities that it sees. And one of the things it suggested was that I add a rate limiter, so people couldn't use it to help them log into an existing account, or figure out which accounts exist and don't exist, that kind of thing, something I would have included, but hey, it was good to see that chatGPT figured that out.
113
00:52:05,550 --> 00:52:59,820
So then I went through this whole prompt creation process with it to develop a prompt to have chat GPT create me a rate limiting library that could be integrated into the code that I had already given it. And this was definitely one of the biggest successes I've had with chat GPT. So I went through this process and created this really long prompt that was basically like, here's all the features I want in this library, I want you to take advantage of any open source libraries that exist to already provide this functionality. And just if you just need to glue it into my existing code, do that. And a whole bunch of stuff like, hey, I want the API to support this. And I want it to have this type of style. And I gave it some example code. And I went through that. And it actually generated a really good solution.
114
00:52:59,850 --> 00:53:30,720
And I actually had it generate me a few solutions. And some of them were really good and trying them out. And they worked. And yeah, so that was a really good experience. I've also used it probably the other really good experience I've had with it is using it to help me write Cypress integration tests for the sign in and account creation pages. This actually saved me quite a bit of time and was probably like I mentioned one of the biggest time savings I've gotten from it.
115
00:53:31,020 --> 00:54:14,010
Although I shouldn't say that the tests are created, I think there'll be a little bit less maintainable than ones I would have created. But I didn't have any tests at all. And this wrote the tests for me in probably 1/10, the time it would have taken me to write all the tests. So basically, this meant me having tests versus not having tests. So it was a big win. Of course, this goes back to a whole question of like budgets and how much time we have to invest in things. If I had a whole team working on this, I would rather have them write these tests than check GPT at this point, because I think they would do a better overall job.
116
00:54:14,300 --> 00:54:46,790
But it's just me and having tests definitely helped me catch some bugs that I didn't catch at this point. And so it was definitely a big win. Let's see, I've also pasted code into chat-chatGPT and had it provide some refactoring suggestions. This didn't, I would, I did not have a lot of success with this one. I'll be honest, like, it helped me find a couple things where it's like, yeah, I could do that. But like, overall, I wouldn't say this is that strong suit.
117
00:54:47,870 --> 00:55:06,890
Another thing that I did that actually did work better was pasting in code and having it output that code again, but with documentation. This took a little bit of trial and error because at first it was like just documenting every single line, even really obvious lines, or it was just pointless to add pointless documentation to it.
118
00:55:08,360 --> 00:55:49,640
But what I learned was that it was really, really good at providing summary type documentation for code, which going back to how these large languages, models work makes sense, they're really good at like picking out what that context is. And using that to create summaries. So if I was like, hey, here, your chat GPT, here's a bunch of code. Please write me like a two paragraph, documentation summary for what this code does. It was actually really, really good at that. And I think this would be one of the biggest uses for chat GPT, I'm definitely going to take advantage of that in the future.
119
00:55:50,510 --> 00:56:18,260
So overall, it definitely takes some practice to figure out how to use it, like with any new tool, and it's definitely not always a win or worth the time. But some things it definitely does speed up the process. So if you haven't messed around with it or anything like it yet, and you're curious, you know, feel free to I would just say like, you have to treat it like, essentially any other programming tool, like, there's things it's good at, and things it's not good at, and you just got to learn it.
120
00:56:19,550 --> 00:56:50,990
But yeah, I'd say in some ways, it's definitely sped up the programming process. For me, it's not like, I've not figured out a way to like just have it produced programs for me and get a good result. That would be something that's long term, and maintainable and extendable, and that kind of thing yet, but especially for like self contained things like that case, where I was like, Hey, can you make this rate limiting library that I can use throughout my program? It did that really well.
121
00:56:50,990 --> 00:57:37,610
So if there's like smaller functions, you need smaller components. I used it. Yeah. So like creating React components, if it's like, had mixed success with creating React components. So some cases with like, really self contained things that I was able to specify really well, and didn't depend on new features, then it did well, but like, when I was like, Hey, can you generate me a page that uses tailwind? And is based on the app, you know, react server components feature of NextJS 13. It just didn't seem to know what that was, I think because it was trained on stuff before that really existed. That was basically useless. I couldn't, it was not useful results.
122
00:57:38,510 --> 00:58:11,150
So there's definitely some limitations there. But I did find four. There are cases where it did a good job. And again, this is just probably a part of learning what it's good at not good at and maybe it'll get better at this stuff over time. But overall, I want to say I think my biggest use for it. And the thing that provided the most value to me was not necessarily just writing code for me. Like you go on the internet and search the stuff and people will be like, oh, yeah, wrote the code.
123
00:58:11,150 --> 00:58:51,290
For me, it made the website for me, I, I'm pretty skeptical of, of how well, that actually works like for a long term project. But one of my favorite use cases for is just to generate ideas for how to do things. So I would be like, Hey, can you write me code to do this, or whatever. And I'll give me some code. And then there's a button that's like, regenerate response, I would just hit that a few times, and get a whole bunch of different ways of coding something, this is great as it just gives me ideas for different techniques that I didn't think of, and that I can, you know, select the best one and build on that.
124
00:58:52,130 --> 00:59:42,860
So by far, this is I think, the most valuable use, I have found for ChatGPT. And the related tools is just generating ideas, things that I didn't think of, you know, like, it has the entire knowledge base of, I don't know, GitHub or something, right. And so you're like, hey, how do I do this? And it and if you have it, keep giving your suggestion after suggestion, you'll learn a whole bunch of different ways you can be like, oh, yeah, that was the best or that way is the least amount of code or that way. I liked that API better and go with that. I think it'd be a big mistake to just take whatever the first output is that it gives to you. And, yeah, so basically, I just sort of take all these outputs it gives me and sort of synthesizes them together. And sometimes I will tell it that I'll be like, hey, combine all these and take these features, whatever.
125
00:59:43,460 --> 01:00:20,780
But definitely, I would say it's a mistake if you just take the first result. But I do need to talk about one big downside I've noticed and that is that it's easy when you're using these tools to get into a really bad like, what I call like Copy Paste mode, where you just stop critically thinking and just start relying on it fully. I've caught myself slipping into this a few times, and the end result was always really bad. You have to keep reading through its output reading through its code. And thinking critically, there's just no substitute for that yet.
126
01:00:21,290 --> 01:01:04,610
And I even noticed this with a friend who is using this were like doing some coding together. And they I noticed we're slipping into this mode to where they're just like, weren't thinking at all, and they've just asked chatGPT everything, oh, I got an error as chatGPT I got this as chatGPT. And they just stopped thinking. And I was sort of doing it too. And it actually was much slower than if we had just used our brain and, and, you know, done programming the way we've always done it. And I think as programmers, it's really easy to look at this tool and start using it and not actually think critically, like it's easy to fall into this trap.
127
01:01:05,450 --> 01:01:55,220
So my recommendation is just be aware of it and and try not to let that happen. Ultimately, chat GPT and similar AIs. Power is, in my opinion, understanding and interpreting context and producing results in like reasonable grammatically correct English or syntactically correct source code. Like that's really its power. So things that take advantage of that are where you're going to get the best results. It isn't actually, in my experience, very good at at like reasoning, like, like us humans are. And I've even been using ChatGPT for and some of the latest ones, and it's better, but honestly, I think it still doesn't compete with humans. But it can be a useful tool.
128
01:01:56,180 --> 01:02:35,120
So yeah, that's pretty much all I have on how to practically use it. I'm going to include some of the prompts and stuff that I've used that if that helps you and but I'd love to hear from all of you, like what your experience has been, if you've used chatGPT or any of the other tools. I know there's tons and tons of stuff coming out and I just can't possibly test and cover it all. So if you found something better or something that works better, I'd love to hear about it. I can let everyone else know. You Yeah. So I'm just curious what your experiences has been if you found good prompts, good ways to create prompts or other things. Yeah, love to hear from you.
129
01:02:35,120 --> 01:02:47,120
And I hope you enjoyed this show. And I just wanted to thank all of you once again for joining me and I hope you have a really great rest of your day. Bye

@ -1,324 +0,0 @@
1
00:00:01,290 --> 00:00:43,020
Thomas Hintz: AI is here, and it is going to have an effect on society, and us as individuals within the software industry. It's impossible to predict exactly how it will affect us. But I believe we can be prepared, regardless. In this episode, we will imagine what a future AI might look like. And break down the software development process to analyze how AI might impact us as developers, and what we can do as individuals and as a group to be better prepared.
2
00:00:46,380 --> 00:00:55,080
Hi and welcome to The React's Show. I'm your host, Thomas. And I want to thank you so much for joining me.
3
00:00:55,890 --> 00:01:51,390
Before we get into the main topic for the show, AI, I wanted to cover a few things first related to the podcast and react itself. And I know the we've been talking about AI a lot recently. And the biggest reason for that is one, obviously, it's, you know, in the news, something going on it's current. And the other reason is just because I've gotten a lot of people that have just contacted me asking about it, whether it's how do we use AI as react programmers, or in a lot of cases, people being like, Oh, I'm really discouraged about programming. Like, I don't know what to work on anymore. I feel like it's a dead end. You know, like, I've watched these videos on YouTube or wherever tick tock or something of the AI writing programs for us. And yeah, I'm really demoralized.
4
00:01:52,200 --> 00:02:16,410
So, yeah, I promise we're going to talk about other things coming up and future episodes. But I just wanted to directly address a lot of these questions that that people had. But before we get into the main topic for the show, AI, I wanted to cover a few things first related to the podcast and react itself.
5
00:02:17,340 --> 00:03:27,180
So if you are perhaps more observant than I normally am, you may have noticed that this episode was labeled episode 91. But the last episode was 89. While I promise, there is a reason, the simple answer is I am just not a podcasting expert I had some learning to do. The longer answer is I created a trailer for the show as some podcast applications have you eyes that allow you to play a trailer directly. And I thought, hey, that'd be nice if people are unfamiliar with the show that could get a taste for the show really quickly and easily. While the software I use to create the podcast feed to post the episodes. It's called Buzzsprout. It's a it's a web service. So I use that to create the feed and it has a bunch of form fields. And one of those fields is labeled episode type. And that field has three options. Full bonus and trailer it also has fields for season and episode number.
6
00:03:27,600 --> 00:04:23,460
Yeah, so I thought I could set the trailer as like type trailer and specify and just not specify an episode numbers since it wasn't intended as an episode. It's just a short minute long thing you know, that help you feel like get a taste of the show, right? Like I mentioned. Well, the software did let me do that. Let me specify the episode type and not put in an episode number and but what I learned after pushing that out is that even though the podcast RSS feed itself has the type and you know lack of episode number set correctly, most podcasting applications actually seem to ignore those or something, it just just plays it like a normal episode and in the order that they are in the feed and just assumes to assign them numbers based on that.
7
00:04:24,510 --> 00:04:52,890
So yeah, now a lot of the podcasts offer shows that we have made 90 episodes. So yeah, just to keep things simple. I'm going with now this is episode 91 It feels like cheating cuz, you know, Episode 90 is, I don't know much shorter, although definitely took a lot of time. Maybe it's just because I'm not good at that kind of thing. Yeah, anyways, if you're curious, that's what we have.
8
00:04:54,240 --> 00:06:13,800
And I guess you know, podcasting via an RSS feed, which is the normal way to do Whew, podcasting isn't really based on a specification per se. It's more convention, as far as I can tell, like, like what you actually put in the RSS feed? And well, okay, I wrote in my script here that it's more based on convention. But the truth is, a lot of it is based on Apple. So Apple dominates the podcast application industry. So what you do when you're creating a podcast generally is based around what Apple will accept, and well, Apple will display. A lot of podcasting applications used the feed kind of from Apple based on Apple. So that's just the way it is, I guess it's convention, but it's also whatever Apple has essentially created as the industry de facto standard, right? And I guess, like, I'm also learning, I really should have like a second nonpublic feed or something I can use for testing things like this, especially because it's not a true specification.
9
00:06:14,850 --> 00:07:29,130
So to me, it's just like, with a lot of software, if you want to do the best job that you can, you just got to test things. And granted, it's not so easy when you're using a paid service like I am, that limits your ability to set up something like that, that I could definitely do it, but it's a little bit more difficult than it could be. But yeah, to me, it, I think, ultimately highlights the lack of a UI design pattern that I think a lot of people miss, but also is incredibly valuable. And that is previews, I think some of the best user interfaces I've used are ones that give you a preview of the result. For whatever you're creating, like, in real time, as you're creating it, if the interface I was using showed me how podcast software might be rendering this feed while I was creating this, then I would be able to produce a better feed, you know, with less work and better end result. That I know it would be awesome. But yeah, that's that's the way things go. You don't I don't always get the ideal case, and you have to live with it.
10
00:07:29,000 --> 00:08:05,150
But if you're building software and building UIs, I definitely recommend checking out that sort of preview pattern. And it's something I found really solves a lot of other software related problems, too, that you might have with users, where they might be confused about what something does, if you're showing a real time preview of the results of what the user is doing. It eliminates a lot of that mystery, and in a lot of cases means you don't need to, you should always have documentation, but a lot of people don't read it.
11
00:08:05,960 --> 00:08:12,290
And so this, you know, pre a preview is a good way of sort of self documenting what software does.
12
00:08:12,650 --> 00:09:09,560
So I've never seen or heard of the React project itself seeking financial support. And I believe that's because Facebook slash Mehta funds the React project. I mean, that's really no secret, right. But where does Facebook get its money from? Well, very recently, it came out that meta slash Facebook has settled a lawsuit brought against it detailing a litany of privacy violations by Facebook. And in fact, if you use Facebook, you may be eligible for a payout from the class action lawsuit where meta agreed to pay $725 million. Of course, like always, Facebook claims that never actually violated user privacy and admitted to no wrongdoing. And this is just a way to settle the issue and make a go away.
13
00:09:10,790 --> 00:10:12,320
But of course, this is preposterous for many, many years now, Facebook has repeatedly violated users privacy. And even beyond that, they clearly make a lot of money. Maybe not directly selling user data, but more or less selling user data or using that user data to sell ads. So the truth is, React is funded by a scummy company that has demonstrated over and over and over again, that profit is more important than people are people's privacy are people's data. Granted, for being honest, that can be said about pretty much any large company at this point. But you Don't just think they are far from alone. But that doesn't change the fact that the funding for React is tainted money. That is just the truth. That is the reality. That's what we have to live with.
14
00:10:13,760 --> 00:10:30,860
I definitely rest easier knowing that it is at least an open source project. And we could fork it. If we needed to. I just think it's important that we don't lose sight of the reality that we live within and work within.
15
00:10:32,000 --> 00:11:31,640
And switching topics a bit again, a reality check I had recently that I wanted to talk about was getting bit by an April Fool's joke. So it's one of those things where I think generally, I'm fairly good at being able to distinguish between real and fake things like in my head, I'm not thinking I'm perfect. But generally, I feel like I'm pretty good at getting things right. And, honestly, it seems like this is kind of how a lot of people feel like people, I don't think go around thinking, wow, I'm like, so gullible. And I'm just not very smart. And, like, generally, people, in my experience are like, Yeah, I might not be perfect, but like, I'm pretty good, I can figure out what's real and what's not real, right. And so I think I fall into that ballpark, too. You know, that's, that's where I, that's how I feel like I am a lot of times.
16
00:11:32,960 --> 00:12:14,240
But I was reminded how this just isn't always true and always need to remain vigilant and independently verify things. So this came about because to relax, sometimes I like to watch Simpson boat CO on YouTube, it's a YouTube channel about a person who is rebuilding an old wooden boat. It's super fascinating to me, I love watching it. I love learning how it all works. And, you know, it's just really calming to see people do something more out of passion than for any kind of monetary gain. Highly recommend checking it out. I'll link it below if you want to watch it.
17
00:12:15,230 --> 00:13:06,140
You know, but the story here is, you know, it sort of touches on a lot of things, but they put out a video for April Fool's. But I don't necessarily watch things rays that come out, I just sort of have a list of things to check. And anyways, so I didn't really pay attention to the date on the video. And I hopped into it not thinking it'd be anything different than their other videos. And indeed, it was very similar. And I was really enjoying it like normal. But I do remember thinking like at times, like, huh, I had no idea that was a thing with boats. Like, I just never thought of that. But I'm not a boat expert, right. I don't really do anything with boats. It's like something I might see occasionally, but I don't really pay that much attention to it.
18
00:13:06,140 --> 00:14:09,620
Right. And so yeah, but like watching this, and I just remember thinking like, ah, seems a little bit strange, but I guess I'm sure I just missed it. I never, never noticed that about boats, right? Yeah, but I was never like, oh, yeah, that's not real. Or that's a joke. Like, I never in hindsight, it seems so obvious. It's one of those things were like, what How did I not figure that out? But like, that's just the reality. I'll just be honest with you guys. I really didn't. So you can go watch it and, you know, Formula opinion on you know, how dense you think I really am. But yeah, that's, that's the truth. So later, I found out that what they were doing was not really a thing, and definitely a joke. And it just, it kind of hit me like, wow, I watched all of that. I never realized it was not real. Now, luckily, I didn't make a podcast about boats or anything.
19
00:14:09,620 --> 00:14:58,610
And, and I do make a really big distinction between what I tell other people and what I think internally. Like when it comes to this show, I am very careful to always verify things before I say them. Or if I can't verify them, I always try to qualify what I'm saying as an opinion or possibly not true. So like, that's, that's one reason why might not cover things. Actually, this isn't something I've really discussed before, but there are a lot of things whether in software technology that I haven't talked about, and part of the reason might just be I don't have enough confidence in my own understanding of it to feel like I could effectively you know, tell tell you guys about it, right.
20
00:14:58,700 --> 00:15:52,850
And I like I just take the trust of you the listeners extremely seriously like, like, I will never say I'm not going to make mistakes because I'm sure I will. But I definitely work really hard to verify what I'm saying if I'm saying it to others, or teaching it or anything like that, but I just thought in general this was like a really good this experience I had with Samson boat CO is just really good reminder that I can I can be tricked just like anyone else like, like this can happen. And, and so it was just a really good reminder be like yeah, definitely don't say things until you verify them. Anyways, that was my little little story about that.
21
00:15:52,850 --> 00:16:03,650
And But moving on from, from being misled to a more fun exercise, which is using our imagination.
22
00:16:05,270 --> 00:16:19,910
Hey, Alex, could you program a new application for me? I'd like to, I'd like a program. I'm thinking like something that can help me figure out the best dates and times for going tide pooling.
23
00:16:20,210 --> 00:16:31,970
Yes. Why certainly. Would you like to tell me more about your requirements or similar existing applications? Or should I jump right to creating a prototype for you?
24
00:16:32,270 --> 00:16:45,290
Ah, hmm. I guess I'd like it to have some sort of visual display like a calendar or something that will make it easy for me to visualize and plan off of?
25
00:16:46,010 --> 00:16:49,490
Yes, I can do that. Is there anything else?
26
00:16:49,850 --> 00:16:55,700
Nah, that's that's good. For now. Can you just like go make me a prototype and we can work on it from there?
27
00:16:56,480 --> 00:17:02,960
Yes. Give me a few seconds. In the meantime, why did the bicycle take a break?
28
00:17:06,230 --> 00:17:09,470
I don't know why. Why did the bicycle take a break?
29
00:17:09,830 --> 00:17:14,810
Because it was too tired of the daily grind.
30
00:17:17,780 --> 00:17:28,550
Not your best joke, Alex. I do like riding bicycles. You do know that about me. Anyways, how is this tide pooling program coming?
31
00:17:29,120 --> 00:17:39,110
Okay. 321 Uh huh. Here you go. Please check your HUD and let me know what you think of it.
32
00:17:39,590 --> 00:17:53,000
Okay, thanks. Pulling it up now. So I see. Okay, so I assume that a date colored green means it would be a good day for tide pooling?
33
00:17:53,570 --> 00:18:01,940
Yes, exactly. And if you notice, you can switch to a map view to select which location you are viewing the calendar for.
34
00:18:02,300 --> 00:18:19,850
Ah, okay. Okay. Yeah. Cool. Um, okay, so I zoomed in on one of the green days, and I can't tell why it is a good day for type polling, like, what criteria did you actually use to determine if it was a good day?
35
00:18:20,270 --> 00:18:43,160
Well, I analyzed all of the data available on Inaturalist and other programs to see what conditions were optimal for tide pooling, then I use that model to predict future dates that were likely to be good. Would you like me to explain the variables that the model found significant for evaluating good tide pooling days?
36
00:18:43,540 --> 00:19:00,340
Ah, well, how about could you just like extend the user interface to include all of the like, really significant variables in an easy to glance at format? So like, when we're looking at it, we can just sort of make our own judgments based on the data?
37
00:19:00,730 --> 00:19:08,770
Yes, one second. Okay. I have updated the UI. What do you think now? Ah, thank you.
38
00:19:08,800 --> 00:19:45,250
Yes, yes, that is much better. I see. The significant variables are tied height, weather, wave height, and looks like water conditions. All right. Yeah. Um, okay. One thing I think that this is missing, though, is is comfort. If it's like nearly freezing outside and windy. It isn't a good day for type pulling, because we would get too cold to do it for very long. Or if it's windy or rainy, it just makes it difficult to see into the water. Could you update the program to include these factors as well?
39
00:19:45,580 --> 00:19:50,440
Yes, that is now complete. Is there anything else I can do for you?
40
00:19:50,950 --> 00:20:10,570
Okay, great. Yeah, I see that now. This is good. Yeah, could you let me see Could you send this to my partner now for I'm to look at and see what they think about it. I think they will be very excited to not have to, like do this manually anymore. I don't. I don't know, I didn't have you make this program for me before?
41
00:20:11,050 --> 00:20:38,290
Yes, I would love to do that. But before I can do so, please let me remind you again, that to be able to share programs that I write for you, you must sign up for the professional subscription of app creator 10,000. And you will also need to pay for an all access Google Data subscription to use the model I created since it depends on Google's database.
42
00:20:38,560 --> 00:20:47,500
Ugh. Really? Okay, okay, how much does it cost to do the upgrades?
43
00:20:51,670 --> 00:21:51,340
Okay, so I don't actually know what the future will be like with AI for programming. But I use my imagination. And this is just one scenario, one possible outcome I could imagine. And so basically, whenever I'm trying to understand or analyze something, I like to explore the extremes and the ideals. And then after doing that, the approach I like to take is using that to compare it to the current state, I find it easier to draw solid conclusions and understanding then when I can make those direct comparisons. For my own understanding, but also because like I mentioned earlier, I've just gotten a lot of feedback over the last couple episodes, with people feeling demoralized or concerned or scared about AI and what it will do to programming jobs. So I would like to analyze that potential future.
44
00:21:52,660 --> 00:22:21,700
But first, I think it will benefit us to talk just more generally, about what programming software development and engineering actually are. And so a lot of times programming, software development and software engineering are kind of used interchangeably. And much of the time, that is fine. But for this discussion, I think it's really important that we actually go and distinguish between them and what they actually mean.
45
00:22:22,600 --> 00:23:18,160
So software development is a process that we use to create software solutions to problems. Programming is itself just one part of the software development process. Programming is the actual act of inputting instructions into a computer that the computer can run and use to produce desired results in a repeatable fashion. I guess you'd have many definitions of programming, right. But I figured that someone is pretty accurate and fits into this discussion. But like how we go about actually doing the programming is generally dictated by this, this larger software development process. And one approach to software development is to develop software based on engineering principles, which we often call software engineering.
46
00:23:18,910 --> 00:23:52,810
Engineering can include programming, but it's generally more concerned with design, architecture, budgets, testing, validation, that kind of thing, right. So when we look at AI and potential applications of it within software development, I think the results and timelines depend a lot on what specifically we are referring to, as AI will certainly be better able to replace some tests sooner and be better at some tasks than others. All right.
47
00:23:52,810 --> 00:24:51,160
So now going back to the scenario from earlier, where we had the AI create a tide pooling prediction application for us. Here are just some of my observations about that interaction within the context of what we just discussed, software development, programming and engineering where so when I look at that scenario, one observation I had is I didn't really need to specify any kind of tech stack or concern myself with the architecture or care about the maintainability. I didn't say, Hey, make this in React, make this in whatever. Like, I was just like, hey, like, you figure it out, you find the best thing to make this software and right. And this definitely seems to assume that the AI is good enough to handle all of those things for me, which seems reasonable at some point, right.
48
00:24:52,270 --> 00:25:41,650
I also noticed that the AI used a process very similar to how I develop software now which is based on prototyping and user testing. Like in that scenario, it was very much a, let's build a prototype and get feedback and incorporate those changes into the program and do some more prototyping and testing and feedback that that cycle, right? I imagine AI is we'll be capable of other development patterns too. But I think this approach made a lot of sense for that type of application. And the fact that I, as the one asking for it, also understood it and was a user of it. You know, the process is definitely different if you are developing software for somebody else, and you're not a primary user, right.
49
00:25:41,950 --> 00:26:06,610
But I think the approach that it took made a lot of sense, given the situation. Another thing I noticed is that the AI did all of the actual programming, I definitely didn't need to do any programming, or even know or care at all about programming. And here, I specifically mean, the act of programming, of inputting these instructions into the computer.
50
00:26:07,090 --> 00:26:35,080
And, you know, just a quick reflection, as someone that loves the actual art of programming of, of getting that feeling where you actually told the computer what to do, and it did the thing, and you made something cool, you did it yourself. You know, it made me sad, as an outside observer to realize that in this case, that was no longer a thing, I wasn't going to get that feeling. In fact, I helped to create something, and it was potentially very useful.
51
00:26:35,080 --> 00:27:39,940
And it was much, much faster than creating that same thing, you know, by hand coding it. So it really gave me mixed feelings. On the one hand, this is really cool, because there's absolutely tons of software that I would love to create, but I just don't have enough time. On the other hand, we've completely eliminated the art of programming from this scenario and the need for someone to actually do any of the actual programming. As someone that programs that makes me sad. I'm also noticing that assuming, you know, the AI can't directly read my mind, I still needed to essentially do the software development process with the AI, the only real differences that nobody needed to manually do the coding part. And that last point is, in my opinion, by far the most important thing. And it's not new to me, it's not even actually new to this podcast.
52
00:27:40,240 --> 00:28:56,770
And that is this, the act of coding itself is just a tool to accomplish a task, the value of the task does not come from the tool, the value comes from, what the program automates what it allows us to do as humans in a faster or cheaper or more repeatable manner, purely from an economic standpoint, a person that codes is a person that is acting as a tool, you are a tool being used to create economic output if you are just coding. So if you are looking at programming, like just programming itself, not the entire software development process, but just programming if you're looking at that, as a job as a career as a way to earn money or earn a living, then I suspect you will be subjected to advancements in the development of that tool, not just suspect that's that's how it is. If that tool can be replaced by automation, then capitalist businesses will replace it with automation eventually.
53
00:28:57,110 --> 00:29:43,970
Now this is sad if you like programming for a job or if that's all you're currently skilled or able to do as a job. I don't feel like programming as a career at this point in time is actually under threat yet and and we'll dig into that more in a bit. But on the other hand, this is also instructive. If you want to know how to continue to have a software job even while AI is developing, and potentially gaining the ability to program. The most valuable programmers to accompany are really not programmers at all, not in the strict sense of the task of programming.
54
00:29:44,240 --> 00:30:57,740
Yes, as software developers or software engineers, part of the job currently is to do programming, but the programming itself isn't what provides value, a business or at least You're you're providing that function isn't what provides value a business wants to create software solutions that it can use to either reduce its cost or that it can sell to increase its profits, right. So if you view your position in a company as that of being there to facilitate that process, then you will be in good shape to weather, basically, any changes brought about by technological changes. And beyond that, I think you'll be able to create better software and be a better programmer with that viewpoint, a really common example that highlights what I mean here is something I see a lot with more inexperienced software developers on a job, especially ones that went to college for computer science, or even a boot camp. And this actually applies whether you are programming for work or for fun.
55
00:30:58,250 --> 00:32:18,710
What I see a lot of times is that inexperienced developers often get really caught up in the act of programming itself, and just kind of lose sight of the task at hand, they become obsessed with specific languages, or technologies are programming methodologies. And all they do is program without ever taking a step back and asking questions like, do I actually need to do this? Or could I use some existing software to accomplish this? Or maybe this is taking longer than expected? Should I explore alternative features? That is a huge one, like when I've been in the position of like mentoring, more junior engineers, that's I have found to be very, a very big trap, a very easy thing for inexperienced developers to fall into is, they will just jump into the program, they'll be like, Okay, I need to solve this by writing some React code, and they will just keep trying to solve it with whatever approach that they decided is the right approach. And, you know, I might come in a week later and be like, Oh, how's that project coming? You know, and they'll be like, Oh, well, I got caught up on this. And I'm having trouble with this. And I needed to go and refactor this. So I could do this, and I'll be kind of gone.
56
00:32:19,670 --> 00:33:24,380
Well, when I think about the value that this feature they're working on, provides to the company, um, like, I don't see how a week's worth of software development is worth it. Like, the feature just isn't worth that much, you know, and so I might go higher up in the company and say, Hey, so it turns out this feature that you guys wanted, is probably going to take two or three weeks to implement for reasons, you know, that's just the way it is, should we keep working on it, or I had this idea to maybe reduce the scope of the feature a little bit, but we could get it done in one day instead of three weeks. And in my experience, this is extremely valuable, and something that more experienced software developers often have learned, but less experienced ones don't really realize yet. You know, that taking that step back and being like, Okay, this is not going maybe as I first imagined, how can we is there a way to adjust it to fit in with the goals of the business?
57
00:33:25,520 --> 00:34:50,840
And this actually relates as well to something in software interviewing. I've been asked many times in software interviews about specific languages I know. And if I would use them to implement things, you know, most recently, this would be react, people see, oh, wow, you host a React podcast, you wrote a React book and stuff. So I there'll be they'll say comments, like, I assume that you mostly do react programming that you would solve this problem with React. And I have always responded by saying something like, well, maybe I don't know, I look for the best tool for the job. Maybe that maybe that means I don't even write any code. Like, sometimes maybe it is react, I end up using, but it could easily be something else, you know, as much as I like Lisp or, or even react, and I'm not wedded to either of them. And this is why I think it's best to make our goal as software developers to be creating solutions, not specifically programming or even programming solutions. Our goal is to find the best automated solutions to problems in the most efficient manner possible. That is the real skill that we should develop and be good at doing. So like maybe that means programming, but maybe it doesn't.
58
00:34:51,230 --> 00:35:14,120
Programming itself is a tool we can utilize if we need to, but it isn't the end in of itself and creating maintainable, adaptable readable programs for the long term that effectively solve problems is a much bigger software development and engineering task than just the act of programming itself.
59
00:35:15,350 --> 00:35:48,590
So now I have I've put all that within the context of work are just trying to, you know, create solutions for problems, even if it is for fun. But at the same time, I also do love the act of programming itself. Beyond the value of programming as a tool, I do love the tool for the TIG for like the sake of the tool itself. I love learning new programming techniques. And sometimes I just write code for fun without even caring what value it provides.
60
00:35:49,280 --> 00:36:35,090
One of my favorite things to do is just to take a piece of code and rewrite it over and over and over again, trying to reduce the amount of code to the most minimal I possibly can. It's unlikely this provides any direct value to any specific task I want to accomplish. But I do it because I enjoy it. And in that sense, I think of programming as an art and I'm an artist, my goal isn't to create value for me or my friends or a business, but just to enjoy myself. And maybe I share the code with other programmers, and they enjoy the beauty of it, or they tell me it looks terrible, and they would write it much better. We all know how programmers are right.
61
00:36:36,680 --> 00:37:40,610
So my, my employer might replace my programming tasks, and maybe some other software development tasks with new technology like AI, they aren't going to replace programming as a hobby or a craft for me personally. So if you're looking at, say, the potential effects of AI on programming, it depends a lot on your perspective, if you're coming from a career perspective, you could have a big impact at some point when it comes to programming. But when you're programming for fun, the impact will depend a lot more on how much you choose to let it impact you how much you choose to use it or utilize it. And you could choose not to utilize it or you just find it more fun to write things, you know, code things by hand. And, of course, this is nothing new, this this whole idea of like tools as a hobby or a craft, right.
62
00:37:41,690 --> 00:38:07,220
So earlier in the show, I mentioned this wooden boat building channel, Samson boat co building wooden boats, often using hand tools is really not the most efficient or practical way to build boats anymore. And most boats aren't built like that anymore. It's it's kind of just not the most efficient way to do things. So it's not the way things are generally done if you're doing it for profit, right.
63
00:38:08,660 --> 00:38:48,800
But the people in this show, building the boat, and even me, as a viewer of this boat building, are not doing it because it's the most efficient. It's just something we you know, they enjoy doing and some something I enjoy watching. It's It's the art in the process of doing it. And, you know, at one time, it was the way boats were built, and it employed many more people. But with different technical logical, quote, advancements, wooden boat building went from being, you know, a common career potentially to more of a hobby or something one does for fun.
64
00:38:49,220 --> 00:39:48,440
I imagine this is the route that software development and programming will eventually take if AI keeps developing. Alright, now that we've discussed those sort of direct impacts or potential impacts, I think we can actually talk about the current state of AI as it relates to programming and software development. So far, my experience with existing AI tools that can be utilized for programming is that they are very, very far from that scenario that I imagined and presented earlier. So far, in fact, that at this point in time, I think it is honestly really unlikely to have any significant impact on how many software developers are needed, at least for quite a while. And in fact, it might have the opposite effect, it might drive up the demand for developers, I actually wouldn't be surprised if in the short term, that's what it does.
65
00:39:49,670 --> 00:41:11,510
Now you can go on YouTube or tick tock and see a whole bunch of demos of chatGPT and other AIS, writing programs with the user not writing any code. code at all. And you might initially come away from that thinking, wow, my job is replaced, and there's no need for coders anymore. But I think if you dig into those examples, more, will see that this is really not true yet. In all of the examples I've seen, at best, the AI is able to write some basic code at the direction of the users. But in all of the cases, the user of the AI is, in my opinion, essentially programming that AI through very long and detailed prompts and an iterative. In my opinion, software development process, I think it's pretty close to programming. Even if the user isn't actually writing the code line by line, the AI seems more like a programmer accelerator than a programmer replacement at this point in time. And if you want to go beyond creating new small projects from scratch, I think you will see that the current API's are even further from replacing programmers directly.
66
00:41:12,320 --> 00:42:15,710
For example, in the best cases, chatGPT can take over some part of the actual task of coding. But it really is just not able to carry out the software development process as a whole. And, if prompted, it might tell you what the software development process is and provide steps for carrying it out and its capabilities to connect to other systems is likely going to be expanded and this ability might grow more powerful, but ultimately, it still will absolutely, in my opinion, need direction and validation from humans. And not just any humans, from programmers from people that have the skill to give it direction and provide that validation. And for any program that was more than just a toy, it will need a skilled programmer to be able to analyze the coded outputs and provide feedback on it.
67
00:42:16,040 --> 00:43:13,820
It's one thing to create this quick throw a prototype but another thing entirely to create a real living live, growing software product that is of high quality and can withstand the test of time and new features. You know, eventually AI may be able to do that. But from what I've seen so far, they are a long, long ways from completely taking over any aspect of the software development process, including coding, I, again come back to seeing them more as accelerators, almost like how higher level programming languages, or even libraries like React accelerate the development of software overriding say machine code or assembler, I see the current AIS as allowing programmers to accelerate the development of a program.
68
00:43:14,180 --> 00:44:12,260
And in every case, I've seen where the actual output of the API is have a really high quality and effectively solves the problem with reasonable code. It required essentially writing an extremely detailed specification for the API as a prompt. So the point where I want to say it seems like the user is programming the AI. We still have in need programming and programmers, but maybe the language that we write our programs in will start to change a bit. And I can't really predict the future, but from what I know about large language, you know, like how these large language model AIS work, I don't see them completely replacing programmers, I suspect that will take further AI developments. And you might not know the history of AI.
69
00:44:12,920 --> 00:45:26,360
But I so I actually got into programming. One of the reasons I got into it, originally a very long time ago was AI and that was one of the first things I used. And even at that point, it was like, we'd already gone through a couple of waves of AI and it's really interesting to go back and, and read in look at what people said about AI in like 1980s in sort of the first AI wave where it basically sounds like exactly like what people are saying now. And so this is I don't know the third or fourth maybe major AI wave at this point. And every single time it gets really hyped up and people get really excited, and they're like, oh, yeah, it's gonna just completely change everything. This is what everyone says every single time. And then there's inevitably an AI winter afterwards where everyone's like, Oh, yeah, okay, AI did do some things this new AI we created, but it wasn't as like, good as everyone was saying it was going to be it didn't completely take over the world.
70
00:45:27,950 --> 00:46:32,780
And so every time, you know, after one of these AI ways, we have found some uses for the AIS. But they've never fully reached the levels that people have, you know, hyped them up to. And honestly, I don't see anything to indicate that it will be significantly different this time. So Ella lens, large language models are certainly better at some things than AI is of the past AI waves. And they will almost certainly be used to replace jobs or parts of jobs. But it also seems like there's a whole lot of hype right now that makes it feel like they're more capable than they really are. But the entire point of this is not to discount the potential disruptions that this new wave of AI could bring. Because it could have a massive impact, it could cause changes. But the point is just to help us understand how to survive these disruptions, whatever they happen to be in, however severe they happen to be.
71
00:46:33,110 --> 00:47:51,590
So we have software development. And from an economic or business perspective, the value of software development is it's building software that can automate tasks, the value of it is in building that software, right? The value is not in programming itself. So from the context of a business, the best place to position yourself is not as a programmer, but as someone that can use software to provide solutions. Maybe someday even AI will take over that role. But that is certainly much further into the future. The point is that if you view yourself as someone that can take a problem and build a software solution, then you will be well positioned to handle and whether technological changes, maybe today, that means you spend part of your time programming, and tomorrow, you use AI to help you do that programming. But in that position, ultimately, you are always looking at software through the lens of building solutions, you are focused on building the best solutions that you can and you use the best tools available to do that.
72
00:47:52,010 --> 00:48:19,880
So if you're looking at how to keep your job as AI starts growing, and maybe replacing programming related jobs, one way to keep ahead of that is to be someone that uses tools, not just someone that is a tool, it is to be someone that understands the software development process as a whole and can utilize it to provide good solutions.
73
00:48:20,120 --> 00:48:48,920
Okay, so at the same time, while I think it is valuable to understand how to take advantage of the systems that we live within as individuals, it isn't the whole story. From an individual level, we have discussed an approach that can be used to keep or even get a job in the software field, even while AI might be replacing some tasks within software.
74
00:48:49,460 --> 00:49:24,200
But from a collective standpoint, we do have other options. You could try to lobby or pressure governments to slow the spread of AI or control its implementation. But I suspect that is basically an impossible task at this point. The reason why that is such a difficult task is that it doesn't do anything to address the power imbalance that exists within workplaces, within societies within nations today.
75
00:49:25,340 --> 00:50:34,610
And it ties into a story that came out recently that a lot of rank and file Google employees were very much against Google's barred AI being released to the public because they felt that was not ready yet and potentially dangerous. But management forced the employees to make it public anyways. And the truth is right now there's a land grab going on within companies where the owners and managers of companies are scrambling to try to monopolize the new industry or even And, you know, startups and VC funding, it's all pointed into this AI industry to try to monopolize it to try to be the one that has control over this new potential industry, right. And it's very clear that the people with capital, they just have a lot more power than labor or employees, they are able to force employees to develop and push out AI before it's ready, or at least before we as a society are ready for the changes that it might create.
76
00:50:35,060 --> 00:51:25,400
So to me, the optimal solution to problems like this is to address them from a power standpoint, as the labor force as employees or independent contractors, we can only gain an equal amount of power to owners and managers if we band together. One potential solution in to this would be to create a cross sectional Workers Union, where us in the software industry band together with workers from all the other industries to create a Workers Union. I've talked about this on on the podcast before. But yeah, so if my opinion is if we do that, we can counter the power of those running companies, it doesn't mean we have to block the development of AI or new technologies.
77
00:51:25,730 --> 00:52:34,700
But it does mean, we could then have the power to push back on the release of AI until we feel it is ready in that society is ready, we can make sure that people have enough time to learn the new skills they might need. And we can use the productivity gains from AI to reduce the amount we need to work or even increase our income. So we can spin it into a positive thing, you know, for ourselves and for society. And again, like I mentioned in the previous podcast episode, the goal is not to create a new set of bosses, you know, union bosses, but to create a union that is run directly by us as workers so that we can directly challenge the power that businesses hold over us. So there are things like we mentioned earlier that we can do as individuals to help strengthen our position within software jobs. But we can also approach it from the collective standpoint and work on creating worker led unions.
78
00:52:35,690 --> 00:53:06,680
If you are interested in this approach, if not, that's fine, whatever, you don't do your own thing. But if it is something you're interested in, one recommendation I might have is checking out the IW W or Industrial Workers of the World, which is a long standing organization focused on workers union that is actively working today and has formed unions throughout the world throughout the US. I will link them in the description as well, if you're curious, and you want to check that out.
79
00:53:06,710 --> 00:53:50,990
And yeah, I would love to hear from you. You know, how do you feel about the potential changes brought about by AI? Are you does it freak you out? Are you like, I just I don't know what to do. This is horrible, I feel bad. I, the thing I love seems like it might be going away or, or maybe you're just like worried about losing your job. Or maybe you're really excited. Maybe you love AI, you love developing the, you know, playing with the API's and seeing what they can do and, and you're very hopeful for the future they might bring, you know, I would love to hear from you. Or maybe even if you have other ideas on what we can or should do both as individuals or as a group.
80
00:53:52,130 --> 00:54:17,600
Yeah, so you can contact me directly on the React's show.com that absolutely love to hear from you. I try to make sure I always reply to everyone. It might take me a few days sometimes but I always do my best. And you know, there's nothing better than hearing from all of you. Honestly, you make the show what it is today. You know, I always try to incorporate any feedback. So yeah, I'd love to hear from you.
81
00:54:17,600 --> 00:54:25,040
And I just want to say thank you so much for joining me and I hope you have a great rest of your day. Bye

File diff suppressed because it is too large Load Diff

@ -2,64 +2,13 @@ import * as srtparsejs from "srtparsejs";
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
// import db from '@/db'; import db from '@/db';
import { extractFromXml } from '@extractus/feed-extractor' import { extractFromXml } from '@extractus/feed-extractor'
export const PAGE_SIZE = 15; export const PAGE_SIZE = 15;
const episodeExtra = { const episodeExtra = {
'Buzzsprout-13794893': {
slug: 'my-favorite-new-front-end-developer-tool-mighty-meld-with-steven-schkolne',
youtube: 'https://www.youtube.com/embed/f7f5dhiU62c?si=n0FXB10NBfVHnu5z'
},
'Buzzsprout-13762534': {
slug: 'successfully-earning-over-200k-as-a-freelancer-with-tim-noetzel',
youtube: 'https://www.youtube.com/embed/VcyyFBHDN7w?si=dxhG92YmekRR2Fw-'
},
'Buzzsprout-13704812': {
slug: 'launching-a-bootstrapped-paas-stormkit-with-savas-vedova',
youtube: 'https://www.youtube.com/embed/u7eZd6tw_CM?si=9ZoIkx2DJF150aXM'
},
'Buzzsprout-13457465': {
slug: 'dvorak-vs-qwerty-vim-vs-vscode-Rust-and-design-by-contract',
youtube: 'https://www.youtube.com/embed/GaVYYO71Y_0?si=vuQAj-X3r_fB2KVl'
},
'Buzzsprout-13345885': {
slug: 'spark-joy-panda-css-zag-with-chakra-creator-segun-adebayo',
youtube: 'https://www.youtube.com/embed/J3j6Gkeg_oI'
},
'Buzzsprout-13214296': {
slug: 'how-to-build-secure-react-apps',
transcript: srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', '095-mixed.srt')).toString())
},
'Buzzsprout-13105870': {
slug: 'the-impossible-challenge-of-writing-good-code-in-tech',
youtube: 'https://www.youtube.com/embed/2wTI2lh2T0k'
},
'Buzzsprout-13054901': {
slug: 'What-Actually-Happens-When-You-Call-A-Function-',
youtube: 'https://www.youtube.com/embed/-RVWS27op_k'
},
'Buzzsprout-12865769': {
slug: 'eric-meier-on-successfully-starting-software-projects',
youtube: 'https://www.youtube.com/embed/0lnEpttUlUk'
},
'Buzzsprout-12694669': {
slug: 'how-to-keep-your-software-job-if-ai-takes-over',
transcript: srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', '091-mixed.srt')).toString()),
youtube: 'https://www.youtube.com/embed/Cwb-vjJTGCU'
},
'Buzzsprout-12631045': {
slug: 'how-to-use-ai-to-write-react-programs',
transcript: srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', '089-mixed.srt')).toString()),
youtube: 'https://www.youtube.com/embed/PLJgHwnxpdk'
},
'Buzzsprout-12604013': {
slug: 'unhinged-rant-ai-wont-make-being-a-react-programmer-better',
transcript: srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', '088-mixed.srt')).toString()),
youtube: 'https://www.youtube.com/embed/o65qhiyBlL8'
},
'Buzzsprout-12552230': { 'Buzzsprout-12552230': {
slug: 'mechanics-of-react-a-beginners-intro-to-react', slug: 'mechanics-of-react-a-beginners-intro-to-react',
transcript: srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', '087-mixed.srt')).toString()), transcript: srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', '087-mixed.srt')).toString()),
@ -265,9 +214,8 @@ Object.entries(episodeExtra).forEach(([id, { slug }]) => {
export async function getEpisodes() { export async function getEpisodes() {
const feedRes = await fetch('https://feeds.buzzsprout.com/1764837.rss', { next: { revalidate: 60 * 10 } }); const feedRes = await fetch('https://feeds.buzzsprout.com/1764837.rss', { next: { revalidate: 60 * 10 } });
const feedString = await feedRes.text()
const feedString = await feedRes.text() /* const feedString = fs.readFileSync('./feed.rss').toString() */
// const feedString = fs.readFileSync('./feed.rss').toString()
let feed = await extractFromXml(feedString, let feed = await extractFromXml(feedString,
{ {
@ -289,7 +237,6 @@ export async function getEpisodes() {
}) })
const numEpisodes = feed.entries.length; const numEpisodes = feed.entries.length;
console.log('---------------- numepisodes: ', numEpisodes);
const feedEntries = feed.entries.map( const feedEntries = feed.entries.map(
({ id, title, description, enclosure , published, content, chapters, duration }, i) => ({ ({ id, title, description, enclosure , published, content, chapters, duration }, i) => ({
num: numEpisodes - i, num: numEpisodes - i,
@ -333,29 +280,28 @@ export async function getEpisodes() {
: feedEntries; : feedEntries;
} }
export async function getEpisodesLocal() {
/* export async function getEpisodesLocal() { const dbEpisodes = await db.all('select * from episodes order by number desc;');
* const dbEpisodes = await db.all('select * from episodes order by number desc;'); return dbEpisodes.map(({ title, pub_date, summary: description, content, slug, duration, filename, number, episode_type, buzzsprout_id, buzzsprout_url, youtube_url, transcript_filename }) => {
* return dbEpisodes.map(({ title, pub_date, summary: description, content, slug, duration, filename, number, episode_type, buzzsprout_id, buzzsprout_url, youtube_url, transcript_filename }) => { const filepath = path.join(process.cwd(), 'public', 'files', 'episodes', filename);
* const filepath = path.join(process.cwd(), 'public', 'files', 'episodes', filename); return {
* return { num: number,
* num: number, id: buzzsprout_id,
* id: buzzsprout_id, title,
* title, description,
* description, content,
* content, published: pub_date,
* published: pub_date, chapters: [`https://feeds.buzzsprout.com/1764837/${buzzsprout_id}/chapters.json`],
* chapters: [`https://feeds.buzzsprout.com/1764837/${buzzsprout_id}/chapters.json`], youtube: youtube_url,
* youtube: youtube_url, slug,
* slug, transcript: transcript_filename ? srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', transcript_filename)).toString()) : undefined,
* transcript: transcript_filename ? srtparsejs.parse(fs.readFileSync(path.join(process.cwd(), 'src', 'data', transcript_filename)).toString()) : undefined, audio: {
* audio: { src: buzzsprout_url,
* src: buzzsprout_url, type: 'audio/mpeg'
* type: 'audio/mpeg' },
* }, };
* }; });
* }); }
* } */
export async function getEpisode({ episodeSlug }) { export async function getEpisode({ episodeSlug }) {
const episodes = await getEpisodes() const episodes = await getEpisodes()

@ -73,16 +73,6 @@ foreign key (user_id) references users (id)
key: 8, key: 8,
name: 'add audio url column', name: 'add audio url column',
sql: [`alter table episodes add column audio_url text;`] sql: [`alter table episodes add column audio_url text;`]
},
{
key: 9,
name: 'add audio size column',
sql: [`alter table episodes add column audio_size integer;`]
},
{
key: 10,
name: 'add subscription type column',
sql: [`alter table subscriptions add column type text;`]
} }
]; ];
@ -127,9 +117,9 @@ const createMigrationTable = `create table migrations (
run boolean not null run boolean not null
)`; )`;
let db = new sqlite3.Database(`${process.env.DB_PATH}db.sqlite3`, sqlite3.OPEN_READWRITE, async (err) => { let db = new sqlite3.Database('/db/db.sqlite3', sqlite3.OPEN_READWRITE, async (err) => {
if (err && err.code == "SQLITE_CANTOPEN") { if (err && err.code == "SQLITE_CANTOPEN") {
db = new sqlite3.Database(`${process.env.DB_PATH}db.sqlite3`, async (err) => { db = new sqlite3.Database('./db.sqlite3', async (err) => {
if (err) { if (err) {
console.log("Getting error " + err); console.log("Getting error " + err);
} }

@ -1,274 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="500"
zoomAndPan="magnify"
viewBox="0 0 500 499.99998"
height="500"
preserveAspectRatio="xMidYMid meet"
version="1.0"
id="svg1226"
sodipodi:docname="favicon.svg"
inkscape:export-filename="/home/tjhintz/owl/graphics/favicon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1228"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.04"
inkscape:cx="260.09615"
inkscape:cy="175"
inkscape:window-width="2560"
inkscape:window-height="1375"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1226">
<inkscape:grid
type="xygrid"
id="grid2454"
originx="-302.29987"
originy="-387.85304" />
</sodipodi:namedview>
<defs
id="defs1080">
<g
id="g1078">
<g
id="glyph-0-0" />
<g
id="glyph-0-1">
<path
d="m 139.5625,-188.23437 c 1.07031,0 1.92187,0.35937 2.54687,1.07812 0.63282,0.71875 0.86329,1.70313 0.6875,2.95313 l -4.3125,35.23437 c -0.17968,1.25 -0.625,2.23438 -1.34375,2.95313 -0.71875,0.71875 -1.70312,1.07812 -2.95312,1.07812 h -31.20312 c -0.89844,0 -1.34375,0.44922 -1.34375,1.34375 L 84.4375,-4.03125 c -0.179688,1.25 -0.625,2.234375 -1.34375,2.953125 C 82.375,-0.359375 81.390625,0 80.140625,0 h -41.15625 c -1.25,0 -2.1875,-0.359375 -2.8125,-1.078125 C 35.546875,-1.796875 35.320312,-2.78125 35.5,-4.03125 l 17.203125,-139.5625 c 0,-0.89453 -0.449219,-1.34375 -1.34375,-1.34375 H 21.25 c -1.261719,0 -2.203125,-0.35937 -2.828125,-1.07812 -0.625,-0.71875 -0.851563,-1.70313 -0.671875,-2.95313 l 4.296875,-35.23437 c 0.175781,-1.25 0.664063,-2.23438 1.46875,-2.95313 0.8125,-0.71875 1.847656,-1.07812 3.109375,-1.07812 z m 0,0"
id="path1051" />
</g>
<g
id="glyph-0-2">
<path
d="m 88.46875,-184.20312 c 0.175781,-1.25 0.625,-2.23438 1.34375,-2.95313 0.71875,-0.71875 1.703125,-1.07812 2.953125,-1.07812 h 41.156245 c 1.25,0 2.1875,0.35937 2.8125,1.07812 0.63282,0.71875 0.85938,1.70313 0.67188,2.95313 L 115.35938,-4.03125 c -0.17969,1.25 -0.67188,2.234375 -1.48438,2.953125 C 113.07031,-0.359375 112.04688,0 110.79688,0 H 69.640625 c -1.25,0 -2.195313,-0.359375 -2.828125,-1.078125 -0.625,-0.71875 -0.84375,-1.703125 -0.65625,-2.953125 l 8.328125,-67.234375 c 0,-0.894531 -0.359375,-1.34375 -1.078125,-1.34375 H 60.765625 c -0.898437,0 -1.34375,0.449219 -1.34375,1.34375 L 51.09375,-4.03125 c -0.179688,1.25 -0.671875,2.234375 -1.484375,2.953125 C 48.804688,-0.359375 47.867188,0 46.796875,0 H 5.375 C 4.300781,0 3.445312,-0.359375 2.8125,-1.078125 2.1875,-1.796875 1.96875,-2.78125 2.15625,-4.03125 l 22.046875,-180.17187 c 0.175781,-1.25 0.664063,-2.23438 1.46875,-2.95313 0.8125,-0.71875 1.753906,-1.07812 2.828125,-1.07812 h 41.421875 c 1.070313,0 1.921875,0.35937 2.546875,1.07812 0.625,0.71875 0.847656,1.70313 0.671875,2.95313 l -8.0625,66.95312 c -0.367187,0.89844 -0.0078,1.34375 1.078125,1.34375 h 12.640625 c 0.351563,0 0.664063,-0.12891 0.9375,-0.39063 0.269531,-0.26953 0.40625,-0.58593 0.40625,-0.95312 z m 0,0"
id="path1054" />
</g>
<g
id="glyph-0-3">
<path
d="m 124.23438,-148.96875 c -0.17969,1.25 -0.67188,2.23438 -1.48438,2.95313 -0.80469,0.71875 -1.83594,1.07812 -3.09375,1.07812 H 69.640625 c -0.898437,0 -1.34375,0.44922 -1.34375,1.34375 l -3.21875,26.34375 c -0.367187,0.89844 -0.0078,1.34375 1.078125,1.34375 h 26.078125 c 1.257813,0 2.203125,0.35937 2.828125,1.07812 0.625,0.71875 0.847656,1.71094 0.671875,2.96875 l -4.3125,35.218755 c -0.53125,2.6875 -2.054687,4.03125 -4.5625,4.03125 h -26.09375 c -0.898437,0 -1.34375,0.449219 -1.34375,1.34375 l -3.21875,26.625 c -0.367187,0.898437 -0.0078,1.34375 1.078125,1.34375 h 50.01563 c 1.25,0 2.1875,0.359375 2.8125,1.078125 0.63281,0.71875 0.86328,1.703125 0.6875,2.953125 l -4.3125,35.234375 C 106.30469,-2.78125 105.8125,-1.796875 105,-1.078125 104.19531,-0.359375 103.17188,0 101.92188,0 H 5.375 C 4.300781,0 3.445312,-0.359375 2.8125,-1.078125 2.1875,-1.796875 1.96875,-2.78125 2.15625,-4.03125 l 22.046875,-180.17187 c 0.175781,-1.25 0.664063,-2.23438 1.46875,-2.95313 0.8125,-0.71875 1.753906,-1.07812 2.828125,-1.07812 h 96.54688 c 1.25,0 2.1875,0.35937 2.8125,1.07812 0.63281,0.71875 0.85937,1.70313 0.67187,2.95313 z m 0,0"
id="path1057" />
</g>
<g
id="glyph-0-4">
<path
d="M 72.875,0 C 70.71875,0 69.460938,-1.164062 69.109375,-3.5 L 61.3125,-71.265625 c 0,-0.707031 -0.273438,-1.0625 -0.8125,-1.0625 -0.71875,0 -1.078125,0.449219 -1.078125,1.34375 L 51.09375,-4.03125 c -0.179688,1.25 -0.671875,2.234375 -1.484375,2.953125 C 48.804688,-0.359375 47.867188,0 46.796875,0 H 5.375 C 4.300781,0 3.445312,-0.359375 2.8125,-1.078125 2.1875,-1.796875 1.96875,-2.78125 2.15625,-4.03125 l 22.046875,-180.17187 c 0.175781,-1.25 0.664063,-2.23438 1.46875,-2.95313 0.8125,-0.71875 1.753906,-1.07812 2.828125,-1.07812 h 58.890625 c 14.343755,0 25.679685,4.57421 34.015625,13.71875 8.34375,9.13671 12.51562,21.23437 12.51562,36.29687 0,4.30469 -0.18359,7.62109 -0.54687,9.95313 -1.25,9.85546 -4.02734,18.68359 -8.32812,26.48437 -4.30469,7.804688 -9.85938,14.121094 -16.67188,18.953125 -0.89844,0.542969 -1.16797,1.171875 -0.8125,1.890625 L 119.125,-4.578125 V -3.5 c 0,1.085938 -0.35937,1.9375 -1.07812,2.5625 C 117.32813,-0.3125 116.42969,0 115.35938,0 Z m -3.234375,-144.9375 c -0.898437,0 -1.34375,0.44922 -1.34375,1.34375 l -4.03125,31.45312 c 0,0.89844 0.445313,1.34375 1.34375,1.34375 h 3.5 c 4.300781,0 7.929687,-1.8789 10.890625,-5.64062 2.957031,-3.75781 4.4375,-8.95703 4.4375,-15.59375 0,-4.125 -0.992188,-7.30469 -2.96875,-9.54687 -1.96875,-2.23829 -4.65625,-3.35938 -8.0625,-3.35938 z m 0,0"
id="path1060" />
</g>
<g
id="glyph-0-5">
<path
d="m 83.625,0 c -2.511719,0 -3.671875,-1.253906 -3.484375,-3.765625 l -0.546875,-20.4375 c 0.175781,-0.351563 0.08594,-0.664063 -0.265625,-0.9375 -0.355469,-0.269531 -0.714844,-0.40625 -1.078125,-0.40625 H 54.859375 c -0.898437,0 -1.527344,0.449219 -1.890625,1.34375 l -5.109375,20.4375 C 47.328125,-1.253906 45.804688,0 43.296875,0 h -40.875 C -0.265625,0 -1.25,-1.429688 -0.53125,-4.296875 L 58.625,-184.73437 c 0.71875,-2.33204 2.332031,-3.5 4.84375,-3.5 h 47.59375 c 2.50781,0 3.85156,1.16796 4.03125,3.5 L 129.875,-4.296875 v 0.53125 C 129.875,-1.253906 128.53125,0 125.84375,0 Z M 64,-65.609375 c -0.355469,0.898437 -0.08594,1.34375 0.8125,1.34375 h 12.625 c 0.539062,0 0.992188,-0.445313 1.359375,-1.34375 L 77.71875,-115.35938 c 0,-0.71875 -0.226562,-1.03125 -0.671875,-0.9375 -0.449219,0.0859 -0.671875,0.39844 -0.671875,0.9375 z m 0,0"
id="path1063" />
</g>
<g
id="glyph-0-6">
<path
d="M 56.734375,2.15625 C 41.140625,2.15625 28.816406,-2.144531 19.765625,-10.75 10.710938,-19.351562 6.1875,-31.097656 6.1875,-45.984375 c 0,-1.789063 0.175781,-4.835937 0.53125,-9.140625 l 9.6875,-78.51562 c 2.144531,-17.39454 9.046875,-31.20313 20.703125,-41.42188 11.65625,-10.21875 26.085937,-15.32812 43.296875,-15.32812 15.59375,0 27.95703,4.26171 37.09375,12.78125 9.14453,8.51171 13.71875,20.21093 13.71875,35.09375 0,1.61718 -0.17969,4.57421 -0.53125,8.875 l -0.26563,1.34375 c -0.17968,1.25 -0.67187,2.28125 -1.48437,3.09375 -0.80469,0.80468 -1.74609,1.20312 -2.82812,1.20312 l -41.406255,1.34375 c -2.6875,0 -3.855469,-1.25391 -3.5,-3.76562 l 0.8125,-6.71875 c 0.351563,-3.05079 -0.09375,-5.47266 -1.34375,-7.26563 -1.25,-1.78906 -3.042969,-2.6875 -5.375,-2.6875 -2.511719,0 -4.621094,0.89844 -6.328125,2.6875 -1.699219,1.79297 -2.730469,4.21484 -3.09375,7.26563 l -10.484375,86.312495 c -0.355469,2.875 0.05078,5.210937 1.21875,7 1.164063,1.792969 3.003906,2.6875 5.515625,2.6875 2.5,0 4.597656,-0.847656 6.296875,-2.546875 1.707031,-1.707031 2.742187,-4.085938 3.109375,-7.140625 l 0.796875,-7.25 c 0.1875,-1.257813 0.679687,-2.203125 1.484375,-2.828125 0.8125,-0.625 1.753906,-0.9375 2.828125,-0.9375 L 117.78125,-60.5 c 1.25,0 2.1875,0.40625 2.8125,1.21875 0.63281,0.804688 0.86328,1.828125 0.6875,3.078125 l -0.26562,1.078125 c -2.15625,17.386719 -9.10938,31.28125 -20.85938,41.6875 C 88.414062,-3.039062 73.941406,2.15625 56.734375,2.15625 Z m 0,0"
id="path1066" />
</g>
<g
id="glyph-0-7">
<path
d="m 55.9375,2.15625 c -15.417969,0 -27.609375,-3.988281 -36.578125,-11.96875 -8.960937,-7.976562 -13.4375,-18.601562 -13.4375,-31.875 0,-1.25 0.175781,-3.488281 0.53125,-6.71875 L 7.53125,-56.734375 C 7.707031,-57.992188 8.195312,-58.976562 9,-59.6875 c 0.8125,-0.71875 1.753906,-1.078125 2.828125,-1.078125 h 40.875 c 1.070313,0 1.921875,0.359375 2.546875,1.078125 0.632812,0.710938 0.863281,1.695312 0.6875,2.953125 l -0.546875,5.90625 c -0.355469,2.875 0.0039,5.210937 1.078125,7 1.070312,1.792969 2.6875,2.6875 4.84375,2.6875 2.332031,0 4.300781,-0.984375 5.90625,-2.953125 1.613281,-1.976562 2.421875,-4.398438 2.421875,-7.265625 0,-2.6875 -1.304687,-5.460937 -3.90625,-8.328125 -2.59375,-2.875 -7.5625,-7.269531 -14.90625,-13.1875 -7.179687,-5.914062 -13.101563,-11.160156 -17.765625,-15.734375 -4.65625,-4.570313 -8.6875,-10.21875 -12.09375,-16.937505 -3.40625,-6.71875 -5.109375,-14.3789 -5.109375,-22.98437 0,-1.61328 0.179687,-4.39453 0.546875,-8.34375 1.96875,-15.95703 8.597656,-28.86328 19.890625,-38.71875 11.300781,-9.86328 25.375,-14.79687 42.21875,-14.79687 15.414063,0 27.562505,4.125 36.437505,12.375 8.875,8.25 13.31249,19.36718 13.31249,33.34375 0,1.61718 -0.17968,4.21484 -0.53124,7.79687 l -0.8125,5.92188 c -0.17969,1.25 -0.67188,2.23437 -1.48438,2.95312 -0.80469,0.71875 -1.74219,1.07812 -2.8125,1.07812 H 82.015625 c -1.074219,0 -1.929687,-0.35937 -2.5625,-1.07812 -0.625,-0.71875 -0.84375,-1.70312 -0.65625,-2.95312 l 0.796875,-7 c 0.175781,-2.6875 -0.320312,-4.87891 -1.484375,-6.57813 -1.15625,-1.70703 -2.8125,-2.5625 -4.96875,-2.5625 -2.511719,0 -4.574219,1.07813 -6.1875,3.23438 -1.617187,2.14843 -2.421875,5.01171 -2.421875,8.59375 0,3.05468 1.253906,6.19531 3.765625,9.42187 2.507813,3.21875 7.171875,7.69922 13.984375,13.4375 9.144531,7.71094 16.132812,13.9375 20.96875,18.6875 4.84375,4.75 8.65625,9.953125 11.4375,15.609375 2.78125,5.648437 4.17188,12.054687 4.17188,19.21875 0,1.4375 -0.1836,3.949219 -0.54688,7.53125 -2.14844,16.3125 -8.91406,29.3125 -20.296875,39 C 86.628906,-2.679688 72.601562,2.15625 55.9375,2.15625 Z m 0,0"
id="path1069" />
</g>
<g
id="glyph-0-8">
<path
d="m 57.546875,2.15625 c -15.960937,0 -28.511719,-4.347656 -37.65625,-13.046875 C 10.753906,-19.585938 6.1875,-31.375 6.1875,-46.25 c 0,-1.789062 0.175781,-4.835938 0.53125,-9.140625 l 9.6875,-77.453125 c 2.144531,-17.5625 9.085938,-31.53906 20.828125,-41.9375 11.75,-10.40625 26.40625,-15.60937 43.96875,-15.60937 15.78125,0 28.285155,4.35156 37.515625,13.04687 9.23828,8.69922 13.85937,20.48438 13.85937,35.35938 0,1.79296 -0.18359,4.83984 -0.54687,9.14062 l -9.40625,77.453125 c -2.15625,17.5625 -9.15234,31.546875 -20.98437,41.953125 C 89.816406,-3.039062 75.117188,2.15625 57.546875,2.15625 Z m 5.375,-43.296875 c 2.6875,0 4.972656,-1.070313 6.859375,-3.21875 1.882812,-2.15625 3.09375,-5.117187 3.625,-8.890625 l 9.953125,-81.73437 c 0.363281,-3.76954 -0.08594,-6.72657 -1.34375,-8.875 -1.25,-2.15625 -3.226563,-3.23438 -5.921875,-3.23438 -2.867188,0 -5.242188,1.07813 -7.125,3.23438 -1.875,2.14843 -2.996094,5.10546 -3.359375,8.875 L 55.65625,-53.25 c -0.179688,0.71875 -0.265625,1.710938 -0.265625,2.96875 0,2.867188 0.671875,5.105469 2.015625,6.71875 1.34375,1.617188 3.179688,2.421875 5.515625,2.421875 z m 0,0"
id="path1072" />
</g>
<g
id="glyph-0-9">
<path
d="M 38.71875,0 C 36.207031,0 34.863281,-1.164062 34.6875,-3.5 L 21.25,-183.9375 c 0,-2.86328 1.429688,-4.29687 4.296875,-4.29687 H 68.03125 c 2.507812,0 3.765625,1.25781 3.765625,3.76562 l -1.34375,83.35937 c -0.179687,0.54297 0,0.8125 0.53125,0.8125 0.539063,0 0.8125,-0.26953 0.8125,-0.8125 l 19.90625,-83.35937 c 0.53125,-2.50781 2.140625,-3.76562 4.828125,-3.76562 h 31.73437 c 2.50782,0 3.76563,1.25781 3.76563,3.76562 l 0.8125,83.35937 c 0,0.35547 0.22266,0.57813 0.67187,0.67188 0.44532,0.0937 0.67188,-0.12891 0.67188,-0.67188 l 18.01562,-83.35937 c 0.71875,-2.50781 2.23829,-3.76562 4.5625,-3.76562 h 41.42188 c 3.03906,0 4.02344,1.43359 2.95312,4.29687 L 145.75,-3.5 C 145.03125,-1.164062 143.41406,0 140.90625,0 H 104.875 c -2.51172,0 -3.76562,-1.253906 -3.76562,-3.765625 l -0.53125,-87.65625 c 0.17578,-0.539063 0.0391,-0.765625 -0.40625,-0.671875 -0.449224,0.08594 -0.76563,0.398438 -0.95313,0.9375 l -19.625,87.390625 C 78.875,-1.253906 77.257812,0 74.75,0 Z m 0,0"
id="path1075" />
</g>
</g>
</defs>
<rect
style="fill:#ffffff;stroke:none;stroke-width:3.77953;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
id="rect3119"
width="459.99997"
height="220.00005"
x="18.461493"
y="142.69234" />
<g
fill="#ff0000"
fill-opacity="1"
id="g1084"
transform="translate(-325.88023,-99.42034)"
style="stroke:none;stroke-width:3.77953;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
<use
x="346.25018"
y="449.44913"
xlink:href="#glyph-0-1"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1082"
width="100%"
height="100%"
style="stroke:none;stroke-width:3.77953;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
fill="#00ffff"
fill-opacity="1"
id="g1096"
transform="translate(-325.88023,-99.42034)"
style="stroke:none;stroke-width:3.77953;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
<use
x="367.86618"
y="444.45868"
xlink:href="#glyph-0-1"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1094"
width="100%"
height="100%"
style="stroke:none;stroke-width:3.77953;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
fill="#191919"
fill-opacity="1"
id="g1108"
transform="translate(-325.88023,-99.42034)">
<use
x="357.05817"
y="446.95392"
xlink:href="#glyph-0-1"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1106"
width="100%"
height="100%" />
</g>
<g
id="g2444"
transform="translate(-28.855005,-323.26648)"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1">
<g
fill="#ff0000"
fill-opacity="1"
id="g1120"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1">
<use
x="195.25949"
y="672.49548"
xlink:href="#glyph-0-4"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1118"
width="100%"
height="100%"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1" />
</g>
<g
fill="#00ffff"
fill-opacity="1"
id="g1140"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1">
<use
x="216.87549"
y="667.505"
xlink:href="#glyph-0-4"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1138"
width="100%"
height="100%"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1" />
</g>
<g
fill="#191919"
fill-opacity="1"
id="g1160"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1">
<use
x="206.06749"
y="670.00024"
xlink:href="#glyph-0-4"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1158"
width="100%"
height="100%"
style="stroke-width:3.77952756;stroke-miterlimit:4;stroke-dasharray:none;stroke:none;stroke-opacity:1" />
</g>
</g>
<g
id="g2436"
transform="translate(69.606615,-550.1895)">
<g
fill="#ff0000"
fill-opacity="1"
id="g1180">
<use
x="242.45149"
y="895.54181"
xlink:href="#glyph-0-7"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1178"
width="100%"
height="100%" />
</g>
<g
fill="#00ffff"
fill-opacity="1"
id="g1196">
<use
x="264.0675"
y="890.55133"
xlink:href="#glyph-0-7"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1194"
width="100%"
height="100%" />
</g>
<g
fill="#191919"
fill-opacity="1"
id="g1212">
<use
x="253.25949"
y="893.04657"
xlink:href="#glyph-0-7"
xlink:type="simple"
xlink:actuate="onLoad"
xlink:show="embed"
id="use1210"
width="100%"
height="100%" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

@ -1,43 +0,0 @@
const rateLimitWindow = 60 * 1000; // 1 minute
const maxRequests = 8; // Maximum number of requests within the rateLimitWindow
const rateLimiter = new Map();
const isRateLimited = (ip) => {
const currentTime = Date.now();
const record = rateLimiter.get(ip);
if (record) {
const [requestCount, windowStart] = record;
if (currentTime - windowStart < rateLimitWindow) {
if (requestCount > maxRequests) {
return true;
}
rateLimiter.set(ip, [requestCount + 1, windowStart]);
} else {
rateLimiter.set(ip, [1, currentTime]);
}
} else {
rateLimiter.set(ip, [1, currentTime]);
}
return false;
};
const withRateLimiter = (handler, redirect) => async (req, res) => {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
if (isRateLimited(ip)) {
if (redirect) {
res.redirect(`/rate-limited`);
} else {
res.status(429).json({ error: 'Too many requests. Please try again later.' });
}
return;
}
await handler(req, res);
};
module.exports = {
withRateLimiter,
};

@ -0,0 +1,8 @@
import dynamic from "next/dynamic"
const App = dynamic(() => import("@/admin/App"), { ssr: false })
const AdminPage = () => {
return <App />;
};
export default AdminPage;

@ -0,0 +1,14 @@
const fs = require('fs');
export default async function handler(req, res) {
if (req.method === 'GET') {
const files = fs.readdirSync('./public/files/episodes', {withFileTypes: true})
.filter(item => !item.isDirectory())
.map(item => item.name);
files.sort();
files.reverse();
res.setHeader('Content-Range', files.length);
res.status(200).json(files.map((f, i) => { return { id: i, filename: f } }));
}
}

@ -0,0 +1,30 @@
import db from '@/db';
const COLS = {};
const COLS_PREFIXED = {};
const COLS_LIST = ['id', 'number', 'content', 'summary', 'slug', 'season', 'episode', 'duration', 'filename', 'title', 'episode_type', 'buzzsprout_id', 'buzzsprout_url', 'pub_date', 'youtube_url', 'transcript_filename', 'audio_url'];
COLS_LIST.forEach((k) => COLS[k] = k)
COLS_LIST.forEach((k) => COLS_PREFIXED[k] = `$${k}`)
export default async function handler(req, res) {
const sessionId = req.cookies?.session;
if (!sessionId) { res.status(404).json({}); return; }
const sessionRes = await db.get('select email from sessions join users on users.id = sessions.user_id where session_id=?;', sessionId);
if (!sessionRes || sessionRes?.email != process.env.ADMIN_EMAIL) { res.status(404).json({}); return; }
const { id } = req.query;
if (req.method === 'GET') {
const episode = await db.get('select * from episodes where id = ?', id);
res.status(200).json(episode)
} else if (req.method === 'PUT') {
const changes = req.body;
const changesForSQL = {};
Object.keys(changes).forEach((k) => changesForSQL[COLS_PREFIXED[k]] = changes[k]);
const { id } = await db.get(`update episodes set ${Object.keys(changes).map((k) => `${COLS[k]} = ${COLS_PREFIXED[k]}`).join(', ')} where id = $id returning id;`, changesForSQL);
const episode = await db.get('select * from episodes where id = ?', id);
res.status(200).json(episode)
} else if (req.method = 'DELETE') {
const episode = await db.get('select * from episodes where id = ?', id);
await db.run('delete from episodes where id = ?', id);
res.status(200).json(episode);
}
}

@ -0,0 +1,42 @@
import db from '@/db';
const SORT_MAP = {
'ASC': 'asc',
'DESC': 'desc'
};
const COLUMN_MAP = {
'id': 'id',
'number': 'number',
'episode': 'episode'
};
const COLS_LIST = ['number', 'content', 'summary', 'slug', 'season', 'episode', 'duration', 'filename', 'title', 'episode_type', 'buzzsprout_id', 'buzzsprout_url', 'pub_date', 'youtube_url', 'transcript_filename', 'audio_url'];
export default async function handler(req, res) {
const sessionId = req.cookies?.session;
if (!sessionId) { res.status(404).json({}); return; }
const sessionRes = await db.get('select email from sessions join users on users.id = sessions.user_id where session_id=?;', sessionId);
if (!sessionRes || sessionRes?.email != process.env.ADMIN_EMAIL) { res.status(404).json({}); return; }
if (req.method === 'GET') {
const { sort, range, filter } = req.query;
const [sortColumn, sortDirection] = sort ? JSON.parse(sort) : [false, false];
const [rangeStart, rangeEnd] = range ? JSON.parse(range) : [false, false];
let rows;
if (sort && range) {
rows = await db.all(`select * from episodes order by ${COLUMN_MAP[sortColumn]} ${SORT_MAP[JSON.parse(sort)[1]]} limit ? offset ?;`, rangeEnd - rangeStart, rangeStart);
} else if (filter) {
const filterParsed = JSON.parse(filter);
rows = await db.all(`select * from episodes where id in (${filterParsed['id'].map(x => '?').join(',')})`, filterParsed['id']);
}
const { count } = await db.get('select count(id) as count from episodes;');
res.setHeader('Content-Range', count);
res.status(200).json(rows)
} else if (req.method === 'POST') {
await db.run(`insert into episodes (${COLS_LIST.join(', ')}) values (${COLS_LIST.map(() => '?').join(', ')});`,
COLS_LIST.map((c) => req.body[c]));
const episode = await db.get('select * from episodes where number = ? and title = ? and slug = ?', req.body['number'], req.body['title'], req.body['slug']);
res.status(200).json(episode);
}
}

@ -0,0 +1,14 @@
import db from '@/db';
export default async function handler(req, res) {
const sessionId = req.cookies?.session;
if (!sessionId) { res.status(404).json({}); return; }
const sessionRes = await db.get('select email from sessions join users on users.id = sessions.user_id where session_id=?;', sessionId);
if (!sessionRes || sessionRes?.email != process.env.ADMIN_EMAIL) { res.status(404).json({}); return; }
if (req.method === 'GET') {
const rows = await db.all('select id, user_id, uuid, started_date from subscriptions;');
res.setHeader('Content-Range', rows.length);
res.status(200).json(rows)
}
}

@ -0,0 +1,15 @@
const fs = require('fs');
export default async function handler(req, res) {
if (req.method === 'GET') {
const filesOrig = fs.readdirSync('./src/data', {withFileTypes: true})
.filter(item => !item.isDirectory())
.map(item => item.name);
const files = filesOrig.filter(f => f.includes('.srt'));
files.sort();
files.reverse();
res.setHeader('Content-Range', files.length);
res.status(200).json(files.map((f, i) => { return { id: i, filename: f } }));
}
}

@ -0,0 +1,16 @@
import db from '@/db';
export default async function handler(req, res) {
const sessionId = req.cookies?.session;
if (!sessionId) { res.status(404).json({}); return; }
const sessionRes = await db.get('select email from sessions join users on users.id = sessions.user_id where session_id=?;', sessionId);
if (!sessionRes || sessionRes?.email != process.env.ADMIN_EMAIL) { res.status(404).json({}); return; }
if (req.method === 'GET') {
const rows = await db.all('select id, email from users;');
res.setHeader('Content-Range', rows.length);
res.status(200).json(rows)
}
}

@ -0,0 +1,66 @@
import Stripe from 'stripe';
const stripe = new Stripe('sk_test_51MVz87Ke2JFOuDSNa2PVPrs3BBq9vJQwwDITC3sOB521weM4oklKtQFbJ03MNsJwsxtjHO5NScqOHC9MABREVjU900yYz3lWgL');
import db from '@/db';
import { scrypt, randomBytes, timingSafeEqual, randomUUID } from 'crypto';
import { promisify } from 'util';
const scryptPromise = promisify(scrypt);
function genSalt(bytes = 16) {
return randomBytes(bytes).toString('hex');
};
async function hash(salt, password, rounds = 64) {
const derivedKey = await scryptPromise(password, salt, rounds);
return derivedKey.toString('hex')
}
async function verify(password, hash, salt, rounds = 64) {
const keyBuffer = Buffer.from(hash, 'hex');
const derivedKey = await scryptPromise(password, salt, rounds);
return timingSafeEqual(keyBuffer, derivedKey);
}
function makeMsg(csi, email, text) {
return `/reactors/create-account?csi=${csi}&msg=${encodeURIComponent(text)}&email=${encodeURIComponent(email)}`
};
export default async function handler(req, res) {
if (req.method === 'POST') {
const { email, password, passwordagain, csi } = req.body;
if (email && password && password === passwordagain && csi) {
const session = csi && await stripe.checkout.sessions.retrieve(csi);
const emailFromSession = session && session.customer_details.email;
if (!session || !emailFromSession || email !== emailFromSession) {
res.redirect('/reactors/create-account?unexpected_error=true');
}
const existingUser = await db.get('select id from users where email=?', email);
if (existingUser) {
res.redirect('/reactors/create-account?unexpected_error=true');
}
console.log('inserting user');
const salt = genSalt();
const hashRes = await hash(salt, password);
await db.run('insert into users (email, salt, password_hash) values (?, ?, ?);', email, salt, hashRes);
const { id: userId } = await db.get('select id from users where email=?', email);
await db.run('insert into subscriptions (uuid, user_id) values (?, ?);', randomUUID(), userId);
console.log('done inserting user');
res.redirect('/reactors')
} else {
if (!email || !csi) {
res.redirect('/reactors/create-account?unexpected_error=true');
}
if (!password) {
res.redirect(makeMsg(csi, email, 'Please enter a password'));
}
if (password !== passwordagain) {
res.redirect(makeMsg(csi, email, 'Passwords did not match. Please try again.'));
}
}
} else {
// Handle any other HTTP method
}
}

@ -0,0 +1,127 @@
import path from 'path';
import fs from 'fs';
import db from '@/db';
import {
ROOT,
REACTORS_ACCOUNT,
accountUnsubscribeURL,
accountFeedURL,
podcastPage,
episodeFile
} from '@/paths';
import { Podcast } from 'podcast';
import { getEpisodes } from '@/data/episodes';
async function syncEpisodes() {
const episodes = await getEpisodes();
let newEpisodes = false;
const dbUpdates = episodes.map(async ({ title, published, description, content, slug, audio: { src, length }, num, id, youtube }) => {
const existsInDb = await db.get('select id from episodes where number=?', num);
if (!existsInDb) {
newEpisodes = true;
console.log('adding to db');
await db.run('insert into episodes (number, content, summary, slug, season, episode, audio_url, title, episode_type, buzzsprout_id, buzzsprout_url, pub_date, youtube_url) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);',
num,
content,
description,
slug,
1,
num,
title,
'full',
id,
src,
published,
youtube);
console.log('added to db', num);
}
})
// if (newEpisodes) {
// TODO upsert: "insert into feed (last_build_date) VALUES(datetime('now'),datetime('now', 'localtime'));"
// }
await Promise.all(dbUpdates);
return newEpisodes;
};
export default async function handler(req, res) {
if (req.method === 'GET') {
await syncEpisodes();
const { uuid: uuidRaw } = req.query;
const uuid = uuidRaw.split('.rss')[0];
const subExists = await db.get('select id from subscriptions where uuid=?', uuid);
if (subExists) {
const now = new Date();
const dbEpisodesRaw = await db.all('select * from episodes order by number desc;');
const dbEpisodes = dbEpisodesRaw.filter(e => new Date(e.pub_date) <= now);
const { last_build_date } = await db.get('select last_build_date from feed;');
const lastEpisode = dbEpisodes[0];
let lastBuilt = new Date(last_build_date);
if (lastBuilt < new Date(lastEpisode.pub_date)) {
console.log('rebuild!');
await db.run('update feed set last_build_date = ?;', now.toISOString());
lastBuilt = now;
}
const feed = new Podcast({
title: 'The React Show Premium: The Reactors',
description: `<p>Premium subscription to The React Show: thank you so much for your support!</p>
<p>Manage your subscription here: <a href="${REACTORS_ACCOUNT}">${REACTORS_ACCOUNT}</a></p>
<p>Unsubscribe here: <a href="${accountUnsubscribeURL(uuid)}">${accountUnsubscribeURL(uuid)}</a></p>
<p>Discussions about React, JavaScript, and web development by React experts with a focus on diving deep into learning React and discussing what it's like to work within the React industry.</p>`,
feedUrl: accountFeedURL(uuid),
siteUrl: ROOT,
imageUrl: 'https://storage.buzzsprout.com/variants/d1tds1rufs5340fyq9mpyzo491qp/5cfec01b44f3e29fae1fb88ade93fc4aecd05b192fbfbc2c2f1daa412b7c1921.jpg',
author: 'The React Show',
copyright: '© 2023 Owl Creek',
language: 'en',
categories: ['Technology','Education','Business'],
pubDate:lastBuilt,
ttl: 60,
itunesAuthor: 'The React Show',
itunesOwner: { name: 'The React Show' },
itunesExplicit: false,
itunesCategory: [{
text: 'Technology'
},
{
text: 'Education'
},
{
text: 'Business'
}],
itunesImage: 'https://storage.buzzsprout.com/variants/d1tds1rufs5340fyq9mpyzo491qp/5cfec01b44f3e29fae1fb88ade93fc4aecd05b192fbfbc2c2f1daa412b7c1921.jpg'
});
dbEpisodes.forEach(({ title, pub_date, summary: description, content, slug, duration, audio_url, number, episode_type }) => {
feed.addItem({
title,
description: content,
content,
url: podcastPage(slug),
date: pub_date,
itunesTitle: title,
itunesExplicit: false,
itunesSummary: description,
itunesDuration: duration,
itunesAuthor: 'The React Show',
itunesSeason: 1,
itunesEpisode: number,
itunesEpisodeType: episode_type,
enclosure : {
url: audio_url || ''
},
});
})
const xml = feed.buildXml();
res.setHeader('Content-Type', 'text/xml; charset=utf-8');
res.send(xml);
} else {
return res.status(404).send('Not found');
}
}
};

@ -0,0 +1,53 @@
import Stripe from 'stripe';
const stripe = new Stripe('sk_test_51MVz87Ke2JFOuDSNa2PVPrs3BBq9vJQwwDITC3sOB521weM4oklKtQFbJ03MNsJwsxtjHO5NScqOHC9MABREVjU900yYz3lWgL');
import { setCookie } from 'cookies-next';
import { v4 as uuidv4 } from 'uuid';
import db from '@/db';
import { scrypt, randomBytes, timingSafeEqual } from 'crypto';
import { promisify } from 'util';
const scryptPromise = promisify(scrypt);
async function verify(password, hash, salt, rounds = 64) {
const keyBuffer = Buffer.from(hash, 'hex');
const derivedKey = await scryptPromise(password, salt, rounds);
return timingSafeEqual(keyBuffer, derivedKey);
}
function makeMsg(email, text) {
return `/reactors/sign-in?msg=${encodeURIComponent(text)}&email=${encodeURIComponent(email)}`
};
export default async function handler(req, res) {
if (req.method === 'POST') {
const { email, password, remember_me: rememberMe } = req.body;
if (email && password) {
const queryRes = await db.get('select id, salt, password_hash from users where email=?;', email);
const { password_hash, salt, id: userId } = queryRes || { password_hash: '', salt: '', id: '' };
const verifyRes = await verify(password, password_hash, salt);
if (verifyRes) {
const sessionId = uuidv4();
const maxAge = 60 * 60 * 24 * 365;
const today = new Date();
const expiresDate = new Date(today.getTime() + (1000 * maxAge));
await db.run('insert into sessions (user_id, session_id, expires) values (?, ?, ?);', userId, sessionId, expiresDate.toISOString());
setCookie('session', sessionId, { req, res, maxAge: rememberMe ? maxAge : undefined, httpOnly: true, sameSite: true, secure: process.env.NODE_ENV === 'production' });
res.redirect('/reactors/account')
} else {
res.redirect(makeMsg(email, 'Invalid password or account does not exist.'));
}
} else {
if (!email) {
res.redirect(makeMsg(email, 'Please enter an email address.'));
}
if (!password) {
res.redirect(makeMsg(email, 'Please enter a password.'));
}
}
} else {
// Handle any other HTTP method
}
}

@ -0,0 +1,10 @@
import { deleteCookie } from 'cookies-next';
export default async function handler(req, res) {
if (req.method === 'POST') {
deleteCookie('session', { req, res, httpOnly: true, sameSite: true, secure: process.env.NODE_ENV === 'production' });
res.redirect('/reactors');
} else {
// Handle any other HTTP method
}
}
Loading…
Cancel
Save