Skip to content
Open

mvp #16

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": false
}
15,057 changes: 15,057 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,25 @@
"private": true,
"dependencies": {
"@chakra-ui/core": "^0.4.1",
"@chakra-ui/react": "^1.1.2",
"@emotion/core": "^10.0.22",
"@emotion/styled": "^10.0.23",
"@emotion/react": "^11.1.4",
"@emotion/styled": "^10.0.27",
"@types/jest": "^26.0.19",
"@types/lodash": "^4.14.167",
"@types/node": "^14.14.20",
"@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.7",
"emotion-theming": "^10.0.19",
"framer-motion": "^3.1.4",
"localstoragedb": "^2.3.2",
"lodash": "^4.17.20",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-hook-form": "^6.14.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0"
"react-scripts": "3.2.0",
"typescript": "^4.1.3"
},
"scripts": {
"start": "react-scripts start",
Expand All @@ -33,5 +44,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react": "^17.0.0"
}
}
9 changes: 9 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import LoginWrapper from "./auth/LoginWrapper"
import LogoutPage from "./auth/LogoutPage"
import Frame from "./ui/Frame"
import Home from "./Home"
import EditProfile from "./EditProfile"
import Profile from "./Profile"

function App() {
return (
Expand All @@ -18,9 +20,16 @@ function App() {
<Route path="/logout">
<LogoutPage logout={logout} />
</Route>
<Route path="/edit-profile">
<EditProfile username={username} />
</Route>
<Route path="/profile/:username">
<Profile self={username} />
</Route>
<Route path="/">
<Home username={username} />
</Route>

</Switch>
</Frame>
)}
Expand Down
156 changes: 156 additions & 0 deletions src/EditProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import React, { useCallback, useEffect, useReducer } from "react"
import {
FormControl,
FormLabel,
Input,
Text,
FormErrorMessage,
Button,
Textarea,
Box,
Tag,
TagLabel,
TagCloseButton,
useToast,
} from "@chakra-ui/core"
import { uniq, castArray, without, split, trim, isEmpty } from "lodash"
import { useForm } from "react-hook-form"
import db from "./data/database"
import { Container, Wrap, WrapItem } from "@chakra-ui/react"
import IUser from "./data/user"

interface ProfileProps {
username: string
}

type ITags = Array<string>
type ITagAction = "add" | "remove" | "set"

const EditProfile: React.FC<ProfileProps> = ({ username }) => {
const { handleSubmit, errors, register, reset } = useForm({})
const [tags, modifyTags] = useReducer(
(state: ITags, action: { type: ITagAction; tag: string | string[] }) => {
switch (action.type) {
case "add":
return uniq([...state, ...castArray(action.tag)])
case "remove":
return without(state, ...castArray(action.tag))
case "set":
return castArray(action.tag)
default:
return state
}
},
[] as ITags
)
useEffect(() => {
const users = db.queryAll("users", { query: { username } }) as Array<IUser>
if (!users.length) {
db.insert("users", { username })
db.commit()
return
}
const user = users[0]
modifyTags({
type: "set",
tag: split(user.tags, ",")
.map(trim)
.filter((tag) => !isEmpty(tag)),
})
reset(user)
}, [username, reset])

const toast = useToast()

const onSubmit = useCallback(
(values: any) => {
db.update("users", { username }, (row: any) => ({
...row,
...values,
tags: tags.join(","),
}))
db.commit()
toast({
description: "Account updated.",
status: "success",
duration: 9000,
isClosable: true,
})
},
[toast, username, tags]
)

return (
<Container maxW="lg">
<Text fontSize="3xl" pb={4}>
Edit Profile
</Text>
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl id="email" isInvalid={errors.email} pb={4}>
<FormLabel>Email address</FormLabel>
<Input name="email" type="email" ref={register({ required: true })} />
<FormErrorMessage>
{errors.email && "Must be a valid email"}
</FormErrorMessage>
</FormControl>

<FormControl id="fullName" isInvalid={errors.fullName} pb={4}>
<FormLabel>Full name</FormLabel>
<Input
name="fullName"
type="text"
ref={register({ required: true })}
/>
<FormErrorMessage>
{errors.fullName && "Cannot be empty"}
</FormErrorMessage>
</FormControl>

<FormControl id="fullName" isInvalid={errors.fullName} pb={4}>
<FormLabel>Pitch</FormLabel>
<Textarea
name="pitch"
placeholder="A few words about your idea"
size="sm"
ref={register({ required: true })}
/>
<FormErrorMessage>
{errors.pitch && "Cannot be empty"}
</FormErrorMessage>
</FormControl>

<Box pb={4}>
<Text>Tags:</Text>
<Wrap direction={["column", "row"]} spacing="5px" pb={2}>
{tags.map((tag) => (
<WrapItem key={tag}>
<Tag size="md" variant="solid">
<TagLabel>{tag}</TagLabel>
<TagCloseButton
onClick={(e: React.FormEvent<HTMLButtonElement>) => {
modifyTags({ type: "remove", tag })
}}
/>
</Tag>
</WrapItem>
))}
</Wrap>
<Input
placeholder="Add more tags for people to find you"
type="text"
onKeyPress={(e: any) => {
if (e.key === "Enter") {
modifyTags({ type: "add", tag: e.target.value })
e.target.value = ""
e.preventDefault()
}
}}
/>
</Box>
<Button type="submit">Save</Button>
</form>
</Container>
)
}

export default EditProfile
12 changes: 7 additions & 5 deletions src/Home.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React from 'react'
import Card from './ui/Card'
import DataAccessDemo from './features/DemoDataAccess'
import Search from './Search'
import Wall from './Wall'

const Home = ({username}) => {
return <DataAccessDemo username={username} />

return <>
<Search />
<Wall username={username} />
</>


}

export default Home




28 changes: 28 additions & 0 deletions src/Posts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useEffect, useState } from "react"
import { Text } from "@chakra-ui/core"
import db from "./data/database"
import Card from "./ui/Card"
import Post from "./data/post"
import { VStack } from "@chakra-ui/react"
import { castArray } from "lodash"

