Skip to content

Commit bc29ea0

Browse files
authored
Merge(deploy): 8차 배포
deploy: 8차 배포
2 parents 03bfb93 + 38113fc commit bc29ea0

29 files changed

Lines changed: 336 additions & 207 deletions

File tree

public/index.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,19 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1" />
77
<meta name="theme-color" content="#000000" />
88
<meta name="description" content="Web site created using create-react-app" />
9-
<title>초록 집사</title>
9+
<meta property="og:type" content="website" />
10+
<meta
11+
property="og:url"
12+
content="https://venerable-banoffee-585a32.netlify.app/user/62b210335baf8c52bfe6d523"
13+
/>
14+
<meta property="og:title" content="🌵초록 집사" />
15+
<meta
16+
property="og:image"
17+
content="https://user-images.githubusercontent.com/81489300/174609837-86af1ad0-8b87-4e6f-bc05-88b38b7a6d91.png"
18+
/>
19+
<meta property="og:description" content="반려식물에 관한 정보를 공유하는 SNS" />
20+
<meta property="og:site_name" content="초록 집사" />
21+
<title>초록집사</title>
1022
</head>
1123
<body>
1224
<div id="root"></div>

src/components/Modal/ModalContent.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const ModalContent = ({ title, description }) => {
3030
<Inner>
3131
<Image width={55} height={55} src={FLOWERPOT} />
3232
{title && (
33-
<Title block fontSize={26} strong>
33+
<Title block fontSize={24} strong>
3434
{title}
3535
</Title>
3636
)}

src/components/PostImageContainer/index.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const PostImageContainer = React.memo(function ImageContainer({ posts }) {
2626
threshold={0.4}
2727
placeholder={IMAGE_URLS.POST_DEFAULT_IMG}
2828
style={{ position: 'absolute', left: 0, top: 0 }}
29+
defaultImageUrl={IMAGE_URLS.POST_DEFAULT_GRID_IMG}
2930
/>
3031
</ImageItem>
3132
))}

src/components/SignupForm/index.jsx

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,9 @@ const emailValid = (email) => {
5555
return reg.test(email);
5656
};
5757

