-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adds feeds as a sample for CRUD
- Loading branch information
1 parent
39b07ac
commit 4099cea
Showing
16 changed files
with
466 additions
and
225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
backend/migrations/1596135561298_create_table_public_feeds/down.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
DROP TABLE "public"."feeds"; |
18 changes: 18 additions & 0 deletions
18
backend/migrations/1596135561298_create_table_public_feeds/up.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
CREATE EXTENSION IF NOT EXISTS pgcrypto; | ||
CREATE TABLE "public"."feeds"("id" uuid NOT NULL DEFAULT gen_random_uuid(), "author_id" uuid NOT NULL, "body" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") ); | ||
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() | ||
RETURNS TRIGGER AS $$ | ||
DECLARE | ||
_new record; | ||
BEGIN | ||
_new := NEW; | ||
_new."updated_at" = NOW(); | ||
RETURN _new; | ||
END; | ||
$$ LANGUAGE plpgsql; | ||
CREATE TRIGGER "set_public_feeds_updated_at" | ||
BEFORE UPDATE ON "public"."feeds" | ||
FOR EACH ROW | ||
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); | ||
COMMENT ON TRIGGER "set_public_feeds_updated_at" ON "public"."feeds" | ||
IS 'trigger to set value of column "updated_at" to current timestamp on row update'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
INSERT INTO public.accounts (id, compound_id, user_id, provider_type, provider_id, provider_account_id, refresh_token, access_token, access_token_expires, created_at, updated_at) VALUES ('4a250393-1aa8-4fbc-a349-672609d7ce79', '758a69951dd3f2a6b93662db4c55b22f2333216c5c2079ddf95f557185696af8', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'oauth', 'google', '110127495875220400815', NULL, 'ya29.a0AfH6SMCyjCqi1qMu1dqQu0DWDJ-DUketNL7oRrJklZdl67lgUPSLj3sqb7Wil72G9IBCY5L3iSipakAOtPyvDu1Vbu9eCSYmmjitLoKtbBIpdl2Ypfg4_8NDmGgWfl-sJJbyNn7RKMEn4x1wF8U5YpMiA5fn3ZVVrd9f', NULL, '2020-07-30 15:38:05.270128+00', '2020-07-30 15:38:05.270128+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('bfa5a42d-1fe9-4fd7-a47c-6bc6e074fc51', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque pulvinar urna odio, eget fringilla ipsum volutpat ut. Mauris vel nunc ligula. Curabitur neque odio, varius sit amet fringilla eget, maximus sed ante.', '2020-07-30 19:00:44.107312+00', '2020-07-30 19:00:44.107312+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('07b160d8-8cf3-48be-8bef-aa73f6e459c4', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Morbi mollis tristique tempus. Cras finibus tristique velit, dignissim porttitor enim euismod eu. Duis tincidunt lacinia interdum. Sed eu elit mollis, condimentum nisl iaculis, convallis tortor.', '2020-07-30 19:38:50.639452+00', '2020-07-30 19:38:50.639452+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('15364138-6591-413f-b6db-7dd34e354c45', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Nam condimentum posuere eleifend. Proin rhoncus mi in mi congue, et varius velit semper. Integer suscipit sed augue a interdum. Nunc dictum vulputate facilisis. ', '2020-07-30 19:47:30.427743+00', '2020-07-30 19:47:30.427743+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('8ae90763-9746-4a0c-b952-71c9bda6cab8', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Nulla tempor in leo ut ultrices. Integer eget est at massa ultrices lobortis in ac nisi. Sed lacinia odio ut felis mattis bibendum.', '2020-07-30 19:50:28.129349+00', '2020-07-30 19:50:28.129349+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('6e2b67cb-d8cf-4d9b-842b-2e731dd040cd', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Nunc bibendum viverra nisi, vel sollicitudin urna malesuada sed. Nunc vehicula augue et massa sodales, ac euismod ante varius. Mauris at pretium orci. Pellentesque rutrum a enim et mollis. In metus risus, elementum in felis consectetur, eleifend ultricies leo. Maecenas interdum convallis mauris, eget lobortis ex accumsan et. Aliquam tristique sem id velit pellentesque suscipit.', '2020-07-30 19:52:21.535422+00', '2020-07-30 19:52:21.535422+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('e3df2e9a-17c5-4c35-ad65-609b480f3d04', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Curabitur vel vestibulum magna. Curabitur et arcu quis ligula lacinia mattis at facilisis orci. Vivamus ac elit nunc. Mauris magna turpis, laoreet quis pharetra eget, vulputate non nisi. Nullam condimentum volutpat sapien vel mattis. Donec nibh ex, tempor id felis quis, varius euismod augue.', '2020-07-30 19:52:32.01556+00', '2020-07-30 19:52:32.01556+00'); | ||
INSERT INTO public.feeds (id, author_id, body, created_at, updated_at) VALUES ('91da0b4a-9a82-4d75-b03b-2f306606fa00', '72ad01dc-9c5e-4844-a60d-6b653c75a363', 'Duis facilisis orci sed nisi luctus, vitae pretium neque malesuada. Nullam posuere pharetra lacinia. Maecenas vel leo nisl.', '2020-07-30 19:53:11.154018+00', '2020-07-30 19:53:11.154018+00'); | ||
INSERT INTO public.users (id, name, email, email_verified, image, created_at, updated_at) VALUES ('72ad01dc-9c5e-4844-a60d-6b653c75a363', 'John Doe', 'john@doe.com', NULL, '', '2020-07-30 15:38:05.240549+00', '2020-07-30 19:40:53.634539+00'); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import React, { useState, FormEvent } from "react"; | ||
import gql from "graphql-tag"; | ||
import { useMutation } from "urql"; | ||
import { | ||
Box, | ||
Stack, | ||
FormControl, | ||
FormLabel, | ||
Button, | ||
Alert, | ||
AlertIcon, | ||
AlertTitle, | ||
CloseButton, | ||
Textarea, | ||
useColorMode, | ||
} from "@chakra-ui/core"; | ||
import { useSession } from "next-auth/client"; | ||
import AccessDeniedIndicator from "components/access-denied-indicator"; | ||
|
||
const insertFeedMutation = gql` | ||
mutation insertFeed($userId: uuid!, $body: String) { | ||
insert_feeds_one(object: { author_id: $userId, body: $body }) { | ||
id | ||
} | ||
} | ||
`; | ||
|
||
const AddNewFeedForm = () => { | ||
const { colorMode } = useColorMode(); | ||
const bgColor = { light: "white", dark: "gray.800" }; | ||
const color = { light: "gray.800", dark: "gray.100" }; | ||
const [body, setBody] = useState(""); | ||
const [session] = useSession(); | ||
|
||
if (!session) { | ||
return ( | ||
<AccessDeniedIndicator message="You need to be signed in to add a new feed!" /> | ||
); | ||
} | ||
|
||
const [ | ||
{ fetching: insertFeedFetching, error: insertFeedError }, | ||
insertFeed, | ||
] = useMutation(insertFeedMutation); | ||
|
||
const handleSubmit = async () => { | ||
await insertFeed({ | ||
userId: session.id, | ||
body, | ||
}); | ||
|
||
setBody(""); | ||
}; | ||
|
||
const errorNode = () => { | ||
if (!insertFeedError) { | ||
return false; | ||
} | ||
|
||
return ( | ||
<Alert status="error"> | ||
<AlertIcon /> | ||
<AlertTitle>{insertFeedError}</AlertTitle> | ||
<CloseButton position="absolute" right="8px" top="8px" /> | ||
</Alert> | ||
); | ||
}; | ||
|
||
return ( | ||
<Stack spacing={4}> | ||
{errorNode()} | ||
<Box | ||
p={4} | ||
bg={bgColor[colorMode]} | ||
color={color[colorMode]} | ||
shadow="lg" | ||
rounded="lg" | ||
> | ||
<Stack spacing={4}> | ||
<FormControl isRequired> | ||
<FormLabel htmlFor="body">What's on your mind?</FormLabel> | ||
<Textarea | ||
id="body" | ||
value={body} | ||
onChange={(e: FormEvent<HTMLInputElement>) => | ||
setBody(e.currentTarget.value) | ||
} | ||
isDisabled={insertFeedFetching} | ||
/> | ||
</FormControl> | ||
<FormControl> | ||
<Button | ||
variantColor="cyan" | ||
loadingText="Posting..." | ||
onClick={handleSubmit} | ||
isLoading={insertFeedFetching} | ||
isDisabled={!body.trim()} | ||
> | ||
Post | ||
</Button> | ||
</FormControl> | ||
</Stack> | ||
</Box> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default AddNewFeedForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import React, { FC } from "react"; | ||
import { | ||
Box, | ||
Stack, | ||
Text, | ||
Avatar, | ||
useColorMode, | ||
Input, | ||
Button, | ||
} from "@chakra-ui/core"; | ||
import IFeed from "types/feed"; | ||
import timeFromNow from "lib/time-from-now"; | ||
|
||
interface IProps { | ||
feed: IFeed; | ||
} | ||
|
||
const Feed: FC<IProps> = ({ feed }) => { | ||
const { colorMode } = useColorMode(); | ||
const bgColor = { light: "white", dark: "gray.800" }; | ||
const color = { light: "gray.800", dark: "gray.100" }; | ||
const borderColor = { light: "gray.300", dark: "gray.700" }; | ||
|
||
const authorNode = () => { | ||
return ( | ||
<Stack | ||
spacing={4} | ||
isInline | ||
alignItems="center" | ||
p={4} | ||
borderBottomWidth={1} | ||
borderColor={borderColor[colorMode]} | ||
> | ||
<Avatar name={feed.author.name} src={feed.author.image} /> | ||
<Stack> | ||
<Text fontWeight="bold">{feed.author.name}</Text> | ||
<Text>{timeFromNow(feed.created_at)}</Text> | ||
</Stack> | ||
</Stack> | ||
); | ||
}; | ||
|
||
const bodyNode = () => { | ||
return ( | ||
<Text | ||
fontSize="md" | ||
p={4} | ||
borderBottomWidth={1} | ||
borderColor={borderColor[colorMode]} | ||
> | ||
{feed.body} | ||
</Text> | ||
); | ||
}; | ||
|
||
return ( | ||
<Box | ||
bg={bgColor[colorMode]} | ||
color={color[colorMode]} | ||
shadow="lg" | ||
rounded="lg" | ||
> | ||
<Stack spacing={0}> | ||
{authorNode()} | ||
{bodyNode()} | ||
</Stack> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default Feed; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from "react"; | ||
import gql from "graphql-tag"; | ||
import { useSubscription } from "urql"; | ||
import { Box, Stack } from "@chakra-ui/core"; | ||
import IFeed from "types/feed"; | ||
import Feed from "components/pages/feeds/feed"; | ||
import AddNewFeedForm from "components/pages/feeds/add-new-feed-form"; | ||
|
||
const feedsSubscription = gql` | ||
subscription fetchFeeds { | ||
feeds(order_by: { created_at: desc }) { | ||
id | ||
created_at | ||
body | ||
author { | ||
id | ||
name | ||
image | ||
} | ||
} | ||
} | ||
`; | ||
|
||
const FeedsPageComponent = () => { | ||
const [result] = useSubscription({ | ||
query: feedsSubscription, | ||
}); | ||
|
||
if (!result.data) { | ||
return <p>No feeds!</p>; | ||
} | ||
|
||
return ( | ||
<Stack spacing={8}> | ||
<Box> | ||
<AddNewFeedForm /> | ||
</Box> | ||
{result.data.feeds.map((feed: IFeed) => { | ||
return ( | ||
<Box key={feed.id}> | ||
<Feed feed={feed} /> | ||
</Box> | ||
); | ||
})} | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default FeedsPageComponent; |
Oops, something went wrong.