19
loading...
This website collects cookies to deliver better user experience
yarn create redwood-app custom-video-player
api
and web
.api
folder holds all of the code for the GraphQL back-end and the Prisma model for the Postgres database. The web
folder holds all of the React front-end code. We'll be updating the code in these folders throughout this tutorial.yarn rw dev
.env
file, uncomment the DATABASE_URL
line and update it to match your Postgres instance. Here's an example of what mine looks like. Make sure you remember what your username and password are for your local Postgres server!DATABASE_URL=postgres://postgres:admin@localhost:5432/video_player
schema.prisma
file. We're going to create the model for the Setting
table we need to hold the user values. Redwood already generated an example model, so we can just swap out the names of everything. Update your file to look like this.datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Setting {
id Int @id @default(autoincrement())
videoName String @unique
loop Boolean
volume Float @default(0.5)
controls Boolean @default(false)
playbackRate Float @default(1.5)
}
Setting
model defines the settings we're saving for the user and the data types we expect for them. The last three options have default values so that a video will play whether or not the user picks any particular options.seed.js
file, we're going to add one row of data to the Setting
table. Update your file to look like this./* eslint-disable no-console */
const { PrismaClient } = require('@prisma/client')
const dotenv = require('dotenv')
dotenv.config()
const db = new PrismaClient()
async function main() {
console.warn('Please define your seed data.')
const record = await db.setting.create({
data: {
videoName: 'elephant_herd',
loop: false,
controls: true,
volume: 0.2,
playbackRate: 1.5,
},
})
console.log(record)
}
main()
.catch((e) => console.error(e))
.finally(async () => {
await db.$disconnect()
})
yarn rw prisma migrate dev
yarn rw g sdl setting
api > src > graphql
directory, you'll find all of the GraphQL types based on the Prisma schema you need to do some basic operations. Now look in the api > src > services
directory. There's a settings
folder that has the file for one resolver.settings.js
with the resolver, let's add a couple more resolvers to handle our front-end requests. The first resolver will get an individual setting based on the setting ID. The second resolver will be used to handle updates to the setting.settings
resolver in the file.export const setting = (input) => {
return db.setting.findFirst({
where: { id: input.id },
})
}
export const updateSetting = ({ input }) => {
console.log(`This is the input: + ${input.volume}`)
return db.setting.update({
where: { id: input.id },
data: {
loop: input.loop,
videoName: input.videoName,
controls: input.controls,
volume: input.volume,
playbackRate: input.playbackRate,
},
})
}
settings.sdl.js
file to have the matching schema for these new resolvers.type Mutation {
updateSetting(input: UpdateSettingInput): Setting
}
type Query {
setting(id: Int): Setting!
}
id
field to the UpdateSettingInput
type so that we're able to update based on the setting ID.input UpdateSettingInput {
id: Int
videoName: String
loop: Boolean
volume: Float
controls: Boolean
playbackRate: Float
}
yarn rw g page home /
web > src > pages
directory, you'll see a new HomePage
directory. This is where the home page we created with the previous command is located. We're going to make our video player in this file, but if you want to see what the app looks like in the browser now, run:yarn rw dev
web
directory. Once you're in the web
directory in your terminal, run the following command:yarn add styled-components react-player
styled-components
to add some simple styling to the video player and we're using react-player
as the video player component. Let's start by completely updating the Home
component.HomePage.js
file to have the following code.import {
Form,
Label,
TextField,
CheckboxField,
RangeField,
RadioField,
Submit,
} from '@redwoodjs/forms'
import { useMutation, useQuery } from '@redwoodjs/web'
import styled from 'styled-components'
import ReactPlayer from 'react-player'
const HomePage = () => {
return (
<Container>
<VideoPlayer>
<ReactPlayer
controls={true}
loop={false}
volume={0.5}
playbackRate={1}
url={`https://res.cloudinary.com/milecia/video/upload/c_pad,h_360,w_480,q_70,du_10/elephant_herd.mp4`}
></ReactPlayer>
</VideoPlayer>
<Form>
<FormContainer>
<Label name="videoName">Video Name</Label>
<TextField name="videoName" />
<Label name="loop">Loop</Label>
<CheckboxField name="loop" />
<Label name="controls">Controls</Label>
<CheckboxField name="controls" />
<Label name="volume">Volume</Label>
<RangeField name="volume" />
<Label name="playbackRate">1x</Label>
<RadioField name="playbackRate" value={1} />
<Label name="playbackRate">1.5x</Label>
<RadioField name="playbackRate" value={1.5} />
<Label name="playbackRate">2x</Label>
<RadioField name="playbackRate" value={2} />
<Submit>Save</Submit>
</FormContainer>
</Form>
</Container>
)
}
const Container = styled.div`
width: 100%;
`
const FormContainer = styled.div`
display: flex;
flex-direction: column;
margin: 0 auto;
padding-top: 25px;
width: 500px;
`
const VideoPlayer = styled.div`
display: block;
margin: 0 auto;
width: 50%;
`
export default HomePage
HomePage.js
file, we're going to add a new import to the others in order to create a query and mutation.import { useMutation, useQuery } from '@redwoodjs/web'
HomePage
component. This will create the methods for updating and retriving the user settings and create the onSubmit
method for the form. Since we seeded the database and we're only working with one user, I've hard-coded the setting ID as 1
. We even do some state handling for when the data is being fetched in the GraphQL query.const { loading, error, data } = useQuery(SETTING, { variables: { id: 1 } })
const [updateSetting] = useMutation(UPDATE_SETTING)
const onSubmit = (data) => {
updateSetting({
variables: {
id: 1,
videoName: data.videoName,
loop: data.loop,
controls: data.controls,
volume: Number(data.volume),
playbackRate: Number(data.playbackRate),
},
})
}
if (loading) {
return <div>Loading..</div>
}
if (error) {
return <div>{error.message}</div>
}
HomePage
component ends, add the following code.const SETTING = gql`
query Setting($id: Int) {
setting(id: $id) {
id
videoName
loop
controls
volume
playbackRate
}
}
`
const UPDATE_SETTING = gql`
mutation UpdateSetting(
$id: Int
$videoName: String
$loop: Boolean
$controls: Boolean
$volume: Float
$playbackRate: Float
) {
updateSetting(
input: {
id: $id
videoName: $videoName
loop: $loop
controls: $controls
volume: $volume
playbackRate: $playbackRate
}
) {
id
videoName
loop
controls
volume
playbackRate
}
}
`
videoName
we saved and we're adding defaultValue
attributes to all of the form fields to show the stored values.<VideoPlayer>
<ReactPlayer>
...
url={`https://res.cloudinary.com/milecia/video/upload/c_pad,h_360,w_480,q_70,du_10/${
data.setting.videoName || 'elephant_herd'
}.mp4`}
></ReactPlayer>
</VideoPlayer>
<Form onSubmit={onSubmit}>
<FormContainer>
<Label name="videoName">Video Name</Label>
<TextField name="videoName" defaultValue={data.setting.videoName} />
<Label name="loop">Loop</Label>
<CheckboxField name="loop" defaultValue={data.setting.loop} />
<Label name="controls">Controls</Label>
<CheckboxField name="controls" defaultValue={data.setting.controls} />
<Label name="volume">Volume</Label>
<RangeField name="volume" defaultValue={data.setting.volume} />
<Label name="playbackRate">1x</Label>
<RadioField
name="playbackRate"
defaultValue={data.setting.playbackRate}
value={1}
/>
<Label name="playbackRate">1.5x</Label>
<RadioField
name="playbackRate"
defaultValue={data.setting.playbackRate}
value={1.5}
/>
<Label name="playbackRate">2x</Label>
<RadioField
name="playbackRate"
defaultValue={data.setting.playbackRate}
value={2}
/>
<Submit>Save</Submit>
</FormContainer>
</Form>
url
value of the video player is how where this comes in. That string currently looks like this:url={`https://res.cloudinary.com/milecia/video/upload/c_pad,h_360,w_480,q_70,du_10/${
data.setting.videoName || 'elephant_herd'
}.mp4`}
url
string, replace milecia
with your cloud name and replace elephant_herd
with the name of one of your videos. Now when you run your Redwood app, you'll see your own video!custom-video-player
folder in this repo!