const Posts: React.FC<{ username: string | string[] }> = ({ username }) => {
const [posts, setPosts] = useState<Post[]>([])
useEffect(() => {
const posts = db.queryAll("posts", {
query: (post: Post) => castArray(username).includes(post.user),
}) as Post[]
setPosts(posts)
}, [username])

if (!posts) return <Text>No posts yet</Text>
return (
<VStack spacing="5px" align="stretch" maxW="lg">
{posts.map((post, index) => (
<Card title={post.title} author={post.user} key={index}></Card>
))}
</VStack>
)
}

export default Posts
82 changes: 82 additions & 0 deletions src/Profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useCallback, useEffect, useState } from "react"
import { Text, Box, Tag, TagLabel, Button } from "@chakra-ui/core"
import { trim, first, isEmpty } from "lodash"
import db from "./data/database"
import { Container, Skeleton, VStack, Wrap, WrapItem } from "@chakra-ui/react"
import IUser from "./data/user"
import { useParams } from "react-router-dom"
import Posts from "./Posts"

const Profile: React.FC<{ self: string }> = ({ self }) => {
const { username } = useParams<{ username: string }>()
const [user, setUser] = useState<IUser>()
const [isFollowing, setIsFollowing] = useState<boolean>(false)
useEffect(() => {
const user = first(
db.queryAll("users", { query: { username } }) as Array<IUser>
)
setUser(user)
}, [username])

useEffect(() => {
const follower = db.queryAll("followers", {
query: {
user: username,
follower: self,
}
})
setIsFollowing(!isEmpty(follower))
}, [username, self])

const toggleFollowing = useCallback(() => {
if (!isFollowing) {
db.insert("followers", { user: username, follower: self })
} else {
db.deleteRows("followers", { user: username, follower: self })
}
db.commit()
setIsFollowing(!isFollowing)
}, [isFollowing, username, self])

if (!user) {
return (
<VStack>
<Skeleton height="20px" />
<Skeleton height="20px" />
<Skeleton height="20px" />
</VStack>
)
}
return (
<Container maxW="lg">
<Text fontSize="5xl" pb={4}>
{user?.fullName}
</Text>
<Text my={5} fontSize="2xl">
{user?.pitch}
</Text>
<VStack spacing="5px" align="stretch">
<Box>
<Wrap direction={["column", "row"]} spacing="5px" pb={2}>
{user.tags
.split(",")
.map(trim)
.map((tag) => (
<WrapItem key={tag}>
<Tag size="md" variant="solid">
<TagLabel>{tag}</TagLabel>
</Tag>
</WrapItem>
))}
</Wrap>
</Box>
</VStack>
<Button onClick={() => toggleFollowing()} my={10}>
{isFollowing ? "Unfollow" : "Follow"}
</Button>
<Posts username={user.username} />
</Container>
)
}

export default Profile
Loading