Skip to content

Commit

Permalink
Merge pull request #501 from velopert/feature/turnstile
Browse files Browse the repository at this point in the history
Feature/turnstile
  • Loading branch information
winverse authored Feb 26, 2024
2 parents 4cf0129 + f04ce88 commit 5c2df32
Show file tree
Hide file tree
Showing 17 changed files with 308 additions and 112 deletions.
3 changes: 2 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'G-8D0MD2S4PK');
</script>
<!-- Cloudflare (turnstile) -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onAppReady" defer></script>


<title>React App</title>
Expand Down
4 changes: 2 additions & 2 deletions src/components/base/HeaderLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const HeaderLogo: React.FC<HeaderLogoProps> = ({
<VelogIcon />
</VelogLogoLink>
<VLink to={velogPath} className="user-logo">
{userLogo.title || createFallbackTitle(username)}
<span>{userLogo.title || createFallbackTitle(username)}</span>
</VLink>
</HeaderLogoBlock>
);
Expand Down Expand Up @@ -69,7 +69,7 @@ const HeaderLogoBlock = styled.div`
.user-logo {
display: block;
max-width: calc(100vw - 200px);
max-width: calc(100vw - 250px);
${ellipsis};
}
`;
Expand Down
10 changes: 9 additions & 1 deletion src/components/write/PublishActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import media from '../../lib/styles/media';
const PublishActionButtonsBlock = styled.div`
display: flex;
justify-content: flex-end;
margin-top: 0.5rem;
${media.custom(767)} {
margin-top: 2rem;
}
Expand All @@ -15,12 +16,14 @@ export interface PublishActionButtonsProps {
onCancel: () => void;
onPublish: () => void;
edit: boolean;
isLoading: boolean;
}

const PublishActionButtons: React.FC<PublishActionButtonsProps> = ({
onCancel,
onPublish,
edit,
isLoading,
}) => {
return (
<PublishActionButtonsBlock>
Expand All @@ -32,7 +35,12 @@ const PublishActionButtons: React.FC<PublishActionButtonsProps> = ({
>
취소
</Button>
<Button size="large" data-testid="publish" onClick={onPublish}>
<Button
size="large"
data-testid="publish"
onClick={onPublish}
disabled={isLoading}
>
{edit ? '수정하기' : '출간하기'}
</Button>
</PublishActionButtonsBlock>
Expand Down
24 changes: 21 additions & 3 deletions src/components/write/PublishSeriesCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, FormEvent } from 'react';
import React, { useState, useEffect, FormEvent, useRef } from 'react';
import styled, { css, keyframes } from 'styled-components';
import { themedPalette } from '../../lib/styles/themes';
import OutsideClickHandler from 'react-outside-click-handler';
Expand Down Expand Up @@ -110,8 +110,8 @@ const PublishSeriesCreate: React.FC<PublishSeriesCreateProps> = ({
urlSlug: '',
});
const [editing, setEditing] = useState<boolean>(false);

const [defaultUrlSlug, setDefaultUrlSlug] = useState('');
const hideTimeoutId = useRef<NodeJS.Timeout | null>(null);

useEffect(() => {
let timeoutId: ReturnType<typeof setTimeout> | null = null;
Expand All @@ -137,15 +137,33 @@ const PublishSeriesCreate: React.FC<PublishSeriesCreateProps> = ({
setEditing(true);
}, [form.urlSlug]);

useEffect(() => {
return () => {
if (hideTimeoutId.current) {
clearTimeout(hideTimeoutId.current);
}
};
}, [hideTimeoutId]);

const onHide = () => {
setDisappear(true);
setTimeout(() => {
const timeout = setTimeout(() => {
setOpen(false);
setDisappear(false);
setShowOpenBlock(false);
}, 125);
const timeoutId = timeout;
hideTimeoutId.current = timeoutId;
};

useEffect(() => {
return () => {
if (hideTimeoutId.current) {
clearTimeout(hideTimeoutId.current);
}
};
}, []);

const submit = (e: FormEvent) => {
e.preventDefault();
if (form.name.trim() === '') {
Expand Down
1 change: 1 addition & 0 deletions src/containers/write/ActiveEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ const ActiveEditor: React.FC<ActiveEditorProps> = () => {
}, [dispatch, lastPostHistory, post]);

if (
id &&
!newPost &&
((!readPostForEdit.loading && post === null) ||
(post && post.user.id !== userId))
Expand Down
50 changes: 33 additions & 17 deletions src/containers/write/MarkdownEditorContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,21 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {
tags,
} = useSelector((state: RootState) => state.write);
const uncachedClient = useUncachedApolloClient();
const [writePost] = useMutation<WritePostResponse>(WRITE_POST, {
client: uncachedClient,
});
const [writePost, { loading: writePostLoading }] =
useMutation<WritePostResponse>(WRITE_POST, {
client: uncachedClient,
});

const bodyRef = useRef(initialBody);
const titleRef = useRef(title);
const [createPostHistory] =
useMutation<CreatePostHistoryResponse>(CREATE_POST_HISTORY);
const [editPost] = useMutation<EditPostResult>(EDIT_POST, {
client: uncachedClient,
});
const [editPost, { loading: editPostLoading }] = useMutation<EditPostResult>(
EDIT_POST,
{
client: uncachedClient,
},
);

const [lastSavedData, setLastSavedData] = useState({
title: initialTitle,
Expand Down Expand Up @@ -148,6 +152,7 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {

const onTempSave = useCallback(
async (notify?: boolean) => {
if (writePostLoading || editPostLoading) return;
if (!title || !markdown) {
toast.error('제목 또는 내용이 비어있습니다.');
return;
Expand All @@ -171,14 +176,17 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {
thumbnail: null,
meta: {},
series_id: null,
token: null,
},
});
if (!response || !response.data) return;

if (!response.data?.writePost) return;
const { id } = response.data.writePost;
dispatch(setWritePostId(id));
history.replace(`/write?id=${id}`);
notifySuccess();
}

// tempsaving unreleased post:
if (isTemp) {
await editPost({
Expand All @@ -194,6 +202,7 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {
meta: {},
series_id: null,
tags,
token: null,
},
});
notifySuccess();
Expand All @@ -205,19 +214,22 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {
if (shallowEqual(lastSavedData, { title, body: markdown })) {
return;
}
await createPostHistory({
variables: {
post_id: postId,
title,
body: markdown,
is_markdown: true,
},
});

if (postId) {
await createPostHistory({
variables: {
post_id: postId,
title,
body: markdown,
is_markdown: true,
},
});
}

setLastSavedData({
title,
body: markdown,
});
notifySuccess();
},
[
createPostHistory,
Expand All @@ -231,6 +243,8 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {
tags,
title,
writePost,
writePostLoading,
editPostLoading,
],
);

Expand Down Expand Up @@ -259,9 +273,11 @@ const MarkdownEditorContainer: React.FC<MarkdownEditorContainerProps> = () => {
thumbnail: null,
meta: {},
series_id: null,
token: null,
},
});
if (!response || !response.data) return;

if (!response.data?.writePost) return;
id = response.data.writePost.id;
dispatch(setWritePostId(id));
history.replace(`/write?id=${id}`);
Expand Down
79 changes: 61 additions & 18 deletions src/containers/write/PublishActionButtonsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { setHeadingId } from '../../lib/heading';
import { useHistory } from 'react-router';
import { toast } from 'react-toastify';
import { useUncachedApolloClient } from '../../lib/graphql/UncachedApolloContext';
import useTurnstile from '../../lib/hooks/useTurnstile';

type PublishActionButtonsContainerProps = {};

Expand All @@ -25,6 +26,10 @@ const PublishActionButtonsContainer: React.FC<
> = () => {
const history = useHistory();
const client = useApolloClient();
const user = useSelector((state: RootState) => state.core.user);

const isTurnstileEnabled = !!user && !user.is_trusted;
const { isLoading, token } = useTurnstile(isTurnstileEnabled);

const options = useSelector((state: RootState) =>
pick(
Expand Down Expand Up @@ -54,12 +59,16 @@ const PublishActionButtonsContainer: React.FC<

const uncachedClient = useUncachedApolloClient();

const [writePost] = useMutation<WritePostResponse>(WRITE_POST, {
client: uncachedClient,
});
const [editPost] = useMutation<EditPostResult>(EDIT_POST, {
client: uncachedClient,
});
const [writePost, { loading: writePostLoading }] =
useMutation<WritePostResponse>(WRITE_POST, {
client: uncachedClient,
});
const [editPost, { loading: editPostLoading }] = useMutation<EditPostResult>(
EDIT_POST,
{
client: uncachedClient,
},
);

const variables = {
title: options.title,
Expand All @@ -77,44 +86,78 @@ const PublishActionButtonsContainer: React.FC<
short_description: options.description,
},
series_id: safe(() => options.selectedSeries!.id),
token,
};

const onPublish = async () => {
if (writePostLoading) {
toast.info('포스트 작성 중입니다.');
return;
}

if (options.title.trim() === '') {
toast.error('제목이 비어있습니다.');
return;
}

try {
const response = await writePost({
variables: variables,
});
if (!response || !response.data) return;

if (!response.data?.writePost) {
toast.error('포스트 작성 실패');
return;
}

const { user, url_slug } = response.data.writePost;
await client.resetStore();
history.push(`/@${user.username}/${url_slug}`);
} catch (e) {
} catch (error) {
console.log('write post failed', error);
toast.error('포스트 작성 실패');
}
};

const onEdit = async () => {
const response = await editPost({
variables: {
id: options.postId,
...variables,
},
});
if (!response || !response.data) return;
const { user, url_slug } = response.data.editPost;
await client.resetStore();
history.push(`/@${user.username}/${url_slug}`);
if (editPostLoading) {
toast.info('포스트 수정 중입니다.');
return;
}

if (options.title.trim() === '') {
toast.error('제목이 비어있습니다.');
return;
}

try {
const response = await editPost({
variables: {
id: options.postId,
...variables,
},
});

if (!response.data?.editPost) {
toast.error('포스트 수정 실패');
return;
}

const { user, url_slug } = response.data.editPost;
await client.resetStore();
history.push(`/@${user.username}/${url_slug}`);
} catch (error) {
console.log('edit post failed', error);
toast.error('포스트 수정 실패');
}
};

return (
<PublishActionButtons
onCancel={onCancel}
onPublish={options.postId ? onEdit : onPublish}
edit={!!options.postId && !options.isTemp}
isLoading={isLoading}
/>
);
};
Expand Down
Loading

0 comments on commit 5c2df32

Please sign in to comment.