OurBigBook
web/back/UserPage.ts
import ourbigbook from 'ourbigbook'

import { getLoggedInUser } from 'back'
import { getServerSidePropsArticleHoc } from 'back/ArticlePage'
import { articleLimit  } from 'front/config'
import { getOrderAndPage } from 'front/js'
import { MyGetServerSideProps } from 'front/types'
import { UserPageProps } from 'front/UserPage'

export const getServerSidePropsUserHoc = (what): MyGetServerSideProps => {
  return async (context) => {
    const { params: { uid }, query, req, res } = context
    if (
      typeof uid === 'string'
    ) {
      const sequelize = req.sequelize
      const [loggedInUser, user] = await Promise.all([
        getLoggedInUser(req, res),
        sequelize.models.User.findOne({
          where: { username: uid },
        }),
      ])
      if (!user) {
        return {
          notFound: true
        }
      }
      const [order, pageNum, err] = getOrderAndPage(req, query.page)
      if (err) { res.statusCode = 422 }
      let author, articlesFollowedBy, likedBy, following, followedBy, itemType
      switch (what) {
        case 'follows':
          followedBy = uid
          itemType = 'user'
          break
        case 'followed':
          following = uid
          itemType = 'user'
          break
        case 'home':
          itemType = null
          break
        case 'liked':
          author = uid
          itemType = 'like'
          break
        case 'likes':
          likedBy = uid
          itemType = 'article'
          break
        case 'followed-articles':
          articlesFollowedBy = uid
          itemType = 'article'
          break
        case 'user-articles':
          author = uid
          itemType = 'article'
          break
        case 'user-issues':
          author = uid
          itemType = 'discussion'
          break
        default:
          throw new Error(`Unknown search: ${what}`)
      }
      const offset = pageNum * articleLimit
      const articlesPromise =
        itemType === 'article' ? sequelize.models.Article.getArticles({
          sequelize,
          limit: articleLimit,
          offset,
          order,
          author,
          likedBy,
          followedBy: articlesFollowedBy,
        }) :
        itemType === 'discussion' ? sequelize.models.Issue.getIssues({
          sequelize,
          limit: articleLimit,
          offset,
          order,
          author,
          includeArticle: true,
        }) :
        []
      const likesPromise = itemType === 'like' ? sequelize.models.User.findAndCountArticleLikesReceived(
        user.id, { offset, order }) : []
      const usersPromise = itemType === 'user' ? sequelize.models.User.getUsers({
        following,
        followedBy,
        limit: articleLimit,
        offset,
        order,
        sequelize,
      }) : []
      const updateNewScoreLastCheckPromise = (what === 'liked' && loggedInUser && user.id === loggedInUser.id) ?
        user.update({ newScoreLastCheck: Date.now() }) : null
      const [
        articles,
        userJson,
        loggedInUserJson,
        likes,
        users,
      ] = await Promise.all([
        articlesPromise,
        user.toJson(loggedInUser),
        loggedInUser ? loggedInUser.toJson() : undefined,
        likesPromise,
        usersPromise,
        updateNewScoreLastCheckPromise,
      ])
      const props: UserPageProps = {
        itemType,
        order,
        page: pageNum,
        user: userJson,
        what,
      }
      if (loggedInUser) {
        props.loggedInUser = loggedInUserJson
      }
      if (itemType === 'user') {
        props.users = await Promise.all(users.rows.map(user => user.toJson(loggedInUser)))
        props.usersCount = users.count
      } else if (itemType === 'article' || itemType === 'discussion') {
        props.articles = await Promise.all(articles.rows.map(article => article.toJson(loggedInUser)))
        props.articlesCount = articles.count
      } else if (itemType === 'like') {
        const articles = []
        for (const like of likes.rows) {
          const article = like.article
          article.likedBy = like.user
          article.likedByDate = like.createdAt
          articles.push(article)
        }
        props.articles = await Promise.all(articles.map(article => article.toJson(loggedInUser)))
        props.articlesCount = likes.count
      } else {
        const articleContext = Object.assign({}, context, { params: { slug: [ uid ] } })
        const articleProps = await (getServerSidePropsArticleHoc({
          includeIssues: true, loggedInUserCache: loggedInUser
        })(articleContext))
        if ('props' in articleProps) {
          Object.assign(props, articleProps.props)
        }
      }
      return { props }
    } else {
      throw new TypeError
    }
  }
}