-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #53 from tosaken1116/feat/PostTweet-model
Feat/post tweet model
- Loading branch information
Showing
16 changed files
with
325 additions
and
10 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
NEXT_PUBLIC_BACKEND_URL= | ||
NEXT_PUBLIC_BACKEND_URL= | ||
ACCESS_KEY_ID= | ||
SECRET_ACCESS_KEY= | ||
REGION= | ||
S3_BUCKET_NAME= |
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
Submodule schema
updated
from 9b291c to 2fc205
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,71 @@ | ||
import type { ChangeEvent } from 'react'; | ||
import { useState, useTransition } from 'react'; | ||
|
||
import { useMutation } from '@tanstack/react-query'; | ||
|
||
import type { | ||
Account, | ||
CreateTweetPayload, | ||
CreateTweetResponse, | ||
} from '@/api/@types'; | ||
|
||
import { uploadImage } from '@/hooks/useImageUpload'; | ||
import { apiClient } from '@/libs/apiClients'; | ||
|
||
type IUsePostTweet = { | ||
handlePost: () => void; | ||
handleChange: (e: ChangeEvent<HTMLTextAreaElement>) => void; | ||
handleSelect: (e: ChangeEvent<HTMLInputElement>) => void; | ||
pending: boolean; | ||
account: Account; | ||
tweetAble: boolean; | ||
}; | ||
|
||
const fetcher = async ( | ||
body: CreateTweetPayload | ||
): Promise<CreateTweetResponse> => await apiClient.tweets.post.$post({ body }); | ||
|
||
export const usePostTweet = (): IUsePostTweet => { | ||
const [content, setContent] = useState(''); | ||
const [pending, startTransition] = useTransition(); | ||
const [file, setFile] = useState<File>(); | ||
const account: Account = { | ||
id: 'test_user', | ||
image_url: 'https://avatars.githubusercontent.com/u/94045195?v=4', | ||
name: 'user', | ||
role: 1, | ||
description: '', | ||
birth_day: '2021-09-01', | ||
website_url: 'localhost:3000', | ||
follow_count: 0, | ||
follower_count: 0, | ||
is_following: false, | ||
}; | ||
const { mutate: postTweet } = useMutation({ mutationFn: fetcher }); | ||
const handlePost = (): void => { | ||
postTweet({ content, image_url_list: [] }); | ||
}; | ||
|
||
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>): void => { | ||
setContent(e.target.value); | ||
}; | ||
|
||
const handleSelect = (e: ChangeEvent<HTMLInputElement>): void => { | ||
setFile(e.target?.files?.[0]); | ||
if (!file) return; | ||
|
||
const form = new FormData(); | ||
|
||
form.append('fileUpload', file); | ||
startTransition(() => uploadImage(form)); | ||
}; | ||
|
||
return { | ||
handlePost, | ||
handleChange, | ||
handleSelect, | ||
pending, | ||
account, | ||
tweetAble: content.length > 0, | ||
}; | ||
}; |
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,17 @@ | ||
import { PostTweetPresentation } from './presentations'; | ||
|
||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
const meta: Meta<typeof PostTweetPresentation> = { | ||
component: PostTweetPresentation, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
tags: ['autodocs'], | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof PostTweetPresentation>; | ||
|
||
export const Default: Story = {}; |
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 @@ | ||
import { PostTweetPresentation } from './presentations'; | ||
|
||
import { ClientProvider } from '@/components/functional/ClientProvider'; | ||
|
||
export const PostTweet: React.FC = () => ( | ||
<ClientProvider> | ||
<PostTweetPresentation /> | ||
</ClientProvider> | ||
); |
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,55 @@ | ||
'use client'; | ||
|
||
import { BsEmojiSmile, BsGeoAlt } from 'react-icons/bs'; | ||
import { MdOutlineGifBox } from 'react-icons/md'; | ||
import { RiListRadio } from 'react-icons/ri'; | ||
import { TbCalendarTime } from 'react-icons/tb'; | ||
|
||
import { usePostTweet } from '../hooks'; | ||
|
||
import { CustomFileInput } from './item/ImageInput'; | ||
|
||
import { Avatar, AvatarImage } from '@/components/ui/Avatar'; | ||
import { Button } from '@/components/ui/Button'; | ||
import { Separator } from '@/components/ui/Separator'; | ||
import { Textarea } from '@/components/ui/Textarea'; | ||
|
||
export const PostTweetPresentation: React.FC = () => { | ||
const { | ||
handleSelect, | ||
handleChange, | ||
account: { image_url }, | ||
tweetAble, | ||
} = usePostTweet(); | ||
|
||
return ( | ||
<div className="flex flex-row gap-4 p-4"> | ||
<Avatar className="h-12 w-12"> | ||
<AvatarImage src={image_url} alt={`${image_url}のアイコン`} /> | ||
</Avatar> | ||
<div className="w-full"> | ||
<Textarea | ||
onChange={handleChange} | ||
className=" resize-none border-none bg-transparent text-2xl text-white focus-visible:ring-0" | ||
placeholder="いまどうしてる?" | ||
/> | ||
<Separator orientation="horizontal" className="bg-white-hover" /> | ||
<div className="flex flex-row items-center pt-4"> | ||
<div className="flex flex-1 flex-row gap-4"> | ||
<form> | ||
<CustomFileInput onChange={handleSelect} /> | ||
</form> | ||
<MdOutlineGifBox size={24} className="text-primary" /> | ||
<RiListRadio size={24} className="text-primary" /> | ||
<BsEmojiSmile size={24} className="text-primary" /> | ||
<TbCalendarTime size={24} className="text-primary" /> | ||
<BsGeoAlt size={24} className="text-primary/40" /> | ||
</div> | ||
<Button disabled={!tweetAble} className="rounded-full font-bold"> | ||
ツイートする | ||
</Button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; |
19 changes: 19 additions & 0 deletions
19
src/components/model/PostTweet/presentations/item/ImageInput.tsx
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,19 @@ | ||
import type { ChangeEvent, FC } from 'react'; | ||
|
||
import { GoImage } from 'react-icons/go'; | ||
|
||
export const CustomFileInput: FC<{ | ||
onChange: (e: ChangeEvent<HTMLInputElement>) => void; | ||
}> = ({ onChange }) => ( | ||
<div> | ||
<label htmlFor="fileInput" className="cursor-pointer text-primary"> | ||
<GoImage size={24} /> | ||
</label> | ||
<input | ||
id="fileInput" | ||
type="file" | ||
style={{ display: 'none' }} | ||
onChange={onChange} | ||
/> | ||
</div> | ||
); |
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,22 @@ | ||
import { Input } from '.'; | ||
|
||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
const meta: Meta<typeof Input> = { | ||
component: Input, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
tags: ['autodocs'], | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Input>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
type: 'email', | ||
placeholder: 'Email', | ||
}, | ||
}; |
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,27 @@ | ||
import * as React from 'react'; | ||
|
||
import { cn } from '@/libs/utils'; | ||
|
||
export interface InputProps | ||
extends React.InputHTMLAttributes<HTMLInputElement> {} | ||
|
||
const Input = React.forwardRef< | ||
HTMLInputElement, | ||
InputProps & { startIcon: React.ReactNode } | ||
>(({ className, type, startIcon, ...props }, ref) => ( | ||
<div className="placeholder:text-muted-foreground ring-offset-background border-input flex h-12 w-full flex-row rounded-full bg-white-hover"> | ||
<span className="flex items-center pl-3 text-center">{startIcon}</span> | ||
<input | ||
type={type} | ||
ref={ref} | ||
{...props} | ||
className={cn( | ||
'flex w-full bg-transparent px-5 py-4 text-lg file:border-0 file:text-sm file:font-medium focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50', | ||
className | ||
)} | ||
/> | ||
</div> | ||
)); | ||
Input.displayName = 'Input'; | ||
|
||
export { Input }; |
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,21 @@ | ||
import { Textarea } from '.'; | ||
|
||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
const meta: Meta<typeof Textarea> = { | ||
component: Textarea, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
tags: ['autodocs'], | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Textarea>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
placeholder: 'Type your message here.', | ||
}, | ||
}; |
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,22 @@ | ||
import * as React from 'react'; | ||
|
||
import { cn } from '@/libs/utils'; | ||
|
||
export interface TextareaProps | ||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} | ||
|
||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( | ||
({ className, ...props }, ref) => ( | ||
<textarea | ||
className={cn( | ||
'border-input bg-background placeholder:text-muted-foreground flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50', | ||
className | ||
)} | ||
ref={ref} | ||
{...props} | ||
/> | ||
) | ||
); | ||
Textarea.displayName = 'Textarea'; | ||
|
||
export { Textarea }; |
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,38 @@ | ||
'use server'; | ||
|
||
import { S3Client } from '@aws-sdk/client-s3'; | ||
import { Upload } from '@aws-sdk/lib-storage'; | ||
|
||
export const uploadImage = async (data: FormData | null): Promise<void> => { | ||
if (data === null) { | ||
return; | ||
} | ||
const file = data.get('fileUpload'); | ||
if (file === null) { | ||
return; | ||
} | ||
const s3client = new S3Client({ | ||
credentials: { | ||
accessKeyId: process.env['ACCESS_KEY_ID'] || '', | ||
secretAccessKey: process.env['SECRET_ACCESS_KEY'] || '', | ||
}, | ||
}); | ||
try { | ||
const upload = new Upload({ | ||
client: s3client, | ||
params: { | ||
Bucket: `${process.env['S3_BUCKET_NAME']}`, | ||
Key: 'test.png', | ||
Body: file, | ||
ContentType: 'image/jpg', | ||
}, | ||
}); | ||
|
||
console.log('Uploading to S3...'); | ||
await upload.done(); | ||
|
||
console.log('Upload successful!'); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
}; |