mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-25 10:03:07 +03:00
Added following other users.
This commit is contained in:
parent
cdb7484279
commit
804e3ef37d
@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import moment from 'moment'
|
||||
|
||||
import useAuth from '@wasp/auth/useAuth.js'
|
||||
import logout from '@wasp/auth/logout.js'
|
||||
@ -8,6 +9,7 @@ import getUser from '@wasp/queries/getUser'
|
||||
import getArticlesByUser from '@wasp/queries/getArticlesByUser'
|
||||
import getFavoritedArticles from '@wasp/queries/getFavoritedArticles'
|
||||
import setArticleFavorited from '@wasp/actions/setArticleFavorited'
|
||||
import followUser from '@wasp/actions/followUser'
|
||||
import { useQuery } from '@wasp/queries'
|
||||
|
||||
import Navbar from './Navbar'
|
||||
@ -16,6 +18,8 @@ import smileyImageUrl from './smiley.jpg'
|
||||
const UserProfilePage = (props) => {
|
||||
const history = useHistory()
|
||||
|
||||
const { data: me } = useAuth()
|
||||
|
||||
const username = props.match.params.username
|
||||
const { data: user, error: userError } = useQuery(getUser, { username })
|
||||
|
||||
@ -28,9 +32,6 @@ const UserProfilePage = (props) => {
|
||||
history.push("/")
|
||||
}
|
||||
|
||||
// TODO: List My Articles
|
||||
// TODO: List Favorited Articles
|
||||
|
||||
return user ? (
|
||||
<div>
|
||||
<Navbar />
|
||||
@ -38,26 +39,52 @@ const UserProfilePage = (props) => {
|
||||
<img src={user.profilePictureUrl || smileyImageUrl} />
|
||||
<p> { user.username } </p>
|
||||
<p> { user.bio } </p>
|
||||
<div>
|
||||
{ /* TODO: Show this link only if user is logged in. */ }
|
||||
<Link to='/settings'>Edit Profile Settings</Link>
|
||||
</div>
|
||||
{ me && me.username === username && (
|
||||
<div>
|
||||
<Link to='/settings'>Edit Profile Settings</Link>
|
||||
</div>
|
||||
)}
|
||||
{ me && me.username !== username && (
|
||||
<div>
|
||||
<FollowUserButton user={user} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Articles user={user} />
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
||||
const FollowUserButton = (props) => {
|
||||
const user = props.user
|
||||
const { data: me } = useAuth()
|
||||
|
||||
const toggleFollow = async () => {
|
||||
try {
|
||||
followUser({ username: user.username, follow: !user.following })
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
window.alert(err)
|
||||
}
|
||||
}
|
||||
|
||||
return me && me.username !== user.username ? (
|
||||
<button onClick={toggleFollow}>
|
||||
{ user.following ? 'Unfollow' : 'Follow' }
|
||||
</button>
|
||||
) : null
|
||||
}
|
||||
|
||||
const Articles = (props) => {
|
||||
const user = props.user
|
||||
|
||||
const { data: myArticles } = useQuery(getArticlesByUser, { username: props.user.username })
|
||||
const { data: authoredArticles } = useQuery(getArticlesByUser, { username: props.user.username })
|
||||
const { data: favoritedArticles } = useQuery(getFavoritedArticles, { username: props.user.username })
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1> My Articles </h1>
|
||||
<ArticleList articles={myArticles} />
|
||||
<ArticleList articles={authoredArticles} />
|
||||
<h1> Favorited Articles </h1>
|
||||
<ArticleList articles={favoritedArticles} />
|
||||
</div>
|
||||
@ -82,11 +109,20 @@ const Article = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ border: '1px solid black' }}>
|
||||
<Link to={`/article/${article.slug}`}>
|
||||
<h2> { article.title } </h2>
|
||||
</Link>
|
||||
<p> { article.description } </p>
|
||||
<p>
|
||||
<em> Tags: </em>
|
||||
{ article.tags.map(t => t.name).join('; ') }
|
||||
</p>
|
||||
<p>
|
||||
<img src={ article.user.profilePictureUrl || smileyImageUrl } width='30px' />
|
||||
<div> { article.user.username } </div>
|
||||
<div> { moment(article.createdAt).format('MMMM DD, YYYY') } </div>
|
||||
</p>
|
||||
<div>
|
||||
<button onClick={toggleArticleFavorited}>
|
||||
{ article.favorited ? 'Unlike' : 'Like' } ({ article.favoritesCount })
|
||||
|
@ -4,7 +4,6 @@ import slug from 'slug'
|
||||
|
||||
export const signup = async ({ username, email, password }, context) => {
|
||||
try {
|
||||
console.log('juhu')
|
||||
await createNewUser({ username, email, password })
|
||||
} catch (err) {
|
||||
// TODO: I wish I didn't have to do this, I would love this to be in some
|
||||
@ -114,6 +113,22 @@ export const setArticleFavorited = async ({ id, favorited }, context) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const followUser = async ({ username, follow }, context) => {
|
||||
if (!context.user) { throw new HttpError(403) }
|
||||
|
||||
await context.entities.User.update({
|
||||
where: { username },
|
||||
data: {
|
||||
followedBy: {
|
||||
...(follow === true ? { connect: { id: context.user.id } } :
|
||||
follow === false ? { disconnect: { id: context.user.id } } :
|
||||
{}
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const createComment = async ({ articleId, content }, context) => {
|
||||
if (!context.user) { throw new HttpError(403) }
|
||||
|
||||
|
@ -15,12 +15,24 @@ export const getUser = async ({ username }, context) => {
|
||||
// TODO: Tricky, if you forget this you could return unwanted fields
|
||||
// like hashed password!
|
||||
// It would be cool if we had some protection against making this mistake easily.
|
||||
select: userPublicSelection
|
||||
select: {
|
||||
...userPublicSelection,
|
||||
followedBy: { select: { id: true } }
|
||||
}
|
||||
})
|
||||
if (!user) throw new HttpError(404, 'No user with username ' + username)
|
||||
|
||||
userSetFollowedFields(user, context.user)
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
const userSetFollowedFields = (user, me) => {
|
||||
user.following = me && user.followedBy.find(({ id }) => id === me.id)
|
||||
user.followersCount = user.followedBy.length
|
||||
delete user.followedBy
|
||||
}
|
||||
|
||||
// TODO: I extracted this articleInclude and articleSetFavoritedFields to enable
|
||||
// reusing of logic that shapes articles as they come out of the server,
|
||||
// but I wonder if there is a more elegant way - here there are a lot of assumptions,
|
||||
|
@ -48,6 +48,8 @@ entity User {=psl
|
||||
articles Article[]
|
||||
comments Comment[]
|
||||
favoriteArticles Article[] @relation("FavoritedArticles")
|
||||
followedBy User[] @relation("FollowedUser", references: [id])
|
||||
following User[] @relation("FollowedUser", references: [id])
|
||||
psl=}
|
||||
|
||||
entity Article {=psl
|
||||
@ -103,6 +105,11 @@ query getUser {
|
||||
entities: [User]
|
||||
}
|
||||
|
||||
action followUser {
|
||||
fn: import { followUser } from "@ext/actions.js",
|
||||
entities: [User]
|
||||
}
|
||||
|
||||
query getArticlesByUser {
|
||||
fn: import { getArticlesByUser } from "@ext/queries.js",
|
||||
entities: [Article]
|
||||
|
@ -0,0 +1,48 @@
|
||||
# Migration `20201127145135-added-following-of-other-users`
|
||||
|
||||
This migration has been generated by Martin Sosic at 11/27/2020, 3:51:35 PM.
|
||||
You can check out the [state of the schema](./schema.prisma) after the migration.
|
||||
|
||||
## Database Steps
|
||||
|
||||
```sql
|
||||
CREATE TABLE "_FollowedUser" (
|
||||
"A" INTEGER NOT NULL,
|
||||
"B" INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY ("A") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)
|
||||
|
||||
CREATE UNIQUE INDEX "_FollowedUser_AB_unique" ON "_FollowedUser"("A", "B")
|
||||
|
||||
CREATE INDEX "_FollowedUser_B_index" ON "_FollowedUser"("B")
|
||||
```
|
||||
|
||||
## Changes
|
||||
|
||||
```diff
|
||||
diff --git schema.prisma schema.prisma
|
||||
migration 20201127131647-init..20201127145135-added-following-of-other-users
|
||||
--- datamodel.dml
|
||||
+++ datamodel.dml
|
||||
@@ -1,8 +1,8 @@
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
- url = "***"
|
||||
+ url = "***"
|
||||
}
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
@@ -19,8 +19,10 @@
|
||||
articles Article[]
|
||||
comments Comment[]
|
||||
favoriteArticles Article[] @relation("FavoritedArticles")
|
||||
+ followedBy User[] @relation("FollowedUser", references: [id])
|
||||
+ following User[] @relation("FollowedUser", references: [id])
|
||||
}
|
||||
model Article {
|
||||
id Int @id @default(autoincrement())
|
||||
```
|
||||
|
||||
|
@ -0,0 +1,59 @@
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "***"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../server/node_modules/.prisma/client"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
email String @unique
|
||||
password String
|
||||
bio String?
|
||||
profilePictureUrl String?
|
||||
|
||||
articles Article[]
|
||||
comments Comment[]
|
||||
favoriteArticles Article[] @relation("FavoritedArticles")
|
||||
followedBy User[] @relation("FollowedUser", references: [id])
|
||||
following User[] @relation("FollowedUser", references: [id])
|
||||
}
|
||||
|
||||
model Article {
|
||||
id Int @id @default(autoincrement())
|
||||
slug String @unique
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
title String
|
||||
description String
|
||||
markdownContent String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
comments Comment[]
|
||||
tags ArticleTag[]
|
||||
favoritedBy User[] @relation("FavoritedArticles")
|
||||
}
|
||||
|
||||
model Comment {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
content String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
article Article @relation(fields: [articleId], references: [id])
|
||||
articleId Int
|
||||
}
|
||||
|
||||
model ArticleTag {
|
||||
name String @id
|
||||
|
||||
articles Article[]
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
{
|
||||
"version": "0.3.14-fixed",
|
||||
"steps": [
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "followedBy",
|
||||
"type": "User",
|
||||
"arity": "List"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "followedBy"
|
||||
},
|
||||
"directive": "relation"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "followedBy"
|
||||
},
|
||||
"directive": "relation"
|
||||
},
|
||||
"argument": "",
|
||||
"value": "\"FollowedUser\""
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "followedBy"
|
||||
},
|
||||
"directive": "relation"
|
||||
},
|
||||
"argument": "references",
|
||||
"value": "[id]"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "following",
|
||||
"type": "User",
|
||||
"arity": "List"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "following"
|
||||
},
|
||||
"directive": "relation"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "following"
|
||||
},
|
||||
"directive": "relation"
|
||||
},
|
||||
"argument": "",
|
||||
"value": "\"FollowedUser\""
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "following"
|
||||
},
|
||||
"directive": "relation"
|
||||
},
|
||||
"argument": "references",
|
||||
"value": "[id]"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
# Prisma Migrate lockfile v1
|
||||
|
||||
20201127131647-init
|
||||
20201127131647-init
|
||||
20201127145135-added-following-of-other-users
|
Loading…
Reference in New Issue
Block a user