- Vamos começar criando o serviço userService e um método responsável por obter a lista de “Continuar Assitindo”, mas inicialmente vamos apenas obter todos os episódios que estão sendo assistidos pelo usuário. Adicione o código:
// src/services/userService.ts
import { User } from '../models'
import { UserCreationAttributes } from '../models/user'
export const userService = {
// ...
getKeepWatchingList: async (id: number) => {
const userWithWatchingEpisodes = await User.findByPk(id, {
include: {
association: 'Episodes',
attributes: [
'id',
'name',
'synopsis',
'order',
['video_url', 'videoUrl'],
['seconds_long', 'secondsLong'],
['course_id', 'courseId']
],
include: [{
association: 'Course',
attributes: [
'id',
'name',
'synopsis',
['thumbnail_url', 'thumbnailUrl']
],
as: 'course'
}],
through: {
as: 'watchTime',
attributes: [
'seconds',
['updated_at', 'updatedAt']
]
}
}
})
if (!userWithWatchingEpisodes) throw new Error('Usuário não encontrado.')
}
}
- Agora vamos criar uma função separada para filtrar a lista de episódios mantendo apenas o episódio mais recente de cada curso que está sendo assistido. Isso porque o usuário da aplicação pode passar para um episódio seguinte sem terminar o anterior, mas a lista de continuar assistindo vai sempre pegar o episódio mais avançado.
// src/services/userService.ts
import { User } from '../models'
import { EpisodeInstance } from '../models/episode'
import { UserCreationAttributes } from '../models/user'
function filterLastEpisodesByCourse(episodes: EpisodeInstance[]) {
const coursesOnList: number[] = []
const lastEpisodes = episodes.reduce((currentList, episode) => {
if (!coursesOnList.includes(episode.courseId)) {
coursesOnList.push(episode.courseId)
currentList.push(episode)
return currentList
}
const episodeFromSameCourse = currentList.find(ep => ep.courseId === episode.courseId)
if (episodeFromSameCourse!.order > episode.order) return currentList
const listWithoutEpisodeFromSameCourse = currentList.filter(ep => ep.courseId !== episode.courseId)
listWithoutEpisodeFromSameCourse.push(episode)
return listWithoutEpisodeFromSameCourse
}, [] as EpisodeInstance[])
return lastEpisodes
}
export const userService = {
// ...
- Agora podemos voltar ao método getKeepWatchingList e terminar de implementá-lo:
- Obs.: Repare que adicionamos uma anotação para que o typescript ignore qualquer verificação em uma única linha. Não é recomendado ficar utilizando esse tipo de anulação com frequência, mas aqui estamos usando apenas para facilitar o uso da propriedade updatedAt.
// src/services/profile-service.ts
// ...
getKeepWatchingList: async (id: number) => {
const userWithWatchingEpisodes = await User.findByPk(id, {
include: {
association: 'Episodes',
attributes: [
'id',
'name',
'synopsis',
'order',
['video_url', 'videoUrl'],
['seconds_long', 'secondsLong'],
['course_id', 'courseId']
],
include: [{
association: 'Course',
attributes: [
'id',
'name',
'synopsis',
['thumbnail_url', 'thumbnailUrl']
],
as: 'course'
}],
through: {
as: 'watchTime',
attributes: [
'seconds',
['updated_at', 'updatedAt']
]
}
}
})
if (!userWithWatchingEpisodes) throw new Error('Usuário não encontrado.')
const keepWatchingList = filterLastEpisodesByCourse(userWithWatchingEpisodes.Episodes!)
// @ts-ignore
keepWatchingList.sort((a, b) => a.watchTime.updatedAt < b.watchTime.updatedAt ? 1 : -1)
return keepWatchingList
}
}
- Com o método do serviço pronto, podemos criar o controlador e um método que irá cuidar da resposta do endpoint:
// src/controllers/usersController.ts
import { Response } from "express";
import { AuthenticatedRequest } from "../middlewares/auth";
import { userService } from "../services/userService";
export const usersController = {
// GET /users/current/watching
watching: async (req: AuthenticatedRequest, res: Response) => {
const { id } = req.user!
try {
const watching = await userService.getKeepWatchingList(id)
return res.json(watching)
} catch (err) {
if (err instanceof Error) {
return res.status(400).json({ message: err.message })
}
}
}
}
- Por último vamos adicionar a rota com o método do controlador:
// src/routes.ts
// ...
import { usersController } from './controllers/usersController'
// ...
router.get('/users/current/watching', ensureAuth, usersController.watching)
// ...