58-
const SignupForm = ({
59-
onSubmit,
60-
inValidEmail = false,
61-
inValidPassword = false,
62-
inValidFullName = false,
63-
inValidPasswordCheck = false,
64-
}) => {
65-
const [currentEmailInvalid, setEmailInvalid] = useState(inValidEmail);
66-
const [currentPasswordInvalid, setPasswordInvalid] = useState(inValidPassword);
67-
const [currentPasswordCheckInvalid, setPasswordCheckInvalid] = useState(inValidPasswordCheck);
68-
const [currentFullNameInvalid, setFullNameInvalid] = useState(inValidFullName);
58+
const SignupForm = ({ onSubmit }) => {
6959
const {
7060
values,
71-
errors,
7261
handleChange,
7362
handleSubmit,
7463
errorPassword,
@@ -80,37 +69,28 @@ const SignupForm = ({
8069
initialValues: {
8170
email: '',
8271
password: '',
72+
fullame: '',
73+
passwordCheck: '',
8374
},
8475
onSubmit,
8576
validate: ({ email, password, fullName, passwordCheck }) => {
8677
const newErrors = {};
8778

8879
if (!email) {
8980
newErrors.email = '* 이메일을 입력해 주세요.';
90-
setEmailInvalid(true);
9181
} else if (!emailValid(email)) {
9282
newErrors.email = '* 이메일 형식이 아닙니다.';
93-
setEmailInvalid(true);
94-
} else {
95-
setEmailInvalid(false);
9683
}
9784
if (!fullName) {
9885
newErrors.fullName = '* 닉네임을 입력해 주세요.';
99-
setFullNameInvalid(true);
100-
} else {
101-
setFullNameInvalid(false);
10286
}
10387
if (!password || password.length < 8) {
10488
newErrors.password = '* 8-10자 사이로 입력해 주세요.';
105-
setPasswordInvalid(true);
106-
} else {
107-
setPasswordInvalid(false);
10889
}
109-
if (password !== passwordCheck) {
90+
if (!passwordCheck || passwordCheck.length < 8) {
91+
newErrors.passwordCheck = '* 8-10자 사이로 입력해 주세요.';
92+
} else if (password !== passwordCheck) {
11093
newErrors.passwordCheck = '* 비밀번호가 일치하지 않습니다.';
111-
setPasswordCheckInvalid(true);
112-
} else {
113-
setPasswordCheckInvalid(false);
11494
}
11595

11696
return newErrors;
@@ -125,36 +105,28 @@ const SignupForm = ({
125105
height={'70'}
126106
label={''}
127107
fontSize={18}
128-
inValid={currentEmailInvalid || errorEmail ? true : false}
108+
inValid={errorEmail ? true : false}
129109
placeholder={'이메일을 입력해 주세요.'}
130110
onChange={handleChange}
131-
value={values.email || ''}
111+
value={values.email}
132112
onBlur={handleBlur}
133113
></Input>
134-
{errorEmail ? (
135-
<ErrorText>{errorEmail}</ErrorText>
136-
) : errors.email ? (
137-
<ErrorText>{errors.email}</ErrorText>
138-
) : (
139-
<div style={{ height: '23px' }}></div>
140-
)}
114+
{errorEmail ? <ErrorText>{errorEmail}</ErrorText> : <div style={{ height: '23px' }}></div>}
141115
</InputWrapper>
142116
<InputWrapper>
143117
<Input
144118
name="fullName"
145119
height={'70'}
146120
label={''}
147121
fontSize={18}
148-
inValid={currentFullNameInvalid || errorfullName ? true : false}
122+
inValid={errorfullName ? true : false}
149123
placeholder={'닉네임을 입력해주세요.'}
150124
onBlur={handleBlur}
151125
onChange={handleChange}
152-
value={values.fullName || ''}
126+
value={values.fullName}
153127
></Input>
154128
{errorfullName ? (
155129
<ErrorText>{errorfullName}</ErrorText>
156-
) : errors.fullName ? (
157-
<ErrorText>{errors.fullName}</ErrorText>
158130
) : (
159131
<ErrorText style={{ color: theme.color.fontNormal }}>
160132
* 특수문자를 제외하고 6자 이내로 입력해 주세요.
@@ -168,16 +140,14 @@ const SignupForm = ({
168140
height={'70'}
169141
label={''}
170142
fontSize={18}
171-
inValid={currentPasswordInvalid || errorPassword ? true : false}
143+
inValid={errorPassword ? true : false}
172144
placeholder={'비밀번호를 입력해 주세요.'}
173145
onChange={handleChange}
174146
value={values.password}
175147
onBlur={handleBlur}
176148
></Input>
177149
{errorPassword ? (
178150
<ErrorText>{errorPassword}</ErrorText>
179-
) : errors.password ? (
180-
<ErrorText>{errors.password}</ErrorText>
181151
) : (
182152
<ErrorText style={{ color: theme.color.fontNormal }}>
183153
* 8-10자 사이로 입력해 주세요.
@@ -191,16 +161,14 @@ const SignupForm = ({
191161
height={'70'}
192162
label={''}
193163
fontSize={18}
194-
inValid={currentPasswordCheckInvalid || errorPasswordCheck ? true : false}
195-
placeholder={'비밀번호를 한번 더 입력해 주세요.'}
164+
inValid={errorPasswordCheck ? true : false}
165+
placeholder={'비밀번호를 한 번 더 입력해 주세요.'}
196166
onBlur={handleBlur}
197167
onChange={handleChange}
198-
value={values.passwordCheck || ''}
168+
value={values.passwordCheck}
199169
></Input>
200170
{errorPasswordCheck ? (
201171
<ErrorText>{errorPasswordCheck}</ErrorText>
202-
) : errors.passwordCheck ? (
203-
<ErrorText>{errors.passwordCheck}</ErrorText>
204172
) : (
205173
<div style={{ height: '23px' }}></div>
206174
)}

src/components/TagAddForm/index.jsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InputForm, Text, Icon } from 'components';
44
import theme from 'styles/theme';
55
import { useState } from 'react';
66
import { useCallback } from 'react';
7+
import { useEffect } from 'react';
78

89
const { mainGreen, fontNormal, mainRed } = theme.color;
910

@@ -30,27 +31,39 @@ const RemoveBtn = styled.button`
3031
const TagAddForm = ({ onAddTag, onRemoveTag, tags }) => {
3132
const CHARACTER_LIMIT = 6;
3233
const TAG_LIMIT = 5;
33-
const { value, resetValue, error, setError, handleChange } = useValidInput(CHARACTER_LIMIT);
34+
const { value, resetValue, error, handleChange } = useValidInput(CHARACTER_LIMIT);
3435
const [isEmphasis, setIsEmphasis] = useState(false);
36+
const [errorMsg, setErrorMsg] = useState();
3537

3638
const handleRemoveTag = (index) => {
3739
onRemoveTag(index);
38-
setError('');
40+
setErrorMsg('');
3941
};
4042

4143
const onSubmit = useCallback(() => {
42-
setError('');
44+
setErrorMsg('');
45+
46+
if (tags.includes(`#${value}`)) {
47+
setErrorMsg('* 이미 등록한 태그입니다.');
48+
return;
49+
}
4350

4451
if (tags.length >= TAG_LIMIT) {
4552
setIsEmphasis(true);
53+
return;
4654
}
55+
4756
onAddTag(value.slice(0, CHARACTER_LIMIT));
4857
resetValue();
49-
}, [tags.length, onAddTag, resetValue, setError, value]);
58+
}, [tags, onAddTag, resetValue, setErrorMsg, value]);
59+
60+
useEffect(() => {
61+
setErrorMsg(error);
62+
}, [error]);
5063

5164
return (
5265
<>
53-
<InputForm onSubmit={onSubmit} error={error} style={{ marginTop: '15px' }}>
66+
<InputForm onSubmit={onSubmit} error={errorMsg} style={{ marginTop: '15px' }}>
5467
<InputForm.Input
5568
placeholder="태그를 입력해주세요"
5669
onChange={handleChange}

src/components/UploadImage/index.jsx

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from '@emotion/styled';
33
import PropTypes from 'prop-types';
44
import theme from 'styles/theme';
55
import { IMAGE_URLS } from 'utils/constants/images';
6+
import { Modal } from 'components';
67

78
const { backgroundGreen, mainWhite } = theme.color;
89
const { POST_ADD_IMG } = IMAGE_URLS;
@@ -41,9 +42,33 @@ const ImageInner = styled.div`
4142
const UploadImage = ({ onChange, defaultImage, ...props }) => {
4243
const [imageSrc, setImageSrc] = useState(defaultImage);
4344
const fileInputRef = useRef(null);
45+
const [modalMsg, setModalMsg] = useState({ isModal: false, title: '', description: '' });
4446

4547
const handleFileChange = (e) => {
4648
const fileBlob = e.target.files[0];
49+
50+
if (!fileBlob) {
51+
return;
52+
}
53+
54+
if (!/\.(gif|jpg|jpeg|png)$/i.test(fileBlob.name)) {
55+
setModalMsg({
56+
isModal: true,
57+
title: '등록할 수 없는 파일입니다.',
58+
description: '등록 가능한 확장자: jpg, jpeg, gif, png',
59+
});
60+
return;
61+
}
62+
63+
if (fileBlob.size > 1024 * 1024) {
64+
setModalMsg({
65+
isModal: true,
66+
title: '1MB 이하 파일만 등록해 주세요!',
67+
description: `현재 파일 용량 : ${Math.round((fileBlob.size / 1024 / 1024) * 100) / 100}MB`,
68+
});
69+
return;
70+
}
71+
4772
const reader = new FileReader();
4873
reader.readAsDataURL(fileBlob);
4974
reader.onload = () => {
@@ -52,6 +77,10 @@ const UploadImage = ({ onChange, defaultImage, ...props }) => {
5277
};
5378
};
5479

80+
const onCloseModal = () => {
81+
setModalMsg({ isModal: false, title: '', description: '' });
82+
};
83+
5584
useEffect(() => {
5685
setImageSrc(defaultImage);
5786
}, [defaultImage]);
@@ -66,7 +95,17 @@ const UploadImage = ({ onChange, defaultImage, ...props }) => {
6695
<ImageLoad onClick={() => fileInputRef.current.click()} style={{ ...props.style }}>
6796
<ImageInner style={{ ...ImageStyle }} />
6897
</ImageLoad>
69-
<FileInput ref={fileInputRef} type="file" id="file" onChange={handleFileChange} />
98+
<FileInput
99+
ref={fileInputRef}
100+
type="file"
101+
id="file"
102+
accept="image/gif, image/jpeg, image/png"
103+
onChange={handleFileChange}
104+
/>
105+
<Modal visible={modalMsg.isModal} onClose={onCloseModal}>
106+
<Modal.Content title={modalMsg.title} description={modalMsg.description}></Modal.Content>
107+
<Modal.Button onClick={onCloseModal}>확인</Modal.Button>
108+
</Modal>
70109
</ImageWrapper>
71110
);
72111
};

src/components/basic/Avatar/index.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const Avatar = ({
4848
mode="cover"
4949
placeholder={placeholder}
5050
style={{ opacity: loaded ? 1 : 0 }}
51+
defaultImageUrl={IMAGE_URLS.PROFILE_IMG}
5152
/>
5253
</AvatarWrapper>
5354
);

src/components/basic/Image/index.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import PropTypes from 'prop-types';
2+
import { useCallback } from 'react';
23
import { useState, useRef, useEffect } from 'react';
4+
import { IMAGE_URLS } from 'utils/constants/images';
35

46
let observer = null;
57
const LOAD_IMG_EVENT_TYPE = 'loadImage';
@@ -23,6 +25,7 @@ const Image = ({
2325
height,
2426
alt,
2527
mode = 'cover',
28+
defaultImageUrl,
2629
...props
2730
}) => {
2831
const [loaded, setLoaded] = useState(false);
@@ -57,6 +60,15 @@ const Image = ({
5760
imgRef.current && observer.observe(imgRef.current);
5861
}, [lazy, threshold]);
5962

63+
const handleError = useCallback(
64+
({ target }) => {
65+
target.onerror = null;
66+
target.src = defaultImageUrl || '';
67+
target.style.opacity = 1;
68+
},
69+
[defaultImageUrl],
70+
);
71+
6072
return (
6173
<img
6274
ref={imgRef}
@@ -65,6 +77,7 @@ const Image = ({
6577
width={width}
6678
height={height}
6779
style={{ ...props.style, ...imageStyle }}
80+
onError={handleError}
6881
/>
6982
);
7083
};

src/contexts/UserContext/handles.jsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {
1414
setNotification,
1515
} from 'utils/apis/userApi';
1616

17+
import { channelId, addPost, updatePost, getUserPosts } from 'utils/apis/postApi';
18+
import { objectToForm } from 'utils/functions/converter';
19+
1720
const useHandles = () => {
1821
const [localToken, setLocalToken] = useLocalToken();
1922
const navigate = useNavigate();
@@ -139,6 +142,24 @@ const useHandles = () => {
139142
[localToken],
140143
);
141144

145+
// 포스트 등록
146+
const handleAddPost = useCallback(
147+
async (title, image) => {
148+
const formData = await objectToForm({ title, image, channelId });
149+
return await addPost(localToken, formData).then((res) => res.data);
150+
},
151+
[localToken],
152+
);
153+
154+
const handleEditPost = useCallback(
155+
async (postId, title, image) => {
156+
const formData = await objectToForm({ postId, title, image, channelId });
157+
await updatePost(localToken, formData).then((res) => res.data);
158+
return await getUserPosts(postId).then((res) => res.data);
159+
},
160+
[localToken],
161+
);
162+
142163
return {
143164
handleGetCurrentUser,
144165
handleLogin,
@@ -149,6 +170,8 @@ const useHandles = () => {
149170
handlechangePassword,
150171
handlefollow,
151172
handleUnFollow,
173+
handleAddPost,
174+
handleEditPost,
152175
};
153176
};
154177

0 commit comments

Comments
 (0)