From 12c030ffc3b427142f4fa106a3a80dd5acc6c747 Mon Sep 17 00:00:00 2001 From: HowToBeAHappyBoy Date: Wed, 6 Jan 2021 23:11:11 +0900 Subject: [PATCH 001/199] Show error toast when create series with non title --- src/components/write/PublishSeriesCreate.tsx | 5 +++++ .../write/__tests__/PublishSeriesCreate.test.tsx | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/components/write/PublishSeriesCreate.tsx b/src/components/write/PublishSeriesCreate.tsx index f71feff8..9dc107a3 100644 --- a/src/components/write/PublishSeriesCreate.tsx +++ b/src/components/write/PublishSeriesCreate.tsx @@ -5,6 +5,7 @@ import OutsideClickHandler from 'react-outside-click-handler'; import Button from '../common/Button'; import useInputs from '../../lib/hooks/useInputs'; import { escapeForUrl } from '../../lib/utils'; +import { toast } from 'react-toastify'; const fadeIn = keyframes` from { @@ -145,6 +146,10 @@ const PublishSeriesCreate: React.FC = ({ const submit = (e: FormEvent) => { e.preventDefault(); + if (form.name.trim() === '') { + toast.error('시리즈 제목이 비어있습니다.'); + return; + } onSubmit({ name: form.name, urlSlug: form.urlSlug || defaultUrlSlug, diff --git a/src/components/write/__tests__/PublishSeriesCreate.test.tsx b/src/components/write/__tests__/PublishSeriesCreate.test.tsx index 3e86106e..1447fccd 100644 --- a/src/components/write/__tests__/PublishSeriesCreate.test.tsx +++ b/src/components/write/__tests__/PublishSeriesCreate.test.tsx @@ -8,6 +8,7 @@ import { import PublishSeriesCreate, { PublishSeriesCreateProps, } from '../PublishSeriesCreate'; +import { toast } from 'react-toastify'; describe('PublishSeriesCreate', () => { const setup = (props: Partial = {}) => { @@ -80,6 +81,20 @@ describe('PublishSeriesCreate', () => { }); }); + it('show error toast when called onSubmit with no name', async () => { + const onSubmit = jest.fn(); + toast.error = jest.fn(); + const { getByText } = setup({ onSubmit }); + const createSeriesButton = await waitForElement(() => + getByText('시리즈 추가'), + ); + fireEvent.click(createSeriesButton); + expect(onSubmit).toHaveBeenCalledWith({ + name: '', + }); + expect(toast.error).toHaveBeenCalledWith('시리즈 제목이 비어있습니다.'); + }); + it('shows username recevied via props', async () => { const { getByPlaceholderText, getByText } = setup({ username: 'helloworld', From 40e390a574b8c2d3eacd6057cce205ff3ca59da2 Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 5 Mar 2022 05:16:50 +0900 Subject: [PATCH 002/199] feat: :sparkles: Applies GraphCDN --- src/components/base/HeaderUserMenu.tsx | 2 +- src/components/home/HomeMobileHeadExtra.tsx | 24 +++++++++++ src/components/velog/VelogPageTemplate.tsx | 47 ++++++++++++++++++++- src/containers/post/PostViewer.tsx | 12 ++++++ src/lib/graphql/post.ts | 38 +++++++++++++++++ src/pages/home/hooks/useRecentPosts.ts | 4 +- 6 files changed, 122 insertions(+), 5 deletions(-) diff --git a/src/components/base/HeaderUserMenu.tsx b/src/components/base/HeaderUserMenu.tsx index fd85a152..09c1138f 100644 --- a/src/components/base/HeaderUserMenu.tsx +++ b/src/components/base/HeaderUserMenu.tsx @@ -27,7 +27,7 @@ const HeaderUserMenuBlock = styled.div` `; interface HeaderUserMenuProps { - onClose: (e: React.MouseEvent) => void; + onClose: (e: React.MouseEvent) => void; onLogout: () => void; username: string; visible: boolean; diff --git a/src/components/home/HomeMobileHeadExtra.tsx b/src/components/home/HomeMobileHeadExtra.tsx index 20fac85a..3487c257 100644 --- a/src/components/home/HomeMobileHeadExtra.tsx +++ b/src/components/home/HomeMobileHeadExtra.tsx @@ -5,6 +5,7 @@ import palette from '../../lib/styles/palette'; import { Link } from 'react-router-dom'; import { useTransition, animated } from 'react-spring'; import OutsideClickHandler from 'react-outside-click-handler'; +import { useTheme } from '../../lib/hooks/useTheme'; export type MainMobileHeadExtraProps = { visible: boolean; @@ -31,6 +32,12 @@ function MainMobileHeadExtra({ visible, onClose }: MainMobileHeadExtraProps) { }, }); + const theme = useTheme(); + const img = + theme === 'dark' + ? 'https://graphcdn.io/badge-light.svg' + : 'https://graphcdn.io/badge.svg'; + return ( <> {transition((styles, item) => @@ -62,6 +69,11 @@ function MainMobileHeadExtra({ visible, onClose }: MainMobileHeadExtraProps) {
문의
contact@velog.io
+ + + Powered by GraphCDN, the GraphQL CDN + + @@ -78,6 +90,7 @@ const Aligner = styled.div` z-index: 5; `; const Block = styled(animated.div)` + border: 1px solid ${themedPalette.border3}; margin-top: 0.5rem; width: 12rem; box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1); @@ -122,4 +135,15 @@ const Block = styled(animated.div)` } `; +const GraphCDNWrapper = styled.div` + display: flex; + justify-content: center; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + img { + width: 120px; + height: auto; + } +`; + export default MainMobileHeadExtra; diff --git a/src/components/velog/VelogPageTemplate.tsx b/src/components/velog/VelogPageTemplate.tsx index 8c471d9f..a5bd8d77 100644 --- a/src/components/velog/VelogPageTemplate.tsx +++ b/src/components/velog/VelogPageTemplate.tsx @@ -1,5 +1,6 @@ -import * as React from 'react'; +import React, { createContext, useContext, useState } from 'react'; import styled from 'styled-components'; +import { useTheme } from '../../lib/hooks/useTheme'; import PageTemplate from '../base/PageTemplate'; const VelogPageTemplateBlock = styled(PageTemplate)` @@ -8,8 +9,50 @@ const VelogPageTemplateBlock = styled(PageTemplate)` export interface VelogPageTemplateProps {} +const FooterContext = createContext<(value: boolean) => void>(() => {}); + const VelogPageTemplate: React.FC = ({ children }) => { - return {children}; + const [showFooter, setShowFooter] = useState(false); + return ( + + + {children} + {showFooter && } + + + ); }; +export function useSetShowFooter() { + return useContext(FooterContext); +} + +function GraphCDNFooter() { + const theme = useTheme(); + const img = + theme === 'dark' + ? 'https://graphcdn.io/badge-light.svg' + : 'https://graphcdn.io/badge.svg'; + return ( + + + Powered by GraphCDN, the GraphQL CDN + + + ); +} + +const Block = styled.div` + display: flex; + justify-content: center; + padding-top: 1rem; + padding-bottom: 1rem; + position: relative; + z-index: 50; + img { + width: 150px; + height: auto; + } +`; + export default VelogPageTemplate; diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index f28a7aae..94fb3b16 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -37,6 +37,7 @@ import RelatedPost from './RelatedPost'; import optimizeImage from '../../lib/optimizeImage'; import RelatedPostsForGuest from './RelatedPostsForGuest'; import HorizontalAd from './HorizontalAd'; +import { useSetShowFooter } from '../../components/velog/VelogPageTemplate'; const UserProfileWrapper = styled(VelogResponsive)` margin-top: 16rem; @@ -64,6 +65,7 @@ const PostViewer: React.FC = ({ history, match, }) => { + const setShowFooter = useSetShowFooter(); const [showRecommends, setShowRecommends] = useState(false); useEffect(() => { window.scrollTo(0, 0); @@ -142,6 +144,16 @@ const PostViewer: React.FC = ({ const postReady = !!data?.post; + useEffect(() => { + setShowFooter(postReady); + }, [setShowFooter, postReady]); + + useEffect(() => { + return () => { + setShowFooter(false); + }; + }, [setShowFooter]); + const onScroll = useCallback(() => { const scrollTop = getScrollTop(); const { scrollHeight } = document.body; diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index 55a4fe1f..3a0cfee9 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -177,6 +177,44 @@ export const GET_POST_LIST = gql` } `; +export const GET_RECENT_POSTS = gql` + query RecentPosts( + $cursor: ID + $username: String + $temp_only: Boolean + $tag: String + $limit: Int + ) { + posts( + cursor: $cursor + username: $username + temp_only: $temp_only + tag: $tag + limit: $limit + ) { + id + title + short_description + thumbnail + user { + id + username + profile { + id + thumbnail + } + } + url_slug + released_at + updated_at + comments_count + tags + is_private + likes + } + } +`; + export const GET_TRENDING_POSTS = gql` query TrendingPosts($limit: Int, $offset: Int, $timeframe: String) { trendingPosts(limit: $limit, offset: $offset, timeframe: $timeframe) { diff --git a/src/pages/home/hooks/useRecentPosts.ts b/src/pages/home/hooks/useRecentPosts.ts index de990a0d..e63f36d6 100644 --- a/src/pages/home/hooks/useRecentPosts.ts +++ b/src/pages/home/hooks/useRecentPosts.ts @@ -1,11 +1,11 @@ import { useQuery } from '@apollo/react-hooks'; -import { GET_POST_LIST, PartialPost } from '../../../lib/graphql/post'; +import { GET_RECENT_POSTS, PartialPost } from '../../../lib/graphql/post'; import { useCallback, useState } from 'react'; import useScrollPagination from '../../../lib/hooks/useScrollPagination'; export default function useRecentPosts() { const { data, loading, fetchMore } = useQuery<{ posts: PartialPost[] }>( - GET_POST_LIST, + GET_RECENT_POSTS, { variables: { limit: 24, From 0cf89a573a116dc59441971e756f9c9c567b405b Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 5 Mar 2022 05:22:40 +0900 Subject: [PATCH 003/199] Changes GraphQL Host --- .github/workflows/main.yml | 1 + src/lib/graphql/client.ts | 2 +- src/server/serverRender.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 78c600ea..5b4de801 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,6 +29,7 @@ jobs: run: npm run build:ci env: REACT_APP_API_HOST: 'https://v2.velog.io/' + REACT_APP_GRAPHQL_HOST: 'https://velog.graphcdn.app/' PUBLIC_URL: 'https://static.velog.io/' REACT_APP_REDIS_HOST: ${{ secrets.REDIS_HOST }} CI: false diff --git a/src/lib/graphql/client.ts b/src/lib/graphql/client.ts index 9c3cc9e2..c6b4588e 100644 --- a/src/lib/graphql/client.ts +++ b/src/lib/graphql/client.ts @@ -5,7 +5,7 @@ import { createHttpLink } from 'apollo-link-http'; const host = (process.env.NODE_ENV === 'development' ? '/' - : process.env.REACT_APP_API_HOST) || '/'; + : process.env.REACT_APP_GRAPHQL_HOST) || '/'; const graphqlURI = host.concat('graphql'); const link = createHttpLink({ diff --git a/src/server/serverRender.tsx b/src/server/serverRender.tsx index 91175c94..fdfea249 100644 --- a/src/server/serverRender.tsx +++ b/src/server/serverRender.tsx @@ -62,7 +62,7 @@ const serverRender = async ({ url, loggedIn, cookie }: SSROption) => { const client = new ApolloClient({ ssrMode: true, link: createHttpLink({ - uri: `${process.env.REACT_APP_API_HOST}graphql`, + uri: `${process.env.REACT_APP_GRAPHQL_HOST}graphql`, fetch: fetch as any, headers: { cookie, From 57663f9a0a435be23077922d814ece7dc1bdfbd6 Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 5 Mar 2022 05:58:39 +0900 Subject: [PATCH 004/199] revert: --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5b4de801..73acd2ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: run: npm run build:ci env: REACT_APP_API_HOST: 'https://v2.velog.io/' - REACT_APP_GRAPHQL_HOST: 'https://velog.graphcdn.app/' + REACT_APP_GRAPHQL_HOST: 'https://v2.velog.io/' PUBLIC_URL: 'https://static.velog.io/' REACT_APP_REDIS_HOST: ${{ secrets.REDIS_HOST }} CI: false From 448a164a957162258d3409629ec92c503fe69f58 Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 5 Mar 2022 06:41:46 +0900 Subject: [PATCH 005/199] fix: :art: Fixes banner style --- src/components/velog/VelogPageTemplate.tsx | 13 ++++++++----- src/containers/post/HorizontalAd.tsx | 2 +- src/containers/post/RelatedPost.tsx | 4 ++-- src/lib/styles/zIndexes.ts | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/velog/VelogPageTemplate.tsx b/src/components/velog/VelogPageTemplate.tsx index a5bd8d77..d7f1fd0b 100644 --- a/src/components/velog/VelogPageTemplate.tsx +++ b/src/components/velog/VelogPageTemplate.tsx @@ -1,6 +1,7 @@ import React, { createContext, useContext, useState } from 'react'; import styled from 'styled-components'; import { useTheme } from '../../lib/hooks/useTheme'; +import { themedPalette } from '../../lib/styles/themes'; import PageTemplate from '../base/PageTemplate'; const VelogPageTemplateBlock = styled(PageTemplate)` @@ -15,10 +16,8 @@ const VelogPageTemplate: React.FC = ({ children }) => { const [showFooter, setShowFooter] = useState(false); return ( - - {children} - {showFooter && } - + {children} + {showFooter && } ); }; @@ -43,13 +42,17 @@ function GraphCDNFooter() { } const Block = styled.div` + background: ${themedPalette.bg_page1}; display: flex; justify-content: center; padding-top: 1rem; padding-bottom: 1rem; position: relative; - z-index: 50; + a { + display: block; + } img { + display: block; width: 150px; height: auto; } diff --git a/src/containers/post/HorizontalAd.tsx b/src/containers/post/HorizontalAd.tsx index 2776e0d2..8c15f215 100644 --- a/src/containers/post/HorizontalAd.tsx +++ b/src/containers/post/HorizontalAd.tsx @@ -39,7 +39,7 @@ function HorizontalAd({}: Props) { data-ad-client="ca-pub-5574866530496701" data-ad-slot="8809887603" data-ad-format="auto" - // data-full-width-responsive="true" + data-full-width-responsive="true" /> diff --git a/src/containers/post/RelatedPost.tsx b/src/containers/post/RelatedPost.tsx index 31e986ca..07e5a98b 100644 --- a/src/containers/post/RelatedPost.tsx +++ b/src/containers/post/RelatedPost.tsx @@ -87,7 +87,7 @@ const Title = styled.div` `; const Background = styled.div` - z-index: 30; + z-index: 5; position: relative; padding-top: 4rem; padding-bottom: 4rem; @@ -98,7 +98,7 @@ const Background = styled.div` } margin-top: 4rem; background: ${themedPalette.bg_page1}; - box-shadow: 0px 0 32px rgb(0 0 0 / 8%); + box-shadow: 0px -16px 16px rgb(0 0 0 / 4%); `; const Wrapper = styled.div` width: 1376px; diff --git a/src/lib/styles/zIndexes.ts b/src/lib/styles/zIndexes.ts index 3295423f..9e5cddd6 100644 --- a/src/lib/styles/zIndexes.ts +++ b/src/lib/styles/zIndexes.ts @@ -2,7 +2,7 @@ const zIndexes = { OpaqueLayer: 10, AuthModal: 20, Toolbar: 10, - Popup: 25, + Popup: 60, WriteFooter: 10, PublishScreen: 15, DragDropUpload: 100, From f43ceefbe6163cd1e548d3b21779bcf2be959dab Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 02:11:57 +0900 Subject: [PATCH 006/199] feat: :zap: Changes GraphQL Endpoint --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 73acd2ca..eb3ee65e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: run: npm run build:ci env: REACT_APP_API_HOST: 'https://v2.velog.io/' - REACT_APP_GRAPHQL_HOST: 'https://v2.velog.io/' + REACT_APP_GRAPHQL_HOST: 'https://v2cdn.velog.io/' PUBLIC_URL: 'https://static.velog.io/' REACT_APP_REDIS_HOST: ${{ secrets.REDIS_HOST }} CI: false From 1efdc40d3e6cb6120297933f2784e1ebe9093a4b Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 02:48:16 +0900 Subject: [PATCH 007/199] fix: Adds try catch to keepChunks --- scripts/keepChunks.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/keepChunks.js b/scripts/keepChunks.js index 6de30c6e..bc027aad 100644 --- a/scripts/keepChunks.js +++ b/scripts/keepChunks.js @@ -71,7 +71,7 @@ function filterOutMapFiles(files) { */ function downloadUrls(urls) { return Promise.all( - urls.map(url => { + urls.map((url) => { const downloadPath = url .slice(0, url.lastIndexOf('/')) .replace(PUBLIC_URL, ''); @@ -95,13 +95,15 @@ async function keepChunks() { .reduce((acc, current) => { return acc.concat(Object.values(current.files)); }, []) - .filter(url => + .filter((url) => ['index.html', 'loadable-stats.json', 'service-worker.js'].every( - ignored => !url.includes(ignored), + (ignored) => !url.includes(ignored), ), ); - await downloadUrls(urls); + try { + await downloadUrls(urls); + } catch (e) {} // update assetHistory assetHistory.history.push({ From 5ffd700227c0e169233a24aab1399a802ac11a0e Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 02:56:10 +0900 Subject: [PATCH 008/199] fix: :bug: Fixes github action --- .github/workflows/main.yml | 32 +++++--------------------------- package.json | 8 ++++---- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eb3ee65e..3c9d825e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,35 +33,13 @@ jobs: PUBLIC_URL: 'https://static.velog.io/' REACT_APP_REDIS_HOST: ${{ secrets.REDIS_HOST }} CI: false - - name: s3 sync - uses: jakejarvis/s3-sync-action@master - with: - args: --follow-symlinks --delete + - name: upload env: AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + AWS_S3_BUCKET_SSR: ${{ secrets.AWS_S3_BUCKET_SSR }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} AWS_REGION: 'ap-northeast-2' - SOURCE_DIR: 'build' - - name: s3 sync:ssr - uses: jakejarvis/s3-sync-action@master - with: - args: --follow-symlinks --delete - env: - AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET_SSR }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} - AWS_REGION: 'ap-northeast-2' - SOURCE_DIR: 'dist' - - name: Deploy Completion Webhook - uses: joelwmale/webhook-action@master - env: - WEBHOOK_URL: ${{ secrets.SSR_COMPLETE_WEBHOOK }} - data: "{'deployment': 'finished'}" - - name: Slack Notification - uses: rtCamp/action-slack-notify@v2.0.0 - env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - SLACK_USERNAME: 'Github Actions' - SLACK_ICON: 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png' - SLACK_MESSAGE: '*stage: production* - 벨로그 웹 클라이언트 배포가 끝났어요! :rocket:' \ No newline at end of file + run: | + yarn upload:s3 + yarn upload:ssr \ No newline at end of file diff --git a/package.json b/package.json index 8196898d..72214e0e 100644 --- a/package.json +++ b/package.json @@ -163,12 +163,12 @@ "scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js", - "build:ci": "node scripts/build.js && node scripts/keepChunks.js && node scripts/build.server.js", + "build:ci": "node scripts/build.js && node scripts/build.server.js", "build:server": "node scripts/build.server.js", "start:server:local": "node ./dist/server.js", "test": "node scripts/test.js", - "upload:s3": "aws s3 sync ./build s3://$S3_BUCKET --delete", - "upload:ssr": "aws s3 sync ./build s3://$S3_BUCKET_SSR", + "upload": "aws s3 cp ./build s3://$S3_BUCKET", + "upload:ssr": "aws s3 cp ./dist s3://$S3_BUCKET_SSR", "download": "aws s3 cp s3://$S3_BUCKET ./build/ --recursive && aws s3 cp s3://$S3_BUCKET_SSR ./dist/ --recursive", "deploy": "pm2 reload deploy.config.json" }, @@ -248,7 +248,7 @@ "@loadable/babel-plugin" ] }, - "proxy": "https://v2.velog.io/", + "proxy": "https://v2cdn.velog.io/", "devDependencies": { "@loadable/webpack-plugin": "^5.15.1" } From 37e65f838473fcfe6ba544ec6a4ea510ebad8553 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 02:59:24 +0900 Subject: [PATCH 009/199] fix: :bug: Fix typo in github action --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c9d825e..8107d209 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,5 +41,5 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} AWS_REGION: 'ap-northeast-2' run: | - yarn upload:s3 + yarn upload yarn upload:ssr \ No newline at end of file From 263d32d9a33f4b702134fce93f55161d966ff8f0 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 03:03:35 +0900 Subject: [PATCH 010/199] fix: :bug: Fixes another typo in github action --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8107d209..d253e96b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,8 +35,8 @@ jobs: CI: false - name: upload env: - AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} - AWS_S3_BUCKET_SSR: ${{ secrets.AWS_S3_BUCKET_SSR }} + S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + S3_BUCKET_SSR: ${{ secrets.AWS_S3_BUCKET_SSR }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} AWS_REGION: 'ap-northeast-2' From a647e8d3c4013f5713a34c409be952ddb7a755c7 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 03:08:06 +0900 Subject: [PATCH 011/199] fix: :bug: Fixes s3 script --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 72214e0e..6f371341 100644 --- a/package.json +++ b/package.json @@ -167,8 +167,8 @@ "build:server": "node scripts/build.server.js", "start:server:local": "node ./dist/server.js", "test": "node scripts/test.js", - "upload": "aws s3 cp ./build s3://$S3_BUCKET", - "upload:ssr": "aws s3 cp ./dist s3://$S3_BUCKET_SSR", + "upload": "aws s3 cp --recursive ./build s3://$S3_BUCKET", + "upload:ssr": "aws s3 cp --recursive ./dist s3://$S3_BUCKET_SSR", "download": "aws s3 cp s3://$S3_BUCKET ./build/ --recursive && aws s3 cp s3://$S3_BUCKET_SSR ./dist/ --recursive", "deploy": "pm2 reload deploy.config.json" }, From 1029e676d6d79da2d3a78f3ed65e35582a9c745a Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 03:53:47 +0900 Subject: [PATCH 012/199] Adds graphql logout manually --- src/components/base/hooks/useHeader.ts | 7 +++++-- src/lib/graphql/user.ts | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/base/hooks/useHeader.ts b/src/components/base/hooks/useHeader.ts index b51e554c..e7b47b41 100644 --- a/src/components/base/hooks/useHeader.ts +++ b/src/components/base/hooks/useHeader.ts @@ -4,11 +4,14 @@ import { showAuthModal } from '../../../modules/core'; import { RootState } from '../../../modules'; import { logout } from '../../../lib/api/auth'; import storage from '../../../lib/storage'; +import { useMutation } from '@apollo/react-hooks'; +import { LOGOUT } from '../../../lib/graphql/user'; export default function useHeader() { const dispatch = useDispatch(); const user = useSelector((state: RootState) => state.core.user); const customHeader = useSelector((state: RootState) => state.header); + const [graphqlLogout] = useMutation(LOGOUT); const onLoginClick = useCallback(() => { dispatch(showAuthModal('LOGIN')); @@ -16,11 +19,11 @@ export default function useHeader() { const onLogout = useCallback(async () => { try { - await logout(); + await Promise.all([logout(), graphqlLogout()]); } catch {} storage.removeItem('CURRENT_USER'); window.location.href = '/'; - }, []); + }, [graphqlLogout]); return { user, onLoginClick, onLogout, customHeader }; } diff --git a/src/lib/graphql/user.ts b/src/lib/graphql/user.ts index caabb69f..05b59529 100644 --- a/src/lib/graphql/user.ts +++ b/src/lib/graphql/user.ts @@ -157,3 +157,9 @@ export const UPDATE_ABOUT = gql` } } `; + +export const LOGOUT = gql` + mutation Logout { + logout + } +`; From 697a79b3f9b3524dcd8d558cfc0c47beaa606940 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 04:25:47 +0900 Subject: [PATCH 013/199] refactor: :zap: Use recetPosts query for caching --- src/lib/graphql/post.ts | 16 ++-------------- src/pages/home/RecentPostsPage.tsx | 2 +- src/pages/home/hooks/useRecentPosts.ts | 8 ++++---- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index 3a0cfee9..26fcf6c5 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -178,20 +178,8 @@ export const GET_POST_LIST = gql` `; export const GET_RECENT_POSTS = gql` - query RecentPosts( - $cursor: ID - $username: String - $temp_only: Boolean - $tag: String - $limit: Int - ) { - posts( - cursor: $cursor - username: $username - temp_only: $temp_only - tag: $tag - limit: $limit - ) { + query RecentPosts($cursor: ID, $limit: Int) { + recentPosts(cursor: $cursor, limit: $limit) { id title short_description diff --git a/src/pages/home/RecentPostsPage.tsx b/src/pages/home/RecentPostsPage.tsx index 57dc1bb9..aa6b5600 100644 --- a/src/pages/home/RecentPostsPage.tsx +++ b/src/pages/home/RecentPostsPage.tsx @@ -18,7 +18,7 @@ function RecentPostsPage(props: RecentPostsPageProps) { /> diff --git a/src/pages/home/hooks/useRecentPosts.ts b/src/pages/home/hooks/useRecentPosts.ts index e63f36d6..0ff90d6e 100644 --- a/src/pages/home/hooks/useRecentPosts.ts +++ b/src/pages/home/hooks/useRecentPosts.ts @@ -4,7 +4,7 @@ import { useCallback, useState } from 'react'; import useScrollPagination from '../../../lib/hooks/useScrollPagination'; export default function useRecentPosts() { - const { data, loading, fetchMore } = useQuery<{ posts: PartialPost[] }>( + const { data, loading, fetchMore } = useQuery<{ recentPosts: PartialPost[] }>( GET_RECENT_POSTS, { variables: { @@ -25,11 +25,11 @@ export default function useRecentPosts() { }, updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; - if (fetchMoreResult.posts.length === 0) { + if (fetchMoreResult.recentPosts.length === 0) { setIsFinished(true); } return { - posts: [...prev.posts, ...fetchMoreResult.posts], + recentPosts: [...prev.recentPosts, ...fetchMoreResult.recentPosts], }; }, }); @@ -37,7 +37,7 @@ export default function useRecentPosts() { [fetchMore], ); - const cursor = data?.posts[data?.posts.length - 1]?.id; + const cursor = data?.recentPosts[data?.recentPosts.length - 1]?.id; useScrollPagination({ cursor, From 4d196e77bf63121427172c80aeb62c978c4b3415 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 19:35:00 +0900 Subject: [PATCH 014/199] feat: Use no CDN for writepost and editpost --- .github/workflows/main.yml | 1 + .../write/MarkdownEditorContainer.tsx | 9 ++++++-- .../write/PublishActionButtonsContainer.tsx | 13 +++++++++--- src/lib/graphql/client.ts | 21 ++++++++++++++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d253e96b..5250d5b7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,7 @@ jobs: env: REACT_APP_API_HOST: 'https://v2.velog.io/' REACT_APP_GRAPHQL_HOST: 'https://v2cdn.velog.io/' + REACT_APP_GRAPHQL_HOST_NOCDN: 'https://v2.velog.io/' PUBLIC_URL: 'https://static.velog.io/' REACT_APP_REDIS_HOST: ${{ secrets.REDIS_HOST }} CI: false diff --git a/src/containers/write/MarkdownEditorContainer.tsx b/src/containers/write/MarkdownEditorContainer.tsx index 5f802084..8d9b6c1d 100644 --- a/src/containers/write/MarkdownEditorContainer.tsx +++ b/src/containers/write/MarkdownEditorContainer.tsx @@ -42,6 +42,7 @@ import { Helmet } from 'react-helmet-async'; import { toast } from 'react-toastify'; import { usePrevious } from 'react-use'; import { useTheme } from '../../lib/hooks/useTheme'; +import { noCdnClient } from '../../lib/graphql/client'; export type MarkdownEditorContainerProps = {}; @@ -59,10 +60,14 @@ const MarkdownEditorContainer: React.FC = () => { initialTitle, tags, } = useSelector((state: RootState) => state.write); - const [writePost] = useMutation(WRITE_POST); + const [writePost] = useMutation(WRITE_POST, { + client: noCdnClient, + }); const [createPostHistory] = useMutation(CREATE_POST_HISTORY); - const [editPost] = useMutation(EDIT_POST); + const [editPost] = useMutation(EDIT_POST, { + client: noCdnClient, + }); const [lastSavedData, setLastSavedData] = useState({ title: initialTitle, diff --git a/src/containers/write/PublishActionButtonsContainer.tsx b/src/containers/write/PublishActionButtonsContainer.tsx index a832b887..9294cb3a 100644 --- a/src/containers/write/PublishActionButtonsContainer.tsx +++ b/src/containers/write/PublishActionButtonsContainer.tsx @@ -16,10 +16,13 @@ import { useMutation, useApolloClient } from '@apollo/react-hooks'; import { setHeadingId } from '../../lib/heading'; import { useHistory } from 'react-router'; import { toast } from 'react-toastify'; +import { noCdnClient } from '../../lib/graphql/client'; type PublishActionButtonsContainerProps = {}; -const PublishActionButtonsContainer: React.FC = () => { +const PublishActionButtonsContainer: React.FC< + PublishActionButtonsContainerProps +> = () => { const history = useHistory(); const client = useApolloClient(); @@ -49,8 +52,12 @@ const PublishActionButtonsContainer: React.FC(WRITE_POST); - const [editPost] = useMutation(EDIT_POST); + const [writePost] = useMutation(WRITE_POST, { + client: noCdnClient, + }); + const [editPost] = useMutation(EDIT_POST, { + client: noCdnClient, + }); const variables = { title: options.title, diff --git a/src/lib/graphql/client.ts b/src/lib/graphql/client.ts index c6b4588e..ad4e8a7c 100644 --- a/src/lib/graphql/client.ts +++ b/src/lib/graphql/client.ts @@ -7,15 +7,34 @@ const host = ? '/' : process.env.REACT_APP_GRAPHQL_HOST) || '/'; +const noCdnHost = + (process.env.NODE_ENV === 'development' + ? '/' + : process.env.REACT_APP_GRAPHQL_HOST_NOCDN) || '/'; + +const cache = new InMemoryCache().restore((window as any).__APOLLO_STATE__); + const graphqlURI = host.concat('graphql'); +const noCdnGraphqlURI = noCdnHost.concat('graphql'); + const link = createHttpLink({ uri: graphqlURI, credentials: 'include', }); +const noCdnLink = createHttpLink({ + uri: noCdnGraphqlURI, + credentials: 'include', +}); + const client = new ApolloClient({ link, - cache: new InMemoryCache().restore((window as any).__APOLLO_STATE__), + cache, +}); + +export const noCdnClient = new ApolloClient({ + link: noCdnLink, + cache, }); (window as any).client = client; From 22aa4ee97d0e4ff97fa75cc2cad4cf1c914bd666 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 21:23:30 +0900 Subject: [PATCH 015/199] fix: :bug: Fixes SSR WritePage Error by using noCdnClient via context --- .../write/MarkdownEditorContainer.tsx | 7 ++--- .../write/PublishActionButtonsContainer.tsx | 8 +++--- src/index.tsx | 15 ++++++----- src/lib/graphql/UncachedApolloContext.tsx | 27 +++++++++++++++++++ src/server/serverRender.tsx | 13 +++++---- 5 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 src/lib/graphql/UncachedApolloContext.tsx diff --git a/src/containers/write/MarkdownEditorContainer.tsx b/src/containers/write/MarkdownEditorContainer.tsx index 8d9b6c1d..3307faa0 100644 --- a/src/containers/write/MarkdownEditorContainer.tsx +++ b/src/containers/write/MarkdownEditorContainer.tsx @@ -42,7 +42,7 @@ import { Helmet } from 'react-helmet-async'; import { toast } from 'react-toastify'; import { usePrevious } from 'react-use'; import { useTheme } from '../../lib/hooks/useTheme'; -import { noCdnClient } from '../../lib/graphql/client'; +import { useUncachedApolloClient } from '../../lib/graphql/UncachedApolloContext'; export type MarkdownEditorContainerProps = {}; @@ -60,13 +60,14 @@ const MarkdownEditorContainer: React.FC = () => { initialTitle, tags, } = useSelector((state: RootState) => state.write); + const uncachedClient = useUncachedApolloClient(); const [writePost] = useMutation(WRITE_POST, { - client: noCdnClient, + client: uncachedClient, }); const [createPostHistory] = useMutation(CREATE_POST_HISTORY); const [editPost] = useMutation(EDIT_POST, { - client: noCdnClient, + client: uncachedClient, }); const [lastSavedData, setLastSavedData] = useState({ diff --git a/src/containers/write/PublishActionButtonsContainer.tsx b/src/containers/write/PublishActionButtonsContainer.tsx index 9294cb3a..4b59cfaa 100644 --- a/src/containers/write/PublishActionButtonsContainer.tsx +++ b/src/containers/write/PublishActionButtonsContainer.tsx @@ -16,7 +16,7 @@ import { useMutation, useApolloClient } from '@apollo/react-hooks'; import { setHeadingId } from '../../lib/heading'; import { useHistory } from 'react-router'; import { toast } from 'react-toastify'; -import { noCdnClient } from '../../lib/graphql/client'; +import { useUncachedApolloClient } from '../../lib/graphql/UncachedApolloContext'; type PublishActionButtonsContainerProps = {}; @@ -52,11 +52,13 @@ const PublishActionButtonsContainer: React.FC< dispatch(closePublish()); }, [dispatch]); + const uncachedClient = useUncachedApolloClient(); + const [writePost] = useMutation(WRITE_POST, { - client: noCdnClient, + client: uncachedClient, }); const [editPost] = useMutation(EDIT_POST, { - client: noCdnClient, + client: uncachedClient, }); const variables = { diff --git a/src/index.tsx b/src/index.tsx index 66261d48..c18000b2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -9,13 +9,14 @@ import './typography.css'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; -import client from './lib/graphql/client'; +import client, { noCdnClient } from './lib/graphql/client'; import rootReducer from './modules'; import storage from './lib/storage'; import { setUser } from './modules/core'; import * as Sentry from '@sentry/browser'; import { HelmetProvider } from 'react-helmet-async'; import darkMode from './modules/darkMode'; +import { UncachedApolloProvider } from './lib/graphql/UncachedApolloContext'; Sentry.init({ dsn: 'https://99d0ac3ca0f64b4d8709e385e7692893@sentry.io/1886813', @@ -66,11 +67,13 @@ if (process.env.NODE_ENV === 'production') { ReactDOM.render( - - - - - + + + + + + + , document.getElementById('root'), diff --git a/src/lib/graphql/UncachedApolloContext.tsx b/src/lib/graphql/UncachedApolloContext.tsx new file mode 100644 index 00000000..d9d658e4 --- /dev/null +++ b/src/lib/graphql/UncachedApolloContext.tsx @@ -0,0 +1,27 @@ +import { ApolloClient } from 'apollo-boost'; +import React, { createContext } from 'react'; + +const UncachedApolloContext = createContext | null>(null); +export function UncachedApolloProvider({ + client, + children, +}: { + client: ApolloClient; + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} + +export function useUncachedApolloClient() { + const value = React.useContext(UncachedApolloContext); + if (value === null) { + throw new Error( + 'useUncachedApolloClient must be used within a UncachedApolloProvider', + ); + } + return value; +} diff --git a/src/server/serverRender.tsx b/src/server/serverRender.tsx index fdfea249..d53bcfb0 100644 --- a/src/server/serverRender.tsx +++ b/src/server/serverRender.tsx @@ -18,6 +18,7 @@ import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server'; import CacheManager from './CacheManager'; import { HelmetProvider, FilledContext } from 'react-helmet-async'; import error from '../modules/error'; +import { UncachedApolloProvider } from '../lib/graphql/UncachedApolloContext'; const statsFile = path.resolve(__dirname, '../build/loadable-stats.json'); const cacheManager = new CacheManager(); @@ -87,11 +88,13 @@ const serverRender = async ({ url, loggedIn, cookie }: SSROption) => { - - - - - + + + + + + + From b303dd47f557f7aacb79a91de83f1dfb8080726c Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 6 Mar 2022 21:33:10 +0900 Subject: [PATCH 016/199] fix: :bug: Fixes missing context on ssr --- src/index.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index c18000b2..9a65dfaf 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -53,11 +53,13 @@ if (process.env.NODE_ENV === 'production') { ReactDOM.hydrate( - - - - - + + + + + + + , document.getElementById('root'), From ebf08402906ca4667c3609cc7d056f3ce237bd19 Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 2 Apr 2022 15:14:06 +0900 Subject: [PATCH 017/199] fix: optimize image for media.vlpt.us --- src/lib/optimizeImage.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/optimizeImage.ts b/src/lib/optimizeImage.ts index fb91bf72..ff405d12 100644 --- a/src/lib/optimizeImage.ts +++ b/src/lib/optimizeImage.ts @@ -1,5 +1,11 @@ export default function optimizeImage(url: string, width?: number) { - if (!url.includes('https://images.velog.io/')) return url; + if ( + !url.includes('https://images.velog.io/') && + !url.includes('https://media.vlpt.us/') + ) + return url; + + if (url.includes('?')) return url; if (url.includes('.svg')) return url; let replaced = url.replace('://images.velog.io', '://media.vlpt.us'); // Cloudflare From 9c5bbd461604308958290c081c4799a723141642 Mon Sep 17 00:00:00 2001 From: velopert Date: Mon, 4 Apr 2022 02:54:11 +0900 Subject: [PATCH 018/199] feat: cloudflare image upload --- src/components/setting/SettingUserProfile.tsx | 6 +- src/components/write/WriteMarkdownEditor.tsx | 32 ++++++- .../setting/SettingUserProfileContainer.tsx | 18 ++-- .../write/MarkdownEditorContainer.tsx | 85 ++++++++++++++----- .../write/PublishPreviewContainer.tsx | 69 +++++++++++++-- src/lib/api/files.ts | 27 ++++++ src/lib/hooks/useCFUpload.ts | 22 +++++ 7 files changed, 222 insertions(+), 37 deletions(-) create mode 100644 src/lib/hooks/useCFUpload.ts diff --git a/src/components/setting/SettingUserProfile.tsx b/src/components/setting/SettingUserProfile.tsx index 5fcab0d3..6d69665b 100644 --- a/src/components/setting/SettingUserProfile.tsx +++ b/src/components/setting/SettingUserProfile.tsx @@ -17,6 +17,7 @@ export type SettingUserProfileProps = { thumbnail: string | null; displayName: string; shortBio: string; + loading: boolean; }; function SettingUserProfile({ @@ -26,6 +27,7 @@ function SettingUserProfile({ thumbnail, displayName, shortBio, + loading, }: SettingUserProfileProps) { const [edit, onToggleEdit] = useToggle(false); const [inputs, onChange] = useInputs({ @@ -45,7 +47,9 @@ function SettingUserProfile({ src={optimizeImage(thumbnail || userThumbnail, 400)} alt="profile" /> - + diff --git a/src/components/write/WriteMarkdownEditor.tsx b/src/components/write/WriteMarkdownEditor.tsx index 3ba9119c..46e45543 100644 --- a/src/components/write/WriteMarkdownEditor.tsx +++ b/src/components/write/WriteMarkdownEditor.tsx @@ -34,6 +34,7 @@ export interface MarkdownEditorProps { lastUploadedImage: string | null; initialBody: string; theme: 'light' | 'dark'; + tempBlobImage: string | null; } type MarkdownEditorState = { @@ -239,7 +240,29 @@ export default class WriteMarkdownEditor extends React.Component< return; } if (!this.codemirror) return; - this.codemirror.getDoc().replaceSelection(`![](${encodeURI(image)})`); + const lines = this.codemirror.getValue().split('\n'); + const lineIndex = lines.findIndex((l) => l.includes('![업로드중..]')); + if (lineIndex === -1) return; + + const startCh = lines[lineIndex].indexOf('![업로드중..]'); + this.codemirror + .getDoc() + .replaceRange( + `![](${encodeURI(image)})`, + { line: lineIndex, ch: startCh }, + { line: lineIndex, ch: lines[lineIndex].length }, + ); + // this.codemirror.getDoc().replaceSelection(`![](${encodeURI(image)})`); + }; + + addTempImageBlobToEditor = (blobUrl: string) => { + const imageMarkdown = `![업로드중..](${blobUrl})\n`; + + if (this.isIOS) { + return; + } + if (!this.codemirror) return; + this.codemirror.getDoc().replaceSelection(imageMarkdown); }; componentDidUpdate(prevProps: MarkdownEditorProps) { @@ -255,6 +278,13 @@ export default class WriteMarkdownEditor extends React.Component< ) { this.addImageToEditor(lastUploadedImage); } + + if ( + this.props.tempBlobImage && + this.props.tempBlobImage !== prevProps.tempBlobImage + ) { + this.addTempImageBlobToEditor(this.props.tempBlobImage); + } } componentDidMount() { diff --git a/src/containers/setting/SettingUserProfileContainer.tsx b/src/containers/setting/SettingUserProfileContainer.tsx index fde416c9..d0e0117b 100644 --- a/src/containers/setting/SettingUserProfileContainer.tsx +++ b/src/containers/setting/SettingUserProfileContainer.tsx @@ -1,25 +1,30 @@ -import React from 'react'; +import React, { useState } from 'react'; import SettingUserProfile from '../../components/setting/SettingUserProfile'; import useUpload from '../../lib/hooks/useUpload'; -import useS3Upload from '../../lib/hooks/useS3Upload'; import useUserProfile from './hooks/useUserProfile'; import useUpdateThumbnail from './hooks/useUpdateThumbnail'; import useUser from '../../lib/hooks/useUser'; import RequireLogin from '../../components/common/RequireLogin'; +import { useCFUpload } from '../../lib/hooks/useCFUpload'; export type SettingUserProfileContainerProps = {}; function SettingUserProfileContainer(props: SettingUserProfileContainerProps) { const user = useUser(); - const { profile, update } = useUserProfile(); + const { profile, update } = useUserProfile(); const [upload] = useUpload(); - const [s3Upload] = useS3Upload(); + const { upload: cfUpload } = useCFUpload(); const updateThumbnail = useUpdateThumbnail(); + const [imageBlobUrl, setImageBlobUrl] = React.useState(null); + const [loading, setLoading] = useState(false); const uploadThumbnail = async () => { const file = await upload(); if (!file) return; - const image = await s3Upload(file, { type: 'profile' }); + setLoading(true); + setImageBlobUrl(URL.createObjectURL(file)); + const image = await cfUpload(file, { type: 'profile' }); + setLoading(false); if (!image) return; updateThumbnail(image); }; @@ -45,7 +50,8 @@ function SettingUserProfileContainer(props: SettingUserProfileContainerProps) { onClearThumbnail={clearThumbnail} displayName={profile.display_name} shortBio={profile.short_bio} - thumbnail={profile.thumbnail} + thumbnail={imageBlobUrl || profile.thumbnail} + loading={loading} /> ); } diff --git a/src/containers/write/MarkdownEditorContainer.tsx b/src/containers/write/MarkdownEditorContainer.tsx index 3307faa0..76b23aa3 100644 --- a/src/containers/write/MarkdownEditorContainer.tsx +++ b/src/containers/write/MarkdownEditorContainer.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import MarkdownEditor from '../../components/write/WriteMarkdownEditor'; import { useSelector, useDispatch, shallowEqual } from 'react-redux'; import { RootState } from '../../modules'; @@ -21,7 +21,6 @@ import strip from 'strip-markdown'; import TagInputContainer from './TagInputContainer'; import WriteFooter from '../../components/write/WriteFooter'; import useUpload from '../../lib/hooks/useUpload'; -import useS3Upload from '../../lib/hooks/useS3Upload'; import DragDropUpload from '../../components/common/DragDropUpload'; import PasteUpload from '../../components/common/PasteUpload'; import { bindActionCreators } from 'redux'; @@ -43,6 +42,7 @@ import { toast } from 'react-toastify'; import { usePrevious } from 'react-use'; import { useTheme } from '../../lib/hooks/useTheme'; import { useUncachedApolloClient } from '../../lib/graphql/UncachedApolloContext'; +import { useCFUpload } from '../../lib/hooks/useCFUpload'; export type MarkdownEditorContainerProps = {}; @@ -64,6 +64,9 @@ const MarkdownEditorContainer: React.FC = () => { const [writePost] = useMutation(WRITE_POST, { client: uncachedClient, }); + + const bodyRef = useRef(initialBody); + const titleRef = useRef(title); const [createPostHistory] = useMutation(CREATE_POST_HISTORY); const [editPost] = useMutation(EDIT_POST, { @@ -132,15 +135,10 @@ const MarkdownEditorContainer: React.FC = () => { }, [actionCreators, markdown]); const [upload, file] = useUpload(); - const [s3Upload, image] = useS3Upload(); - const prevImage = usePrevious(image); - useEffect(() => { - if (!file) return; - s3Upload(file, { - type: 'post', - }); - }, [file, s3Upload]); + const { upload: cfUpload, image } = useCFUpload(); + const prevImage = usePrevious(image); + const [imageBlobUrl, setImageBlobUrl] = useState(null); useEffect(() => { if (image !== prevImage && !thumbnail && image) { @@ -148,15 +146,6 @@ const MarkdownEditorContainer: React.FC = () => { } }, [actionCreators, image, prevImage, thumbnail]); - const onDragDropUpload = useCallback( - (file: File) => { - s3Upload(file, { - type: 'post', - }); - }, - [s3Upload], - ); - const onTempSave = useCallback( async (notify?: boolean) => { if (!title || !markdown) { @@ -189,7 +178,6 @@ const MarkdownEditorContainer: React.FC = () => { dispatch(setWritePostId(id)); history.replace(`/write?id=${id}`); notifySuccess(); - return; } // tempsaving unreleased post: if (isTemp) { @@ -246,6 +234,57 @@ const MarkdownEditorContainer: React.FC = () => { ], ); + const uploadWithPostId = useCallback( + async (file: File) => { + if (!file) return; + let id = postId; + if (!id) { + const title = titleRef.current || 'Temp Title'; + const body = bodyRef.current || 'Temp Body'; + + const response = await writePost({ + variables: { + title, + body, + tags: [], + is_markdown: true, + is_temp: true, + is_private: false, + url_slug: escapeForUrl(title), + thumbnail: null, + meta: {}, + series_id: null, + }, + }); + if (!response || !response.data) return; + id = response.data.writePost.id; + dispatch(setWritePostId(id)); + history.replace(`/write?id=${id}`); + } + if (!id) return; + + const url = URL.createObjectURL(file); + setImageBlobUrl(url); + + cfUpload(file, { type: 'post', refId: id }).catch((e) => { + toast.error('이미지 업로드 실패! 잠시 후 다시 시도하세요.'); + }); + }, + [postId, cfUpload, writePost, dispatch, history], + ); + + const onDragDropUpload = useCallback( + (file: File) => { + uploadWithPostId(file); + }, + [uploadWithPostId], + ); + + useEffect(() => { + if (!file) return; + uploadWithPostId(file); + }, [file, uploadWithPostId]); + useEffect(() => { const changed = !shallowEqual(lastSavedData, { title, body: markdown }); if (changed) { @@ -267,6 +306,11 @@ const MarkdownEditorContainer: React.FC = () => { ); const theme = useTheme(); + useEffect(() => { + bodyRef.current = markdown; + titleRef.current = title; + }, [markdown, title]); + if (!isReady) return null; return ( @@ -276,6 +320,7 @@ const MarkdownEditorContainer: React.FC = () => { ({ title: state.write.title, @@ -38,9 +46,16 @@ const PublishPreviewContainer: React.FC = ({ (description: string) => changeDescription(description), [changeDescription], ); - + const uncachedClient = useUncachedApolloClient(); + const [writePost] = useMutation(WRITE_POST, { + client: uncachedClient, + }); const [upload, file] = useUpload(); - const [s3Upload, image] = useS3Upload(); + const { upload: cfUpload, image } = useCFUpload(); + + const { markdown, postId, tags } = useSelector( + (state: RootState) => state.write, + ); // fills description with defaultDescription when it is empty useEffect(() => { @@ -57,12 +72,48 @@ const PublishPreviewContainer: React.FC = ({ upload(); }; + const dispatch = useDispatch(); + + const getValidPostId = useCallback(async () => { + if (postId) return postId; + const validTitle = title || 'Temp Title'; + const validBody = markdown || 'Temp Body'; + + const response = await writePost({ + variables: { + title: validTitle, + body: validBody, + tags, + is_markdown: true, + is_temp: true, + is_private: false, + url_slug: escapeForUrl(validTitle), + thumbnail: null, + meta: {}, + series_id: null, + }, + }); + + if (!response || !response.data) return null; + const id = response.data.writePost.id; + dispatch(setWritePostId(id)); + return id; + }, [postId, writePost, markdown, tags, title, dispatch]); + + const uploadWithPostId = useCallback( + async (file: File) => { + const id = await getValidPostId(); + if (!id) return; + cfUpload(file, { type: 'post', refId: id }); + }, + [cfUpload, getValidPostId], + ); + useEffect(() => { if (!file) return; - s3Upload(file, { - type: 'post', - }); - }, [file, s3Upload]); + uploadWithPostId(file); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [file]); useEffect(() => { if (!image) return; diff --git a/src/lib/api/files.ts b/src/lib/api/files.ts index 7728fb5b..1698d8e5 100644 --- a/src/lib/api/files.ts +++ b/src/lib/api/files.ts @@ -16,3 +16,30 @@ export const createSignedUrl = (info: { }) => { return apiClient.post('/api/v2/files/create-url', info); }; + +export async function uploadImage( + file: File, + info: { type: 'post' | 'profile'; refId?: string }, + onUploadProgress?: (value: number) => void, +) { + const formData = new FormData(); + formData.append('image', file); + formData.append('type', info.type); + if (info.refId) { + formData.append('ref_id', info.refId); + } + const response = await apiClient.post<{ path: string }>( + '/api/v2/files/upload', + formData, + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + onUploadProgress(e) { + console.log(e); + }, + }, + ); + + return response.data; +} diff --git a/src/lib/hooks/useCFUpload.ts b/src/lib/hooks/useCFUpload.ts new file mode 100644 index 00000000..1b4da8a2 --- /dev/null +++ b/src/lib/hooks/useCFUpload.ts @@ -0,0 +1,22 @@ +import { useCallback, useState } from 'react'; +import { uploadImage } from '../api/files'; +import { useJazzbar } from '../jazzbar'; + +export function useCFUpload() { + const [setProgress] = useJazzbar(); + const [image, setImage] = useState(null); + + const upload = useCallback( + async (file: File, info: { type: 'post' | 'profile'; refId?: string }) => { + const data = await uploadImage(file, info, setProgress); + setImage(data.path); + return data.path; + }, + [setProgress], + ); + + return { + image, + upload, + }; +} From 641636e6137696ceef5401ab1ba4f44f75704373 Mon Sep 17 00:00:00 2001 From: velopert Date: Mon, 4 Apr 2022 02:54:17 +0900 Subject: [PATCH 019/199] update: .env --- .env | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 90607079..589fc0af 100644 --- a/.env +++ b/.env @@ -1,2 +1,4 @@ REACT_APP_API_HOST=http://localhost:5000/ -PUBLIC_URL=/ \ No newline at end of file +PUBLIC_URL=/ +REACT_APP_GRAPHQL_HOST=https://v2cdn.velog.io/ +REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ \ No newline at end of file From 524138632ed0c3c15b9af790f149b36a21a96e2e Mon Sep 17 00:00:00 2001 From: velopert Date: Mon, 4 Apr 2022 03:04:32 +0900 Subject: [PATCH 020/199] feat: optimize image for cloudflare --- src/lib/optimizeImage.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib/optimizeImage.ts b/src/lib/optimizeImage.ts index ff405d12..867f9419 100644 --- a/src/lib/optimizeImage.ts +++ b/src/lib/optimizeImage.ts @@ -1,4 +1,7 @@ export default function optimizeImage(url: string, width?: number) { + if (url.includes('imagedelivery.net')) { + return optimizeImageForCloudflare(url, width); + } if ( !url.includes('https://images.velog.io/') && !url.includes('https://media.vlpt.us/') @@ -16,3 +19,13 @@ export default function optimizeImage(url: string, width?: number) { } return replaced.concat(`?w=${width}`); } + +export function optimizeImageForCloudflare(url: string, width?: number) { + if (!width) return url; + const replacer = (variant: string) => url.replace('public', variant); + if (width === 640) return replacer('w640'); + if (width === 768) return replacer('w768'); + if (width === 240) return replacer('512x512'); + if (width === 120) return replacer('256x256'); + return url; +} From 3e7326c3d789f5af3aa75da436438d0122e7a2d8 Mon Sep 17 00:00:00 2001 From: velopert Date: Tue, 5 Apr 2022 03:01:02 +0900 Subject: [PATCH 021/199] fix: clear user thumbnail --- src/containers/setting/SettingUserProfileContainer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/containers/setting/SettingUserProfileContainer.tsx b/src/containers/setting/SettingUserProfileContainer.tsx index d0e0117b..abbc0b15 100644 --- a/src/containers/setting/SettingUserProfileContainer.tsx +++ b/src/containers/setting/SettingUserProfileContainer.tsx @@ -31,6 +31,7 @@ function SettingUserProfileContainer(props: SettingUserProfileContainerProps) { const clearThumbnail = () => { updateThumbnail(null); + setImageBlobUrl(null); }; const onUpdate = (params: { displayName: string; shortBio: string }) => { From b8c9914c4c10ddbb082da2136420cd6299ac480d Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 9 Apr 2022 02:54:02 +0900 Subject: [PATCH 022/199] feat: remove CF optimize --- src/lib/optimizeImage.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/lib/optimizeImage.ts b/src/lib/optimizeImage.ts index 867f9419..ff405d12 100644 --- a/src/lib/optimizeImage.ts +++ b/src/lib/optimizeImage.ts @@ -1,7 +1,4 @@ export default function optimizeImage(url: string, width?: number) { - if (url.includes('imagedelivery.net')) { - return optimizeImageForCloudflare(url, width); - } if ( !url.includes('https://images.velog.io/') && !url.includes('https://media.vlpt.us/') @@ -19,13 +16,3 @@ export default function optimizeImage(url: string, width?: number) { } return replaced.concat(`?w=${width}`); } - -export function optimizeImageForCloudflare(url: string, width?: number) { - if (!width) return url; - const replacer = (variant: string) => url.replace('public', variant); - if (width === 640) return replacer('w640'); - if (width === 768) return replacer('w768'); - if (width === 240) return replacer('512x512'); - if (width === 120) return replacer('256x256'); - return url; -} From 93fada62e83344ccef760384467af20d6d1220b4 Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 9 Apr 2022 02:54:08 +0900 Subject: [PATCH 023/199] fix: image upload twice when uploaed via button --- src/containers/write/MarkdownEditorContainer.tsx | 9 +++++++-- src/lib/hooks/useUpload.tsx | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/containers/write/MarkdownEditorContainer.tsx b/src/containers/write/MarkdownEditorContainer.tsx index 76b23aa3..bae00814 100644 --- a/src/containers/write/MarkdownEditorContainer.tsx +++ b/src/containers/write/MarkdownEditorContainer.tsx @@ -234,10 +234,15 @@ const MarkdownEditorContainer: React.FC = () => { ], ); + const postIdRef = useRef(postId); + useEffect(() => { + postIdRef.current = postId; + }, [postId]); + const uploadWithPostId = useCallback( async (file: File) => { if (!file) return; - let id = postId; + let id = postIdRef.current; if (!id) { const title = titleRef.current || 'Temp Title'; const body = bodyRef.current || 'Temp Body'; @@ -270,7 +275,7 @@ const MarkdownEditorContainer: React.FC = () => { toast.error('이미지 업로드 실패! 잠시 후 다시 시도하세요.'); }); }, - [postId, cfUpload, writePost, dispatch, history], + [cfUpload, writePost, dispatch, history], ); const onDragDropUpload = useCallback( diff --git a/src/lib/hooks/useUpload.tsx b/src/lib/hooks/useUpload.tsx index a75498e2..a8698b39 100644 --- a/src/lib/hooks/useUpload.tsx +++ b/src/lib/hooks/useUpload.tsx @@ -5,6 +5,7 @@ const useUpload = () => { const upload = useCallback(() => { const promise = new Promise((resolve, reject) => { const input = document.createElement('input'); + input.accept = 'image/*'; const timeout = setTimeout(reject, 1000 * 60 * 3); input.type = 'file'; From 33831038beeba44273de2d256e97baa7ec9826af Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 10 Apr 2022 04:26:16 +0900 Subject: [PATCH 024/199] feat: replace image.velog.io to velog.velcdn.com --- src/lib/optimizeImage.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/lib/optimizeImage.ts b/src/lib/optimizeImage.ts index ff405d12..a5897fd6 100644 --- a/src/lib/optimizeImage.ts +++ b/src/lib/optimizeImage.ts @@ -1,4 +1,10 @@ -export default function optimizeImage(url: string, width?: number) { +export default function optimizeImage( + url: string, + /** + * S3 -> velcdn으로 넘어가면서 당장은 비활성화 상태. 나중에 다시 돌려놓을 예정 + */ + width?: number, +) { if ( !url.includes('https://images.velog.io/') && !url.includes('https://media.vlpt.us/') @@ -8,11 +14,22 @@ export default function optimizeImage(url: string, width?: number) { if (url.includes('?')) return url; if (url.includes('.svg')) return url; - let replaced = url.replace('://images.velog.io', '://media.vlpt.us'); // Cloudflare + let replaced = url.replace('://images.velog.io', '://velog.velcdn.com'); + + // since b2 requires different encoding.. + const filename = replaced.split('/').pop() ?? ''; + const proeperlyEncoded = encodeURIComponent(decodeURI(filename)); + replaced = replaced.replace(filename, proeperlyEncoded); + return replaced; + + // disabled for now + // let replaced = url.replace('://images', '://img'); // Cloudfront - if (!width) { - return replaced; - } - return replaced.concat(`?w=${width}`); + // if (!width) { + // return replaced; + // } + // return replaced.concat(`?w=${width}`); } + +// https://velog.velcdn.com/images/city7310/post/95db2b3d-44a1-4671-a4ca-af0b2b70bca2/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7,%202021-04-18%2021-09-35.png From 43d2a7710fd0473ca139f5b0dc8852b4df0d18c0 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 10 Apr 2022 07:01:08 +0900 Subject: [PATCH 025/199] feat: replace images.velog.io inside post content --- src/components/post/PostContent.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/post/PostContent.tsx b/src/components/post/PostContent.tsx index 9e62c495..1b894d12 100644 --- a/src/components/post/PostContent.tsx +++ b/src/components/post/PostContent.tsx @@ -29,9 +29,9 @@ function optimizeImagesFromPost(markdown: string) { /(?:!\[(.*?)\]\(https:\/\/images.velog.io\/(.*?)\))/g, ); if (!matches) return markdown; - const replacers = matches.map(match => [ + const replacers = matches.map((match) => [ match, - match.replace('https://images.velog.io/', 'https://media.vlpt.us/'), + match.replace('https://images.velog.io/', 'https://velog.velcdn.com/'), ]); return replacers.reduce((acc, [original, optimized]) => { return acc.replace(original, optimized); @@ -41,9 +41,10 @@ function optimizeImagesFromPost(markdown: string) { const PostContent: React.FC = ({ isMarkdown, body }) => { const [html, setHtml] = useState(isMarkdown ? null : body); const dispatch = usePostViewerDispatch(); - const imageOptimizedPost = useMemo(() => optimizeImagesFromPost(body), [ - body, - ]); + const imageOptimizedPost = useMemo( + () => optimizeImagesFromPost(body), + [body], + ); useEffect(() => { if (!html) return; From 0dea70ea913f39c60fef95db89c784f180385cc8 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 10 Apr 2022 07:10:42 +0900 Subject: [PATCH 026/199] feat: handle encode issue for images inside post --- src/components/post/PostContent.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/post/PostContent.tsx b/src/components/post/PostContent.tsx index 1b894d12..e55f6508 100644 --- a/src/components/post/PostContent.tsx +++ b/src/components/post/PostContent.tsx @@ -29,10 +29,19 @@ function optimizeImagesFromPost(markdown: string) { /(?:!\[(.*?)\]\(https:\/\/images.velog.io\/(.*?)\))/g, ); if (!matches) return markdown; - const replacers = matches.map((match) => [ - match, - match.replace('https://images.velog.io/', 'https://velog.velcdn.com/'), - ]); + console.log(matches); + const replacers = matches.map((match) => { + const filename = + match.match(/https:\/\/images.velog.io\/(.*?)\)/)?.[1] ?? ''; + console.log(filename); + const proeperlyEncoded = encodeURIComponent(decodeURI(filename)); + return [ + match, + match + .replace('https://images.velog.io/', 'https://velog.velcdn.com/') + .replace(filename, proeperlyEncoded), + ]; + }); return replacers.reduce((acc, [original, optimized]) => { return acc.replace(original, optimized); }, markdown); From dca2ca6323bd3b13e7e2d84e49d220754e4dcb81 Mon Sep 17 00:00:00 2001 From: velopert Date: Thu, 5 May 2022 23:22:22 +0900 Subject: [PATCH 027/199] updates ads.txt --- src/index.server.ts | 337 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 336 insertions(+), 1 deletion(-) diff --git a/src/index.server.ts b/src/index.server.ts index 0607bf2c..d4e8c278 100644 --- a/src/index.server.ts +++ b/src/index.server.ts @@ -23,7 +23,342 @@ app.use(router.routes()).use(router.allowedMethods()); app.use(ssrMiddleware); app.use(proxyMiddleware); router.get('/ads.txt', (ctx) => { - ctx.body = 'google.com, pub-5574866530496701, DIRECT, f08c47fec0942fa0'; + ctx.body = `google.com, pub-5574866530496701, DIRECT, f08c47fec0942fa0 +setupad.com, 1557, DIRECT +google.com, pub-7383171830614216, RESELLER, f08c47fec0942fa0 +google.com, pub-3970277535528613, RESELLER, f08c47fec0942fa0 +rubiconproject.com, 13606, RESELLER, 0bfd66d529a55807 +rubiconproject.com, 21118, RESELLER, 0bfd66d529a55807 +appnexus.com, 3711, RESELLER +adform.com, 1484, RESELLER +pubmatic.com, 156191, RESELLER, 5d62403b186f2ace +sovrn.com, 252889, RESELLER, fafdf38b16bf6b2b +lijit.com, 252889, RESELLER, fafdf38b16bf6b2b +appnexus.com, 1360, RESELLER, f5ab79cb980f11d1 +gumgum.com, 11645, RESELLER, ffdef49475d318a9 +openx.com, 537120960, RESELLER +openx.com, 539924617, RESELLER, 6a698e2ec38604c6 +openx.com, 83499, RESELLER +openx.com, 538959099, RESELLER, 6a698e2ec38604c6 +openx.com, 540890784, DIRECT, 6a698e2ec38604c6 +pubmatic.com, 137711, RESELLER, 5d62403b186f2ace +pubmatic.com, 156212, RESELLER, 5d62403b186f2ace +pubmatic.com, 156700, RESELLER, 5d62403b186f2ace +rubiconproject.com, 17960, RESELLER, 0bfd66d529a55807 +pubmatic.com, 62483, RESELLER +contextweb.com, 558511, RESELLER +emxdgt.com, 242, DIRECT, 1e1d41537f7cad7f +appnexus.com, 1356, RESELLER, f5ab79cb980f11d1 +smartadserver.com, 1776, DIRECT +contextweb.com, 561376, RESELLER, 89ff185a4c4e857c +admixer.net, E1FD631C-1259-4EF3-A0F3-452A8B59DCB2, DIRECT +improvedigital.com, 803, DIRECT +indexexchange.com, 186102, DIRECT +adocean-global.com, 83598, DIRECT +districtm.io, 101521, DIRECT +google.com, pub-9685734445476814, RESELLER, f08c47fec0942fa0 +RTBHOUSE.COM, d2380d6f45eaac2c7d22, DIRECT +betweendigital.com, 35081, DIRECT +aps.amazon.com,d14c8d3d-c09a-40c7-8c08-b5d7cd1d7fac,DIRECT +pubmatic.com,157150,RESELLER,5d62403b186f2ace +openx.com,540191398,RESELLER,6a698e2ec38604c6 +rubiconproject.com,18020,RESELLER,0bfd66d529a55807 +appnexus.com,1908,RESELLER,f5ab79cb980f11d1 +smaato.com,1100044650,RESELLER,07bcf65f187117b4 +adtech.com,12068,RESELLER,e1a5b5b6e3255540 +ad-generation.jp,12474,RESELLER,7f4ea9029ac04e53 +districtm.io,100962,RESELLER,3fd707be9c4527c3 +appnexus.com,3663,RESELLER,f5ab79cb980f11d1 +rhythmone.com,1654642120,RESELLER,a670c89d4a324e47 +yahoo.com,55029,RESELLER,e1a5b5b6e3255540 +gumgum.com,14141,RESELLER,ffdef49475d318a9 +pubmatic.com, 160006, RESELLER, 5d62403b186f2ace +pubmatic.com,160096,RESELLER,5d62403b186f2ace +yieldmo.com,2719019867620450718,RESELLER +yahoo.com,55774,DIRECT +emxdgt.com,2009,RESELLER,1e1d41537f7cad7f +smartadserver.com,4125,RESELLER,060d053dcf45cbf3 +themediagrid.com,jtqkmp,RESELLER,35d5010d7789b49d +sovrn.com,375328,RESELLER,fafdf38b16bf6b2b +lijit.com,375328,RESELLER,fafdf38b16bf6b2b +aps.amazon.com, dce921de-ffa9-46de-b23a-dcb73faca71d, DIRECT +sharethrough.com,7144eb80,RESELLER,d53b998a7bd4ecd2 +contextweb.com,562541,RESELLER,89ff185a4c4e857c +pubmatic.com,161085,DIRECT,5d62403b186f2ace +beachfront.com,14804,RESELLER,e2541279e8e2ca4d +improvedigital.com,2050,RESELLER +sonobi.com,7f5fa520f8,RESELLER,d1a215d9eb5aee9e +mintegral.com,10043,RESELLER,0aeed750c80d6423 +lijit.com, 252889-eb, DIRECT, fafdf38b16bf6b2b +smartadserver.com, 3527, DIRECT +contextweb.com, 560288, RESELLER, 89ff185a4c4e857c +pubmatic.com, 156439, RESELLER +pubmatic.com, 154037, RESELLER +rubiconproject.com, 16114, RESELLER, 0bfd66d529a55807 +openx.com, 537149888, RESELLER, 6a698e2ec38604c6 +appnexus.com, 3703, RESELLER, f5ab79cb980f11d1 +districtm.io, 101760, RESELLER, 3fd707be9c4527c3 +loopme.com, s-2411, RESELLER, 6c8d5f95897a5a3b +loopme.com, 5679, RESELLER, 6c8d5f95897a5a3b +xad.com, 958, RESELLER, 81cbf0a75a5e0e9a +indexexchange.com, 191541, RESELLER +#Admixer +admixer.net, 099e1459-d18f-4339-931b-2dab1bbc8a7d, RESELLER +yieldnexus.com, 96204, RESELLER +audience.media, 100358, RESELLER +appnexus.com, 10617, RESELLER +appnexus.com, 9393, RESELLER +advertising.com, 25034, RESELLER +admanmedia.com, 584, DIRECT +video.unrulymedia.com, 3948367200, RESELLER +inmobi.com, 3a4f7da341dd490cbb7dde02b126275e, RESELLER, 83e75a7ae333ca9d +sonobi.com, 7b37f8ccbc, RESELLER, d1a215d9eb5aee9e +improvedigital.com, 1668, RESELLER +indexexchange.com, 191497, RESELLER +pubmatic.com, 157562, DIRECT, 5d62403b186f2ace +advertising.com, 25001, RESELLER, e1a5b5b6e3255540 +rubiconproject.com, 18224, RESELLER, 0bfd66d529a55807 +spotxchange.com, 119135, RESELLER, 7842df1d2fe2db34 +spotx.tv, 119135, RESELLER, 7842df1d2fe2db34 +smartadserver.com, 1894, RESELLER +smartadserver.com, 3445, RESELLER +rhythmone.com, 1816189007, RESELLER, a670c89d4a324e47 +betweendigital.com, 43070, DIRECT +rubiconproject.com, 19724, RESELLER, 0bfd66d529a55807 +google.com, pub-5289985627731322, RESELLER, f08c47fec0942fa0 +#Adagio +adagio.io, 1053, DIRECT +rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 +pubmatic.com, 159110, RESELLER, 5d62403b186f2ace +improvedigital.com, 1790, RESELLER +onetag.com, 6b859b96c564fbe, RESELLER +indexexchange.com, 194558, RESELLER +pubwise.io, 68867843, RESELLER, c327c91a93a7cdd3 +richaudience.com, 1BTOoaD22a, DIRECT +#33Across +33across.com, 0010b00002bTS1QAAW, DIRECT, bbea06d9c4d2853c +adtech.com, 12094, RESELLER +adtech.com, 9993, RESELLER +aol.com, 47594, RESELLER, e1a5b5b6e3255540 +yahoo.com, 55188, DIRECT, e1a5b5b6e3255540 +advangelists.com, 8d3bba7425e7c98c50f52ca1b52d3735, RESELLER, 60d26397ec060f98 +sonobi.com, a416546bb7, RESELLER, d1a215d9eb5aee9e +yoc.com, 5600, DIRECT +#JustPremium +appnexus.com, 7118, RESELLER +spotx.tv, 108933, RESELLER, 7842df1d2fe2db34 +spotxchange.com, 108933, RESELLER, 7842df1d2fe2db34 +improvedigital.com, 185, RESELLER +adform.com, 183, RESELLER +freewheel.tv, 33081, RESELLER +freewheel.tv, 33601, RESELLER +indexexchange.com, 189872, RESELLER +openx.com, 540925214, RESELLER, 6a698e2ec38604c6 +google.com, pub-8172268348509349, RESELLER, f08c47fec0942fa0 +#Eskimi +eskimi.com, eas-2020000001, DIRECT +#RhythmOne +video.unrulymedia.com, 5831063052797481612, DIRECT +rhythmone.com, 5831063052797481612, DIRECT, a670c89d4a324e47 +indexexchange.com, 182257, RESELLER +appnexus.com, 6849, RESELLER +rubiconproject.com, 15268, RESELLER +spotxchange.com, 285547, RESELLER, 7842df1d2fe2db34 +spotx.tv, 285547, RESELLER, 7842df1d2fe2db34 +pubmatic.com, 159277, RESELLER, 5d62403b186f2ace +advertising.com, 28605, RESELLER +improvedigital.com, 1699, RESELLER +#LuponMedia +luponmedia.com, 1994505, DIRECT +adform.com, 1985, DIRECT, 9f5210a2f0999e32 +rubiconproject.com, 12398, DIRECT, 0bfd66d529a55807 +pubmatic.com, 159760, RESELLER, 5d62403b186f2ace +connectad.io, 151, DIRECT +pubmatic.com, 156077, RESELLER, 5d62403b186f2ace +pubmatic.com, 55990, RESELLER, 5d62403b186f2ace +openx.com, 537145117, RESELLER, 6a698e2ec38604c6 +adform.com, 768, RESELLER +indexexchange.com, 190906, DIRECT, 50b1c356f2c5c8fc +appnexus.com, 8738, RESELLER, f5ab79cb980f11d1 +EMXDGT.com, 1138, DIRECT, 1e1d41537f7cad7f +google.com, pub-5995202563537249, RESELLER, f08c47fec0942fa0 +sovrn.com, 244287, DIRECT, fafdf38b16bf6b2b +lijit.com, 244287, DIRECT, fafdf38b16bf6b2b +lijit.com, 244287-eb, DIRECT, fafdf38b16bf6b2b +yahoo.com, 55248, DIRECT +rubiconproject.com, 13132, RESELLER, 0bfd66d529a55807 +rubiconproject.com, 17250, RESELLER, 0bfd66d529a55807 +xad.com, 240, RESELLER, 81cbf0a75a5e0e9a +onetag.com, 5d4e109247a89f6, DIRECT +advertising.com, 28246, RESELLER +sovrn.com, 247505, DIRECT, fafdf38b16bf6b2b +lijit.com, 247505, DIRECT, fafdf38b16bf6b2b +lijit.com, 247505-eb, DIRECT, fafdf38b16bf6b2b +conversantmedia.com, 100264, RESELLER, 03113cd04947736d +adform.com, 2795, RESELLER +admixer.co.kr,1538,RESELLER +betweendigital.com, 43837, RESELLER +ssp.e-volution.ai, AJxF6R108a9M6CaTvK, RESELLER +ssp.logan.ai, LG4, RESELLER +#AMX +amxrtb.com, 105199421, DIRECT +indexexchange.com, 191503, RESELLER +appnexus.com, 11786, RESELLER +appnexus.com, 12290, RESELLER +appnexus.com, 9393, RESELLER #Video #Display +appnexus.com, 3153, RESELLER, f5ab79cb980f11d1 +appnexus.com, 11924, RESELLER, f5ab79cb980f11d1 +lijit.com, 260380, RESELLER, fafdf38b16bf6b2b +sovrn.com, 260380, RESELLER, fafdf38b16bf6b2b +advertising.com, 28305, RESELLER +#NoBid +nobid.io, 22120307115, DIRECT +google.com, pub-1835489473992347, DIRECT, f08c47fec0942fa0 +google.com, pub-1789253751882305, DIRECT, f08c47fec0942fa0 +appnexus.com, 11429, DIRECT, f5ab79cb980f11d1 +rubiconproject.com, 13702, DIRECT, 0bfd66d529a55807 +indexexchange.com, 185104, DIRECT, 50b1c356f2c5c8fc +pubmatic.com, 157898, DIRECT, 5d62403b186f2ace +sovrn.com, 273657, DIRECT, fafdf38b16bf6b2b +lijit.com, 273657, DIRECT, fafdf38b16bf6b2b +adtech.com, 10109, DIRECT +aolcloud.net, 10109, DIRECT +openx.com, 540650310, DIRECT, 6a698e2ec38604c6 +gumgum.com, 13926, DIRECT, ffdef49475d318a9 +33across.com, 0010b00002Mq2FYAAZ, DIRECT, bbea06d9c4d2853c +sonobi.com, bc2afab5f7, DIRECT, d1a215d9eb5aee9e +revcontent.com, 124709, DIRECT +my6sense.com, 9732, RESELLER +criteo.com, 7822, RESELLER +rhythmone.com, 2439829435, RESELLER, a670c89d4a324e47 +emxdgt.com, 326, RESELLER, 1e1d41537f7cad7f +#Adyoulike +adyoulike.com, e5d58aeeb93b1fb77c4e17409eea8a3a, DIRECT +appnexus.com, 9733, RESELLER +rubiconproject.com, 20736, RESELLER, 0bfd66d529a55807 +openx.com, 540847510, RESELLER, 6a698e2ec38604c6 +spotxchange.com, 230037, RESELLER, 7842df1d2fe2db34 +spotx.tv, 230037, RESELLER, 7842df1d2fe2db34 +themediagrid.com, RYIDPE, DIRECT, 35d5010d7789b49d +criteo.com, B-061463, DIRECT, 9fac4a4a87c2a44f +adform.com, 1835, RESELLER, 9f5210a2f0999e32 +admanmedia.com,726,RESELLER +advertising.com, 28949, DIRECT #Verizon - RevNew +smartadserver.com,3447,DIRECT +pubwise.io, 27754564, RESELLER, c327c91a93a7cdd3 #PubWise +onetag.com, 694e68b73971b58, DIRECT +conversantmedia.com, 100257, DIRECT, 03113cd04947736d +pubmatic.com, 160925, RESELLER, 5d62403b186f2ace +smartadserver.com, 4144, DIRECT +smartadserver.com, 4016, DIRECT +smartadserver.com, 4012, DIRECT +smartadserver.com, 4071, DIRECT +smartadserver.com, 4073, DIRECT +smartadserver.com, 4074, DIRECT +indexexchange.com, 194056, DIRECT +rubiconproject.com, 23220, RESELLER, 0bfd66d529a55807 +openx.com, 544005210, RESELLER, 6a698e2ec38604c6 +pubmatic.com, 160623, RESELLER, 5d62403b186f2ace +onetag.com, 753951255855558-OB, DIRECT +sharethrough.com, GBPtKPVp, DIRECT, d53b998a7bd4ecd2 +#Sharethrough +sharethrough.com, c73f63f1, DIRECT, d53b998a7bd4ecd2 +indexexchange.com, 186046, RESELLER +spotxchange.com, 212457, RESELLER +spotx.tv, 212457, RESELLER +pubmatic.com, 156557, RESELLER +pubmatic.com, 158723, RESELLER, 5d62403b186f2ace +rubiconproject.com, 18694, RESELLER, 0bfd66d529a55807 +openx.com, 540274407, RESELLER, 6a698e2ec38604c6 +33across.com, 0013300001kQj2HAAS, RESELLER, bbea06d9c4d2853c +smaato.com, 1100047713, RESELLER, 07bcf65f187117b4 +indexexchange.com, 195658, RESELLER, 50b1c356f2c5c8fc +pubmatic.com, 161085, RESELLER, 5d62403b186f2ace +#BetweenX +openx.com, 541177349, RESELLER, 6a698e2ec38604c6 +pubmatic.com, 159668, RESELLER, 5d62403b186f2ace +adcolony.com, 29b7f4a14dc689eb, RESELLER, 1ad675c9de6b5176 +meitu.com, 654, RESELLER +rhythmone.com, 3880497124, RESELLER, a670c89d4a324e47 +video.unrulymedia.com, 3880497124, RESELLER +sovrn.com, 273644, RESELLER, fafdf38b16bf6b2b +lijit.com, 273644, RESELLER, fafdf38b16bf6b2b +onetag.com, 5d1628750185ace, RESELLER +loopme.com, 11278, RESELLER, 6c8d5f95897a5a3b +emxdgt.com, 2047, RESELLER, 1e1d41537f7cad7f +opera.com, pub5449961587776, RESELLER, 55a0c5fd61378de3 +#OneTag +onetag.com, 753951255855558, DIRECT +rubiconproject.com, 11006, RESELLER, 0bfd66d529a55807 +google.com, pub-3769010358500643, RESELLER, f08c47fec0942fa0 +freewheel.tv, 20393, RESELLER +freewheel.tv, 24377, RESELLER +yahoo.com, 58905, RESELLER, e1a5b5b6e3255540 +aol.com, 58905, RESELLER, e1a5b5b6e3255540 +appnexus.com, 13099, RESELLER +smartadserver.com, 4111, RESELLER +# 152Media +152media.com, 152, DIRECT +# Criteo +themediagrid.com, A6ODQF, DIRECT, 35d5010d7789b49d +# Adtelligent +adtelligent.com, 534151, DIRECT +33across.com, 0010b00002T3JniAAF, DIRECT, bbea06d9c4d2853c +152media.info, 152M10, RESELLER +bidmatic.io, b-82687, DIRECT +e-planning.net, 835fbafe26d231b1, DIRECT, c1ba615865ed87b2 +improvedigital.com, 1628, DIRECT +indexexchange.com, 189529, DIRECT, 50b1c356f2c5c8fc +lijit.com, 310770, DIRECT, fafdf38b16bf6b2b +loopme.com, 11378, RESELLER, 6c8d5f95897a5a3b +onetag.com, 59a18369e249bfb, DIRECT +openx.com, 541177116, RESELLER, 6a698e2ec38604c6 +pubmatic.com, 156813, DIRECT, 5d62403b186f2ace +rhythmone.com, 144481089, RESELLER, a670c89d4a324e47 +rubiconproject.com, 17184, DIRECT, 0bfd66d529a55807 +sovrn.com, 310770, DIRECT, fafdf38b16bf6b2b +spotim.market, 4446666, DIRECT, 077e5f709d15bdbb +video.unrulymedia.com, 144481089, RESELLER +# Conversant +conversantmedia.com, 100359, RESELLER, 03113cd04947736d +appnexus.com, 4052, RESELLER +contextweb.com, 561998, RESELLER, 89ff185a4c4e857c +openx.com, 540031703, RESELLER, 6a698e2ec38604c6 +pubmatic.com, 158100, RESELLER, 5d62403b186f2ace +rubiconproject.com, 23644, RESELLER, 0bfd66d529a55807 +yahoo.com, 55771, RESELLER, e1a5b5b6e3255540 +# TAM - Acuity Ads +admanmedia.com, 962, DIRECT +smartadserver.com, 3713, RESELLER +openx.com, 540866936, RESELLER, 6a698e2ec38604c6 +rubiconproject.com, 14558, RESELLER, 0bfd66d529a55807 +pubmatic.com, 158481, RESELLER, 5d62403b186f2ace +adform.com, 2671, RESELLER +rhythmone.com, 3948367200, RESELLER, a670c89d4a324e47 +axonix.com, 57869, RESELLER +# Holid +holid.io, 543, DIRECT +adform.com, 1612, RESELLER +improvedigital.com, 1134, RESELLER +appnexus.com, 11179, RESELLER +rubiconproject.com, 19172, RESELLER, 0bfd66d529a55807 +adpone.com, 1f3b14785831ec9153c5, DIRECT +google.com, pub-2128757167812663, RESELLER, f08c47fec0942fa0 +rubiconproject.com, 17210, RESELLER, 0bfd66d529a55807 +rubiconproject.com, 20266, RESELLER, 0bfd66d529a55807 +appnexus.com, 10264, DIRECT +appnexus.com, 10264, RESELLER +pubmatic.com, 156383, RESELLER, 5d62403b186f2ace +pubmatic.com, 157841, RESELLER, 5d62403b186f2ace +pubmatic.com, 161173, RESELLER, 5d62403b186f2ace +openx.com, 537122561, RESELLER +openx.com, 539966405, RESELLER, 6a698e2ec38604c6 +openx.com, 540603695, RESELLER, 6a698e2ec38604c6 +adform.com, 2474, DIRECT +appnexus.com, 2725, RESELLER, f5ab79cb980f11d1 +yahoo.com, 59739, RESELLER, e1a5b5b6e3255540 +yahoo.com, 59740, RESELLER, e1a5b5b6e3255540 +yahoo.com, 59741, RESELLER, e1a5b5b6e3255540`; }); // router.post('/graphql', proxyMiddleware); From f7cb62e10ca688c33ef982a7323ad15ce5e2bf7a Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 14 May 2022 15:16:19 +0900 Subject: [PATCH 028/199] fix: show not found if not own post --- src/containers/write/ActiveEditor.tsx | 10 ++++++++++ src/lib/graphql/post.ts | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/src/containers/write/ActiveEditor.tsx b/src/containers/write/ActiveEditor.tsx index f49f6758..de31cbc5 100644 --- a/src/containers/write/ActiveEditor.tsx +++ b/src/containers/write/ActiveEditor.tsx @@ -20,6 +20,8 @@ import { } from '../../lib/graphql/post'; import { safe } from '../../lib/utils'; import PopupOKCancel from '../../components/common/PopupOKCancel'; +import { useUserId } from '../../lib/hooks/useUser'; +import NotFoundPage from '../../pages/NotFoundPage'; export type ActiveEditorProps = {}; @@ -29,6 +31,7 @@ const ActiveEditor: React.FC = () => { const postId = useSelector((state: RootState) => state.write.postId); const [askLoadTemp, setAskLoadTemp] = useState(false); const initialized = useRef(false); + const userId = useUserId(); const dispatch = useDispatch(); const location = useLocation(); @@ -128,6 +131,13 @@ const ActiveEditor: React.FC = () => { // dispatch(setInitialBody(lastPostHistory.body)); }, [dispatch, lastPostHistory, post]); + if ( + (!readPostForEdit.loading && post === null) || + (post && post.user.id !== userId) + ) { + return ; + } + if (id && !post && !postId) return null; return ( diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index 26fcf6c5..33190713 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -341,6 +341,9 @@ export const READ_POST_FOR_EDIT = gql` id name } + user { + id + } } } `; @@ -396,6 +399,9 @@ export type ReadPostForEditResponse = { name: string; } | null; updated_at: string; + user: { + id: string; + }; }; }; From eef54d1ebcef3d7fc8cbdc58c9d120dfc9fb6250 Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 14 May 2022 15:45:04 +0900 Subject: [PATCH 029/199] fix: check post valid only for written post --- src/containers/write/ActiveEditor.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/containers/write/ActiveEditor.tsx b/src/containers/write/ActiveEditor.tsx index de31cbc5..1c1bb896 100644 --- a/src/containers/write/ActiveEditor.tsx +++ b/src/containers/write/ActiveEditor.tsx @@ -132,8 +132,9 @@ const ActiveEditor: React.FC = () => { }, [dispatch, lastPostHistory, post]); if ( - (!readPostForEdit.loading && post === null) || - (post && post.user.id !== userId) + id && + ((!readPostForEdit.loading && post === null) || + (post && post.user.id !== userId)) ) { return ; } From c17ab3842bfa068053644ff22fea469fd7427b4d Mon Sep 17 00:00:00 2001 From: velopert Date: Sat, 14 May 2022 15:48:28 +0900 Subject: [PATCH 030/199] fix: check newPost instead --- src/containers/write/ActiveEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/write/ActiveEditor.tsx b/src/containers/write/ActiveEditor.tsx index 1c1bb896..fa1c9b59 100644 --- a/src/containers/write/ActiveEditor.tsx +++ b/src/containers/write/ActiveEditor.tsx @@ -132,7 +132,7 @@ const ActiveEditor: React.FC = () => { }, [dispatch, lastPostHistory, post]); if ( - id && + !newPost && ((!readPostForEdit.loading && post === null) || (post && post.user.id !== userId)) ) { From 5fa39c40d5060ab860fe71d49afe54eaa492a255 Mon Sep 17 00:00:00 2001 From: velopert Date: Tue, 17 May 2022 01:38:32 +0900 Subject: [PATCH 031/199] replace: ad provider --- src/containers/post/HorizontalAd.tsx | 139 +++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 9 deletions(-) diff --git a/src/containers/post/HorizontalAd.tsx b/src/containers/post/HorizontalAd.tsx index 8c15f215..207bae2c 100644 --- a/src/containers/post/HorizontalAd.tsx +++ b/src/containers/post/HorizontalAd.tsx @@ -1,8 +1,92 @@ import React, { useEffect, useRef } from 'react'; import styled from 'styled-components'; import VelogResponsive from '../../components/velog/VelogResponsive'; +import media from '../../lib/styles/media'; + interface Props {} +function initDesktopAd() { + if (window.innerWidth < 768) return; + (function () { + var size = '728x90', + adunit = 'velog.io_728x90_leaderboard_DFP', + childNetworkId = '22738196472', + xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function () { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + var es = document.querySelectorAll("[data-id='" + adunit + "']"); + var e = Array.from(es).filter(function (e) { + return !e.hasAttribute('data-rendered'); + }); + if (e.length > 0) { + e.forEach(function (el: any) { + var iframe = el.contentWindow.document; + iframe.open(); + iframe.write(xmlhttp.responseText); + iframe.close(); + el.setAttribute('data-rendered', true as any); + }); + } + } + }; + var child = childNetworkId.trim() ? ',' + childNetworkId.trim() : ''; + xmlhttp.open( + 'GET', + 'https://pubads.g.doubleclick.net/gampad/adx?iu=/147246189' + + child + + '/' + + adunit + + '&sz=' + + encodeURI(size) + + '&t=Placement_type%3Dserving&' + + Date.now(), + true, + ); + xmlhttp.send(); + })(); +} + +function initMobileAd() { + (function () { + if (window.innerWidth > 580) return; + var size = '320x100', + adunit = 'velog.io_320x100_leaderboard_DFP', + childNetworkId = '22738196472', + xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function () { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + var es = document.querySelectorAll("[data-id='" + adunit + "']"); + var e = Array.from(es).filter(function (e) { + return !e.hasAttribute('data-rendered'); + }); + if (e.length > 0) { + e.forEach(function (el: any) { + var iframe = el.contentWindow.document; + iframe.open(); + iframe.write(xmlhttp.responseText); + iframe.close(); + el.setAttribute('data-rendered', true); + }); + } + } + }; + var child = childNetworkId.trim() ? ',' + childNetworkId.trim() : ''; + xmlhttp.open( + 'GET', + 'https://pubads.g.doubleclick.net/gampad/adx?iu=/147246189' + + child + + '/' + + adunit + + '&sz=' + + encodeURI(size) + + '&t=Placement_type%3Dserving&' + + Date.now(), + true, + ); + xmlhttp.send(); + })(); +} + function HorizontalAd({}: Props) { const ref = useRef(null); const initializedRef = useRef(false); @@ -13,7 +97,8 @@ function HorizontalAd({}: Props) { entries.forEach((entry) => { if (entry.isIntersecting && !initializedRef.current) { initializedRef.current = true; - (window.adsbygoogle = window.adsbygoogle || []).push({}); + initDesktopAd(); + initMobileAd(); console.log('initialized!'); } }); @@ -33,14 +118,30 @@ function HorizontalAd({}: Props) { return ( - + + + + + + ); @@ -52,4 +153,24 @@ const Wrapper = styled.div` margin-bottom: 1.5rem; `; +const Desktop = styled.div` + display: flex; + justify-content: center; + iframe { + width: 728px; + height: 90px; + } + ${media.small} { + display: none; + } +`; + +const Mobile = styled.div` + display: none; + justify-content: center; + ${media.custom(580)} { + display: flex; + } +`; + export default HorizontalAd; From 8f4a030aa69d30674e96584d6b5e64ab6e0b4889 Mon Sep 17 00:00:00 2001 From: velopert Date: Tue, 17 May 2022 02:00:03 +0900 Subject: [PATCH 032/199] replace: provider for post card --- src/components/common/PostCardGrid.tsx | 15 ++++- src/components/common/SetupAdFeed.tsx | 90 ++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/components/common/SetupAdFeed.tsx diff --git a/src/components/common/PostCardGrid.tsx b/src/components/common/PostCardGrid.tsx index a2166dae..a0b78dee 100644 --- a/src/components/common/PostCardGrid.tsx +++ b/src/components/common/PostCardGrid.tsx @@ -6,6 +6,7 @@ import { mediaQuery } from '../../lib/styles/media'; import AdFeed from './AdFeed'; import { detectAnyAdblocker } from 'just-detect-adblock'; import useUser from '../../lib/hooks/useUser'; +import SetupAdFeed from './SetupAdFeed'; export type PostCardGridProps = { posts: (PartialPost | undefined)[]; @@ -16,8 +17,15 @@ export type PostCardGridProps = { function PostCardGrid({ posts, loading, forHome, forPost }: PostCardGridProps) { const [adBlocked, setAdBlocked] = useState(false); + const [mode, setMode] = useState<'google' | 'setupad'>('setupad'); const user = useUser(); + useEffect(() => { + if (window.innerWidth < 768) { + setMode('google'); + } + }, []); + useEffect(() => { detectAnyAdblocker().then((detected: boolean) => { if (detected) { @@ -60,7 +68,12 @@ function PostCardGrid({ posts, loading, forHome, forPost }: PostCardGridProps) { forPost={forPost} /> ); - return ; + + return mode === 'google' ? ( + + ) : ( + + ); })} {loading && Array.from({ length: 8 }).map((_, i) => ( diff --git a/src/components/common/SetupAdFeed.tsx b/src/components/common/SetupAdFeed.tsx new file mode 100644 index 00000000..13b8d92f --- /dev/null +++ b/src/components/common/SetupAdFeed.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useRef } from 'react'; +import styled from 'styled-components'; +import { mediaQuery } from '../../lib/styles/media'; +import gtag from '../../lib/gtag'; +import { themedPalette } from '../../lib/styles/themes'; +import { useTheme } from '../../lib/hooks/useTheme'; + +function init() { + (function () { + var size = '320x378', + adunit = 'velog.io_320x378_native_DFP', + childNetworkId = '22738196472', + xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function () { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + var es = document.querySelectorAll("[data-id='" + adunit + "']"); + var e = Array.from(es).filter(function (e) { + return !e.hasAttribute('data-rendered'); + }); + if (e.length > 0) { + e.forEach(function (el: any) { + var iframe = el.contentWindow.document; + iframe.open(); + iframe.write(xmlhttp.responseText); + iframe.close(); + el.setAttribute('data-rendered', true); + }); + } + } + }; + var child = childNetworkId.trim() ? ',' + childNetworkId.trim() : ''; + xmlhttp.open( + 'GET', + 'https://pubads.g.doubleclick.net/gampad/adx?iu=/147246189' + + child + + '/' + + adunit + + '&sz=' + + encodeURI(size) + + '&t=Placement_type%3Dserving&' + + Date.now(), + true, + ); + xmlhttp.send(); + })(); +} + +function SetupAdFeed() { + useEffect(() => { + init(); + }, []); + + return ( + + + + ); +} + +const Block = styled.div` + width: 20rem; + margin: 1rem; + min-height: 23.5625rem; + height: auto; + border-radius: 4px; + box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.04); + background: ${themedPalette.bg_element1}; + + ${mediaQuery(1056)} { + width: calc(50% - 2rem); + } + ${mediaQuery(767)} { + margin: 0; + width: 100%; + margin-top: 1rem; + margin-bottom: 1rem; + min-height: 5rem; + } +`; + +export default SetupAdFeed; From 524cb4b8e27a5a11954ce4ab8b19930e6ace6063 Mon Sep 17 00:00:00 2001 From: velopert Date: Mon, 4 Jul 2022 02:33:15 +0900 Subject: [PATCH 033/199] revert: setupad --- src/components/common/AdFeed.tsx | 2 +- src/components/common/PostCardGrid.tsx | 15 +- src/containers/post/HorizontalAd.tsx | 139 +--------- src/index.server.ts | 337 +------------------------ 4 files changed, 13 insertions(+), 480 deletions(-) diff --git a/src/components/common/AdFeed.tsx b/src/components/common/AdFeed.tsx index 7e40b56b..f9aefc89 100644 --- a/src/components/common/AdFeed.tsx +++ b/src/components/common/AdFeed.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; import { mediaQuery } from '../../lib/styles/media'; import gtag from '../../lib/gtag'; diff --git a/src/components/common/PostCardGrid.tsx b/src/components/common/PostCardGrid.tsx index a0b78dee..f1fcd4cc 100644 --- a/src/components/common/PostCardGrid.tsx +++ b/src/components/common/PostCardGrid.tsx @@ -6,7 +6,6 @@ import { mediaQuery } from '../../lib/styles/media'; import AdFeed from './AdFeed'; import { detectAnyAdblocker } from 'just-detect-adblock'; import useUser from '../../lib/hooks/useUser'; -import SetupAdFeed from './SetupAdFeed'; export type PostCardGridProps = { posts: (PartialPost | undefined)[]; @@ -17,14 +16,8 @@ export type PostCardGridProps = { function PostCardGrid({ posts, loading, forHome, forPost }: PostCardGridProps) { const [adBlocked, setAdBlocked] = useState(false); - const [mode, setMode] = useState<'google' | 'setupad'>('setupad'); - const user = useUser(); - useEffect(() => { - if (window.innerWidth < 768) { - setMode('google'); - } - }, []); + const user = useUser(); useEffect(() => { detectAnyAdblocker().then((detected: boolean) => { @@ -69,11 +62,7 @@ function PostCardGrid({ posts, loading, forHome, forPost }: PostCardGridProps) { /> ); - return mode === 'google' ? ( - - ) : ( - - ); + return ; })} {loading && Array.from({ length: 8 }).map((_, i) => ( diff --git a/src/containers/post/HorizontalAd.tsx b/src/containers/post/HorizontalAd.tsx index 207bae2c..8c15f215 100644 --- a/src/containers/post/HorizontalAd.tsx +++ b/src/containers/post/HorizontalAd.tsx @@ -1,92 +1,8 @@ import React, { useEffect, useRef } from 'react'; import styled from 'styled-components'; import VelogResponsive from '../../components/velog/VelogResponsive'; -import media from '../../lib/styles/media'; - interface Props {} -function initDesktopAd() { - if (window.innerWidth < 768) return; - (function () { - var size = '728x90', - adunit = 'velog.io_728x90_leaderboard_DFP', - childNetworkId = '22738196472', - xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function () { - if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { - var es = document.querySelectorAll("[data-id='" + adunit + "']"); - var e = Array.from(es).filter(function (e) { - return !e.hasAttribute('data-rendered'); - }); - if (e.length > 0) { - e.forEach(function (el: any) { - var iframe = el.contentWindow.document; - iframe.open(); - iframe.write(xmlhttp.responseText); - iframe.close(); - el.setAttribute('data-rendered', true as any); - }); - } - } - }; - var child = childNetworkId.trim() ? ',' + childNetworkId.trim() : ''; - xmlhttp.open( - 'GET', - 'https://pubads.g.doubleclick.net/gampad/adx?iu=/147246189' + - child + - '/' + - adunit + - '&sz=' + - encodeURI(size) + - '&t=Placement_type%3Dserving&' + - Date.now(), - true, - ); - xmlhttp.send(); - })(); -} - -function initMobileAd() { - (function () { - if (window.innerWidth > 580) return; - var size = '320x100', - adunit = 'velog.io_320x100_leaderboard_DFP', - childNetworkId = '22738196472', - xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function () { - if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { - var es = document.querySelectorAll("[data-id='" + adunit + "']"); - var e = Array.from(es).filter(function (e) { - return !e.hasAttribute('data-rendered'); - }); - if (e.length > 0) { - e.forEach(function (el: any) { - var iframe = el.contentWindow.document; - iframe.open(); - iframe.write(xmlhttp.responseText); - iframe.close(); - el.setAttribute('data-rendered', true); - }); - } - } - }; - var child = childNetworkId.trim() ? ',' + childNetworkId.trim() : ''; - xmlhttp.open( - 'GET', - 'https://pubads.g.doubleclick.net/gampad/adx?iu=/147246189' + - child + - '/' + - adunit + - '&sz=' + - encodeURI(size) + - '&t=Placement_type%3Dserving&' + - Date.now(), - true, - ); - xmlhttp.send(); - })(); -} - function HorizontalAd({}: Props) { const ref = useRef(null); const initializedRef = useRef(false); @@ -97,8 +13,7 @@ function HorizontalAd({}: Props) { entries.forEach((entry) => { if (entry.isIntersecting && !initializedRef.current) { initializedRef.current = true; - initDesktopAd(); - initMobileAd(); + (window.adsbygoogle = window.adsbygoogle || []).push({}); console.log('initialized!'); } }); @@ -118,30 +33,14 @@ function HorizontalAd({}: Props) { return ( - - - - - - + ); @@ -153,24 +52,4 @@ const Wrapper = styled.div` margin-bottom: 1.5rem; `; -const Desktop = styled.div` - display: flex; - justify-content: center; - iframe { - width: 728px; - height: 90px; - } - ${media.small} { - display: none; - } -`; - -const Mobile = styled.div` - display: none; - justify-content: center; - ${media.custom(580)} { - display: flex; - } -`; - export default HorizontalAd; diff --git a/src/index.server.ts b/src/index.server.ts index d4e8c278..bc13298a 100644 --- a/src/index.server.ts +++ b/src/index.server.ts @@ -23,342 +23,7 @@ app.use(router.routes()).use(router.allowedMethods()); app.use(ssrMiddleware); app.use(proxyMiddleware); router.get('/ads.txt', (ctx) => { - ctx.body = `google.com, pub-5574866530496701, DIRECT, f08c47fec0942fa0 -setupad.com, 1557, DIRECT -google.com, pub-7383171830614216, RESELLER, f08c47fec0942fa0 -google.com, pub-3970277535528613, RESELLER, f08c47fec0942fa0 -rubiconproject.com, 13606, RESELLER, 0bfd66d529a55807 -rubiconproject.com, 21118, RESELLER, 0bfd66d529a55807 -appnexus.com, 3711, RESELLER -adform.com, 1484, RESELLER -pubmatic.com, 156191, RESELLER, 5d62403b186f2ace -sovrn.com, 252889, RESELLER, fafdf38b16bf6b2b -lijit.com, 252889, RESELLER, fafdf38b16bf6b2b -appnexus.com, 1360, RESELLER, f5ab79cb980f11d1 -gumgum.com, 11645, RESELLER, ffdef49475d318a9 -openx.com, 537120960, RESELLER -openx.com, 539924617, RESELLER, 6a698e2ec38604c6 -openx.com, 83499, RESELLER -openx.com, 538959099, RESELLER, 6a698e2ec38604c6 -openx.com, 540890784, DIRECT, 6a698e2ec38604c6 -pubmatic.com, 137711, RESELLER, 5d62403b186f2ace -pubmatic.com, 156212, RESELLER, 5d62403b186f2ace -pubmatic.com, 156700, RESELLER, 5d62403b186f2ace -rubiconproject.com, 17960, RESELLER, 0bfd66d529a55807 -pubmatic.com, 62483, RESELLER -contextweb.com, 558511, RESELLER -emxdgt.com, 242, DIRECT, 1e1d41537f7cad7f -appnexus.com, 1356, RESELLER, f5ab79cb980f11d1 -smartadserver.com, 1776, DIRECT -contextweb.com, 561376, RESELLER, 89ff185a4c4e857c -admixer.net, E1FD631C-1259-4EF3-A0F3-452A8B59DCB2, DIRECT -improvedigital.com, 803, DIRECT -indexexchange.com, 186102, DIRECT -adocean-global.com, 83598, DIRECT -districtm.io, 101521, DIRECT -google.com, pub-9685734445476814, RESELLER, f08c47fec0942fa0 -RTBHOUSE.COM, d2380d6f45eaac2c7d22, DIRECT -betweendigital.com, 35081, DIRECT -aps.amazon.com,d14c8d3d-c09a-40c7-8c08-b5d7cd1d7fac,DIRECT -pubmatic.com,157150,RESELLER,5d62403b186f2ace -openx.com,540191398,RESELLER,6a698e2ec38604c6 -rubiconproject.com,18020,RESELLER,0bfd66d529a55807 -appnexus.com,1908,RESELLER,f5ab79cb980f11d1 -smaato.com,1100044650,RESELLER,07bcf65f187117b4 -adtech.com,12068,RESELLER,e1a5b5b6e3255540 -ad-generation.jp,12474,RESELLER,7f4ea9029ac04e53 -districtm.io,100962,RESELLER,3fd707be9c4527c3 -appnexus.com,3663,RESELLER,f5ab79cb980f11d1 -rhythmone.com,1654642120,RESELLER,a670c89d4a324e47 -yahoo.com,55029,RESELLER,e1a5b5b6e3255540 -gumgum.com,14141,RESELLER,ffdef49475d318a9 -pubmatic.com, 160006, RESELLER, 5d62403b186f2ace -pubmatic.com,160096,RESELLER,5d62403b186f2ace -yieldmo.com,2719019867620450718,RESELLER -yahoo.com,55774,DIRECT -emxdgt.com,2009,RESELLER,1e1d41537f7cad7f -smartadserver.com,4125,RESELLER,060d053dcf45cbf3 -themediagrid.com,jtqkmp,RESELLER,35d5010d7789b49d -sovrn.com,375328,RESELLER,fafdf38b16bf6b2b -lijit.com,375328,RESELLER,fafdf38b16bf6b2b -aps.amazon.com, dce921de-ffa9-46de-b23a-dcb73faca71d, DIRECT -sharethrough.com,7144eb80,RESELLER,d53b998a7bd4ecd2 -contextweb.com,562541,RESELLER,89ff185a4c4e857c -pubmatic.com,161085,DIRECT,5d62403b186f2ace -beachfront.com,14804,RESELLER,e2541279e8e2ca4d -improvedigital.com,2050,RESELLER -sonobi.com,7f5fa520f8,RESELLER,d1a215d9eb5aee9e -mintegral.com,10043,RESELLER,0aeed750c80d6423 -lijit.com, 252889-eb, DIRECT, fafdf38b16bf6b2b -smartadserver.com, 3527, DIRECT -contextweb.com, 560288, RESELLER, 89ff185a4c4e857c -pubmatic.com, 156439, RESELLER -pubmatic.com, 154037, RESELLER -rubiconproject.com, 16114, RESELLER, 0bfd66d529a55807 -openx.com, 537149888, RESELLER, 6a698e2ec38604c6 -appnexus.com, 3703, RESELLER, f5ab79cb980f11d1 -districtm.io, 101760, RESELLER, 3fd707be9c4527c3 -loopme.com, s-2411, RESELLER, 6c8d5f95897a5a3b -loopme.com, 5679, RESELLER, 6c8d5f95897a5a3b -xad.com, 958, RESELLER, 81cbf0a75a5e0e9a -indexexchange.com, 191541, RESELLER -#Admixer -admixer.net, 099e1459-d18f-4339-931b-2dab1bbc8a7d, RESELLER -yieldnexus.com, 96204, RESELLER -audience.media, 100358, RESELLER -appnexus.com, 10617, RESELLER -appnexus.com, 9393, RESELLER -advertising.com, 25034, RESELLER -admanmedia.com, 584, DIRECT -video.unrulymedia.com, 3948367200, RESELLER -inmobi.com, 3a4f7da341dd490cbb7dde02b126275e, RESELLER, 83e75a7ae333ca9d -sonobi.com, 7b37f8ccbc, RESELLER, d1a215d9eb5aee9e -improvedigital.com, 1668, RESELLER -indexexchange.com, 191497, RESELLER -pubmatic.com, 157562, DIRECT, 5d62403b186f2ace -advertising.com, 25001, RESELLER, e1a5b5b6e3255540 -rubiconproject.com, 18224, RESELLER, 0bfd66d529a55807 -spotxchange.com, 119135, RESELLER, 7842df1d2fe2db34 -spotx.tv, 119135, RESELLER, 7842df1d2fe2db34 -smartadserver.com, 1894, RESELLER -smartadserver.com, 3445, RESELLER -rhythmone.com, 1816189007, RESELLER, a670c89d4a324e47 -betweendigital.com, 43070, DIRECT -rubiconproject.com, 19724, RESELLER, 0bfd66d529a55807 -google.com, pub-5289985627731322, RESELLER, f08c47fec0942fa0 -#Adagio -adagio.io, 1053, DIRECT -rubiconproject.com, 19116, RESELLER, 0bfd66d529a55807 -pubmatic.com, 159110, RESELLER, 5d62403b186f2ace -improvedigital.com, 1790, RESELLER -onetag.com, 6b859b96c564fbe, RESELLER -indexexchange.com, 194558, RESELLER -pubwise.io, 68867843, RESELLER, c327c91a93a7cdd3 -richaudience.com, 1BTOoaD22a, DIRECT -#33Across -33across.com, 0010b00002bTS1QAAW, DIRECT, bbea06d9c4d2853c -adtech.com, 12094, RESELLER -adtech.com, 9993, RESELLER -aol.com, 47594, RESELLER, e1a5b5b6e3255540 -yahoo.com, 55188, DIRECT, e1a5b5b6e3255540 -advangelists.com, 8d3bba7425e7c98c50f52ca1b52d3735, RESELLER, 60d26397ec060f98 -sonobi.com, a416546bb7, RESELLER, d1a215d9eb5aee9e -yoc.com, 5600, DIRECT -#JustPremium -appnexus.com, 7118, RESELLER -spotx.tv, 108933, RESELLER, 7842df1d2fe2db34 -spotxchange.com, 108933, RESELLER, 7842df1d2fe2db34 -improvedigital.com, 185, RESELLER -adform.com, 183, RESELLER -freewheel.tv, 33081, RESELLER -freewheel.tv, 33601, RESELLER -indexexchange.com, 189872, RESELLER -openx.com, 540925214, RESELLER, 6a698e2ec38604c6 -google.com, pub-8172268348509349, RESELLER, f08c47fec0942fa0 -#Eskimi -eskimi.com, eas-2020000001, DIRECT -#RhythmOne -video.unrulymedia.com, 5831063052797481612, DIRECT -rhythmone.com, 5831063052797481612, DIRECT, a670c89d4a324e47 -indexexchange.com, 182257, RESELLER -appnexus.com, 6849, RESELLER -rubiconproject.com, 15268, RESELLER -spotxchange.com, 285547, RESELLER, 7842df1d2fe2db34 -spotx.tv, 285547, RESELLER, 7842df1d2fe2db34 -pubmatic.com, 159277, RESELLER, 5d62403b186f2ace -advertising.com, 28605, RESELLER -improvedigital.com, 1699, RESELLER -#LuponMedia -luponmedia.com, 1994505, DIRECT -adform.com, 1985, DIRECT, 9f5210a2f0999e32 -rubiconproject.com, 12398, DIRECT, 0bfd66d529a55807 -pubmatic.com, 159760, RESELLER, 5d62403b186f2ace -connectad.io, 151, DIRECT -pubmatic.com, 156077, RESELLER, 5d62403b186f2ace -pubmatic.com, 55990, RESELLER, 5d62403b186f2ace -openx.com, 537145117, RESELLER, 6a698e2ec38604c6 -adform.com, 768, RESELLER -indexexchange.com, 190906, DIRECT, 50b1c356f2c5c8fc -appnexus.com, 8738, RESELLER, f5ab79cb980f11d1 -EMXDGT.com, 1138, DIRECT, 1e1d41537f7cad7f -google.com, pub-5995202563537249, RESELLER, f08c47fec0942fa0 -sovrn.com, 244287, DIRECT, fafdf38b16bf6b2b -lijit.com, 244287, DIRECT, fafdf38b16bf6b2b -lijit.com, 244287-eb, DIRECT, fafdf38b16bf6b2b -yahoo.com, 55248, DIRECT -rubiconproject.com, 13132, RESELLER, 0bfd66d529a55807 -rubiconproject.com, 17250, RESELLER, 0bfd66d529a55807 -xad.com, 240, RESELLER, 81cbf0a75a5e0e9a -onetag.com, 5d4e109247a89f6, DIRECT -advertising.com, 28246, RESELLER -sovrn.com, 247505, DIRECT, fafdf38b16bf6b2b -lijit.com, 247505, DIRECT, fafdf38b16bf6b2b -lijit.com, 247505-eb, DIRECT, fafdf38b16bf6b2b -conversantmedia.com, 100264, RESELLER, 03113cd04947736d -adform.com, 2795, RESELLER -admixer.co.kr,1538,RESELLER -betweendigital.com, 43837, RESELLER -ssp.e-volution.ai, AJxF6R108a9M6CaTvK, RESELLER -ssp.logan.ai, LG4, RESELLER -#AMX -amxrtb.com, 105199421, DIRECT -indexexchange.com, 191503, RESELLER -appnexus.com, 11786, RESELLER -appnexus.com, 12290, RESELLER -appnexus.com, 9393, RESELLER #Video #Display -appnexus.com, 3153, RESELLER, f5ab79cb980f11d1 -appnexus.com, 11924, RESELLER, f5ab79cb980f11d1 -lijit.com, 260380, RESELLER, fafdf38b16bf6b2b -sovrn.com, 260380, RESELLER, fafdf38b16bf6b2b -advertising.com, 28305, RESELLER -#NoBid -nobid.io, 22120307115, DIRECT -google.com, pub-1835489473992347, DIRECT, f08c47fec0942fa0 -google.com, pub-1789253751882305, DIRECT, f08c47fec0942fa0 -appnexus.com, 11429, DIRECT, f5ab79cb980f11d1 -rubiconproject.com, 13702, DIRECT, 0bfd66d529a55807 -indexexchange.com, 185104, DIRECT, 50b1c356f2c5c8fc -pubmatic.com, 157898, DIRECT, 5d62403b186f2ace -sovrn.com, 273657, DIRECT, fafdf38b16bf6b2b -lijit.com, 273657, DIRECT, fafdf38b16bf6b2b -adtech.com, 10109, DIRECT -aolcloud.net, 10109, DIRECT -openx.com, 540650310, DIRECT, 6a698e2ec38604c6 -gumgum.com, 13926, DIRECT, ffdef49475d318a9 -33across.com, 0010b00002Mq2FYAAZ, DIRECT, bbea06d9c4d2853c -sonobi.com, bc2afab5f7, DIRECT, d1a215d9eb5aee9e -revcontent.com, 124709, DIRECT -my6sense.com, 9732, RESELLER -criteo.com, 7822, RESELLER -rhythmone.com, 2439829435, RESELLER, a670c89d4a324e47 -emxdgt.com, 326, RESELLER, 1e1d41537f7cad7f -#Adyoulike -adyoulike.com, e5d58aeeb93b1fb77c4e17409eea8a3a, DIRECT -appnexus.com, 9733, RESELLER -rubiconproject.com, 20736, RESELLER, 0bfd66d529a55807 -openx.com, 540847510, RESELLER, 6a698e2ec38604c6 -spotxchange.com, 230037, RESELLER, 7842df1d2fe2db34 -spotx.tv, 230037, RESELLER, 7842df1d2fe2db34 -themediagrid.com, RYIDPE, DIRECT, 35d5010d7789b49d -criteo.com, B-061463, DIRECT, 9fac4a4a87c2a44f -adform.com, 1835, RESELLER, 9f5210a2f0999e32 -admanmedia.com,726,RESELLER -advertising.com, 28949, DIRECT #Verizon - RevNew -smartadserver.com,3447,DIRECT -pubwise.io, 27754564, RESELLER, c327c91a93a7cdd3 #PubWise -onetag.com, 694e68b73971b58, DIRECT -conversantmedia.com, 100257, DIRECT, 03113cd04947736d -pubmatic.com, 160925, RESELLER, 5d62403b186f2ace -smartadserver.com, 4144, DIRECT -smartadserver.com, 4016, DIRECT -smartadserver.com, 4012, DIRECT -smartadserver.com, 4071, DIRECT -smartadserver.com, 4073, DIRECT -smartadserver.com, 4074, DIRECT -indexexchange.com, 194056, DIRECT -rubiconproject.com, 23220, RESELLER, 0bfd66d529a55807 -openx.com, 544005210, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 160623, RESELLER, 5d62403b186f2ace -onetag.com, 753951255855558-OB, DIRECT -sharethrough.com, GBPtKPVp, DIRECT, d53b998a7bd4ecd2 -#Sharethrough -sharethrough.com, c73f63f1, DIRECT, d53b998a7bd4ecd2 -indexexchange.com, 186046, RESELLER -spotxchange.com, 212457, RESELLER -spotx.tv, 212457, RESELLER -pubmatic.com, 156557, RESELLER -pubmatic.com, 158723, RESELLER, 5d62403b186f2ace -rubiconproject.com, 18694, RESELLER, 0bfd66d529a55807 -openx.com, 540274407, RESELLER, 6a698e2ec38604c6 -33across.com, 0013300001kQj2HAAS, RESELLER, bbea06d9c4d2853c -smaato.com, 1100047713, RESELLER, 07bcf65f187117b4 -indexexchange.com, 195658, RESELLER, 50b1c356f2c5c8fc -pubmatic.com, 161085, RESELLER, 5d62403b186f2ace -#BetweenX -openx.com, 541177349, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 159668, RESELLER, 5d62403b186f2ace -adcolony.com, 29b7f4a14dc689eb, RESELLER, 1ad675c9de6b5176 -meitu.com, 654, RESELLER -rhythmone.com, 3880497124, RESELLER, a670c89d4a324e47 -video.unrulymedia.com, 3880497124, RESELLER -sovrn.com, 273644, RESELLER, fafdf38b16bf6b2b -lijit.com, 273644, RESELLER, fafdf38b16bf6b2b -onetag.com, 5d1628750185ace, RESELLER -loopme.com, 11278, RESELLER, 6c8d5f95897a5a3b -emxdgt.com, 2047, RESELLER, 1e1d41537f7cad7f -opera.com, pub5449961587776, RESELLER, 55a0c5fd61378de3 -#OneTag -onetag.com, 753951255855558, DIRECT -rubiconproject.com, 11006, RESELLER, 0bfd66d529a55807 -google.com, pub-3769010358500643, RESELLER, f08c47fec0942fa0 -freewheel.tv, 20393, RESELLER -freewheel.tv, 24377, RESELLER -yahoo.com, 58905, RESELLER, e1a5b5b6e3255540 -aol.com, 58905, RESELLER, e1a5b5b6e3255540 -appnexus.com, 13099, RESELLER -smartadserver.com, 4111, RESELLER -# 152Media -152media.com, 152, DIRECT -# Criteo -themediagrid.com, A6ODQF, DIRECT, 35d5010d7789b49d -# Adtelligent -adtelligent.com, 534151, DIRECT -33across.com, 0010b00002T3JniAAF, DIRECT, bbea06d9c4d2853c -152media.info, 152M10, RESELLER -bidmatic.io, b-82687, DIRECT -e-planning.net, 835fbafe26d231b1, DIRECT, c1ba615865ed87b2 -improvedigital.com, 1628, DIRECT -indexexchange.com, 189529, DIRECT, 50b1c356f2c5c8fc -lijit.com, 310770, DIRECT, fafdf38b16bf6b2b -loopme.com, 11378, RESELLER, 6c8d5f95897a5a3b -onetag.com, 59a18369e249bfb, DIRECT -openx.com, 541177116, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 156813, DIRECT, 5d62403b186f2ace -rhythmone.com, 144481089, RESELLER, a670c89d4a324e47 -rubiconproject.com, 17184, DIRECT, 0bfd66d529a55807 -sovrn.com, 310770, DIRECT, fafdf38b16bf6b2b -spotim.market, 4446666, DIRECT, 077e5f709d15bdbb -video.unrulymedia.com, 144481089, RESELLER -# Conversant -conversantmedia.com, 100359, RESELLER, 03113cd04947736d -appnexus.com, 4052, RESELLER -contextweb.com, 561998, RESELLER, 89ff185a4c4e857c -openx.com, 540031703, RESELLER, 6a698e2ec38604c6 -pubmatic.com, 158100, RESELLER, 5d62403b186f2ace -rubiconproject.com, 23644, RESELLER, 0bfd66d529a55807 -yahoo.com, 55771, RESELLER, e1a5b5b6e3255540 -# TAM - Acuity Ads -admanmedia.com, 962, DIRECT -smartadserver.com, 3713, RESELLER -openx.com, 540866936, RESELLER, 6a698e2ec38604c6 -rubiconproject.com, 14558, RESELLER, 0bfd66d529a55807 -pubmatic.com, 158481, RESELLER, 5d62403b186f2ace -adform.com, 2671, RESELLER -rhythmone.com, 3948367200, RESELLER, a670c89d4a324e47 -axonix.com, 57869, RESELLER -# Holid -holid.io, 543, DIRECT -adform.com, 1612, RESELLER -improvedigital.com, 1134, RESELLER -appnexus.com, 11179, RESELLER -rubiconproject.com, 19172, RESELLER, 0bfd66d529a55807 -adpone.com, 1f3b14785831ec9153c5, DIRECT -google.com, pub-2128757167812663, RESELLER, f08c47fec0942fa0 -rubiconproject.com, 17210, RESELLER, 0bfd66d529a55807 -rubiconproject.com, 20266, RESELLER, 0bfd66d529a55807 -appnexus.com, 10264, DIRECT -appnexus.com, 10264, RESELLER -pubmatic.com, 156383, RESELLER, 5d62403b186f2ace -pubmatic.com, 157841, RESELLER, 5d62403b186f2ace -pubmatic.com, 161173, RESELLER, 5d62403b186f2ace -openx.com, 537122561, RESELLER -openx.com, 539966405, RESELLER, 6a698e2ec38604c6 -openx.com, 540603695, RESELLER, 6a698e2ec38604c6 -adform.com, 2474, DIRECT -appnexus.com, 2725, RESELLER, f5ab79cb980f11d1 -yahoo.com, 59739, RESELLER, e1a5b5b6e3255540 -yahoo.com, 59740, RESELLER, e1a5b5b6e3255540 -yahoo.com, 59741, RESELLER, e1a5b5b6e3255540`; + ctx.body = `google.com, pub-5574866530496701, DIRECT, f08c47fec0942fa0`; }); // router.post('/graphql', proxyMiddleware); From 1041858318a2ba5463956f10bd3930fbb631ed40 Mon Sep 17 00:00:00 2001 From: velopert Date: Mon, 4 Jul 2022 02:45:42 +0900 Subject: [PATCH 034/199] fix: removes console.log --- src/components/post/PostContent.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/post/PostContent.tsx b/src/components/post/PostContent.tsx index e55f6508..01a532ba 100644 --- a/src/components/post/PostContent.tsx +++ b/src/components/post/PostContent.tsx @@ -29,11 +29,9 @@ function optimizeImagesFromPost(markdown: string) { /(?:!\[(.*?)\]\(https:\/\/images.velog.io\/(.*?)\))/g, ); if (!matches) return markdown; - console.log(matches); const replacers = matches.map((match) => { const filename = match.match(/https:\/\/images.velog.io\/(.*?)\)/)?.[1] ?? ''; - console.log(filename); const proeperlyEncoded = encodeURIComponent(decodeURI(filename)); return [ match, From fa18810f81ccea2f459249746e82f3303f2c75b4 Mon Sep 17 00:00:00 2001 From: velopert Date: Wed, 6 Jul 2022 17:42:54 +0900 Subject: [PATCH 035/199] fix: handles register loading --- src/components/common/RoundButton.tsx | 3 +++ src/components/register/RegisterForm.tsx | 3 +++ src/containers/register/RegisterFormContainer.tsx | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/src/components/common/RoundButton.tsx b/src/components/common/RoundButton.tsx index 58550ac6..eae19f8b 100644 --- a/src/components/common/RoundButton.tsx +++ b/src/components/common/RoundButton.tsx @@ -78,6 +78,9 @@ const RoundButtonBlock = styled.button` box-shadow: 0px 2px 12px #00000030; } cursor: pointer; + &:disabled { + background: ${themedPalette.bg_element2}; + } `; type ButtonProps = React.DetailedHTMLProps< diff --git a/src/components/register/RegisterForm.tsx b/src/components/register/RegisterForm.tsx index 4e225c00..9b3cdbaa 100644 --- a/src/components/register/RegisterForm.tsx +++ b/src/components/register/RegisterForm.tsx @@ -35,6 +35,7 @@ export interface RegisterFormProps { displayName: string; username: string; } | null; + loading: boolean; } const RegisterForm: React.FC = ({ @@ -42,6 +43,7 @@ const RegisterForm: React.FC = ({ fixedEmail, error, defaultInfo, + loading, }) => { const [form, onChange] = useInputs({ displayName: defaultInfo ? defaultInfo.displayName : '', @@ -99,6 +101,7 @@ const RegisterForm: React.FC = ({ onSubmit({ ...form, email: fixedEmail || form.email }) } size="LARGE" + disabled={loading} > 다음 diff --git a/src/containers/register/RegisterFormContainer.tsx b/src/containers/register/RegisterFormContainer.tsx index 02f1d112..3541c691 100644 --- a/src/containers/register/RegisterFormContainer.tsx +++ b/src/containers/register/RegisterFormContainer.tsx @@ -28,6 +28,7 @@ const RegisterFormContainer: React.FC = ({ ignoreQueryPrefix: true, }); const client = useApolloClient(); + const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [socialProfile, setSocialProfile] = useState( @@ -97,6 +98,7 @@ const RegisterFormContainer: React.FC = ({ } try { + setLoading(true); if (query.code) { // local email register const formWithoutEmail = { ...form } as Partial; @@ -114,6 +116,7 @@ const RegisterFormContainer: React.FC = ({ }); } } catch (e) { + setLoading(false); if ((e as any).response.status === 409) { setError('이미 존재하는 아이디입니다.'); return; @@ -139,6 +142,7 @@ const RegisterFormContainer: React.FC = ({ return ( Date: Thu, 7 Jul 2022 03:21:07 +0900 Subject: [PATCH 036/199] fix updates policy for facebook login --- src/components/policy/policyData.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/policy/policyData.ts b/src/components/policy/policyData.ts index 5cb2c648..e58fcce5 100644 --- a/src/components/policy/policyData.ts +++ b/src/components/policy/policyData.ts @@ -24,8 +24,7 @@ const data = { 2. 개인정보 수집방법: 홈페이지(회원 가입) ## 4. 개인정보의 파기절차 및 방법 -회사는 원칙적으로 개인정보 수집 및 이용목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다. -파기절차 및 방법은 다음과 같습니다. +이용자는 로그인 후 [설정](https://velog.io/setting) 페이지에서 계정을 탈퇴할 수 있습니다. 회사는 원칙적으로 개인정보의 수집 및 이용목적이 달성된 경우에는 지체없이 해당 개인정보를 파기합니다. 파기의 절차 및 방법은 다음과 같습니다. ### 파기절차 From 91d9e58ff6dd8c51160d1c767859a5acd8634616 Mon Sep 17 00:00:00 2001 From: velopert Date: Thu, 7 Jul 2022 10:32:37 +0900 Subject: [PATCH 037/199] fix: updates privacy policy for facebook 2 --- src/components/policy/policyData.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/policy/policyData.ts b/src/components/policy/policyData.ts index e58fcce5..f0ab43db 100644 --- a/src/components/policy/policyData.ts +++ b/src/components/policy/policyData.ts @@ -18,21 +18,23 @@ const data = { ## 3. 수집하는 개인정보의 항목 회사는 회원가입, 서비스 이용 등을 위해 아래와 같은 개인정보를 수집하고 있습니다. + 1. 수집항목 -필수 입력 : 이메일 혹은 소셜 계정 정보 -자동 수집항목 : IP 정보, MAC정보, 이용 기록, 접속 로그, 쿠키, 접속 기록 등 +사용자 입력 +- 이메일 또는 소셜 미디어 서비스 ID: 사용자의 구분 +- 이름: 콘텐츠에서 작성자의 정보를 보여주기 위함 +- 프로필 사진: 콘텐츠에서 작성자의 정보를 보여주기 위함 + +자동 수집항목 : IP 정보, 이용 기록, 접속 로그, 쿠키, 접속 기록 등 + 2. 개인정보 수집방법: 홈페이지(회원 가입) ## 4. 개인정보의 파기절차 및 방법 이용자는 로그인 후 [설정](https://velog.io/setting) 페이지에서 계정을 탈퇴할 수 있습니다. -회사는 원칙적으로 개인정보의 수집 및 이용목적이 달성된 경우에는 지체없이 해당 개인정보를 파기합니다. 파기의 절차 및 방법은 다음과 같습니다. +또는, 가입한 계정의 이메일을 사용하여 개인정보 관리 책임자(7조 참고)에게 이메일을 발송하여 탈퇴 요청을 할 수 있습니다. ### 파기절차 -이용자가 회원가입 등을 위해 입력한 정보는 목적 달성 후 별도의 DB에 옮겨져(종이의 경우 별도의 서류) 내부 방침 및 기타 관련 법령에 의한 정보보호 사유에 따라 일정기간(개인정보 보유 및 이용기간 참조) 저장된 후 파기됩니다. 동 개인정보는 법률에 의한 경우가 아니고서는 보유되는 이외의 다른 목적으로 이용되지 않습니다. - -### 파기방법 -전자적 파일 형태의 개인정보는 기록을 재생할 수 없는 기술적 방법을 사용하여 삭제합니다. -종이에 출력된 개인정보는 분쇄기로 분쇄하거나 소각을 통하여 파기합니다. +탈퇴처리가 진행되면 DB에 있는 계정정보와, 해당 계정으로 작성된 모든 게시글과 댓글이 삭제됩니다. ## 5. 개인정보 제공 회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다. 다만, 아래의 경우에는 예외로 합니다. @@ -52,7 +54,8 @@ const data = { 기타 개인정보침해에 대한 신고나 상담이 필요한 경우에는 아래 회사에 문의하시기 바랍니다. 개인정보침해신고센터 (www.118.or.kr / 118) -8. 개인정보 취급방침 변경에 관한 사항 + +## 8. 개인정보 취급방침 변경에 관한 사항 이 개인정보 취급방침은 2018년 8월 23일부터 적용됩니다. 변경이전의 “정보보안 처리방침”을 과거이력 기록`, terms: `# 서비스 이용약관 From fd98ce34ce4df897100e90282434730d01030a87 Mon Sep 17 00:00:00 2001 From: velopert Date: Thu, 7 Jul 2022 10:41:24 +0900 Subject: [PATCH 038/199] fix: shows facebook error description below social login --- src/components/auth/AuthForm.tsx | 13 +++++++++++++ src/components/auth/AuthModal.tsx | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/auth/AuthForm.tsx b/src/components/auth/AuthForm.tsx index 8287d778..f96736a3 100644 --- a/src/components/auth/AuthForm.tsx +++ b/src/components/auth/AuthForm.tsx @@ -56,6 +56,13 @@ const AuthFormBlock = styled.div` } `; +const Warning = styled.div` + margin-top: 1rem; + margin-bottom: 1rem; + font-size: 0.875rem; + color: ${themedPalette.text3}; +`; + export interface AuthFormProps { mode: AuthMode; loading: boolean; @@ -101,6 +108,12 @@ const AuthForm: React.FC = ({

소셜 계정으로 {modeText}

+ + 현재 페이스북 로그인이 정상적으로 작동하지 않으며 고치는중에 + 있습니다. 페이스북 계정과 같은 이메일로 로그인을 시도하시거나, 해당 + 계정에 이메일이 설정되어 있지 않다면 contact@velog.io로 문의 메일을 + 보내주세요. +
diff --git a/src/components/auth/AuthModal.tsx b/src/components/auth/AuthModal.tsx index 35b22150..2ad87485 100644 --- a/src/components/auth/AuthModal.tsx +++ b/src/components/auth/AuthModal.tsx @@ -21,7 +21,7 @@ const AuthModalBlock = styled.div<{ visible: boolean }>` z-index: ${zIndexes.AuthModal}; .wrapper { width: 606px; - height: 480px; + height: 530px; ${media.small} { flex: 1; width: auto; From cc33941b6d47343756e8ebd840fa33c35095b5c4 Mon Sep 17 00:00:00 2001 From: velopert Date: Wed, 13 Jul 2022 09:09:01 +0900 Subject: [PATCH 039/199] fix: fb login now working --- src/components/auth/AuthForm.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/auth/AuthForm.tsx b/src/components/auth/AuthForm.tsx index f96736a3..f4b462f6 100644 --- a/src/components/auth/AuthForm.tsx +++ b/src/components/auth/AuthForm.tsx @@ -108,12 +108,6 @@ const AuthForm: React.FC = ({

소셜 계정으로 {modeText}

- - 현재 페이스북 로그인이 정상적으로 작동하지 않으며 고치는중에 - 있습니다. 페이스북 계정과 같은 이메일로 로그인을 시도하시거나, 해당 - 계정에 이메일이 설정되어 있지 않다면 contact@velog.io로 문의 메일을 - 보내주세요. -
From 5b029f079d644e91d90ded9c3735050c310814e2 Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 24 Jul 2022 17:41:04 +0900 Subject: [PATCH 040/199] feat: allows comment delete of ownPost --- src/components/post/PostCommentItem.tsx | 15 ++++++++++++++- src/components/post/PostCommentsList.tsx | 5 ++++- src/components/post/PostReplies.tsx | 3 +++ src/containers/post/PostComments.tsx | 3 +++ src/containers/post/PostRepliesContainer.tsx | 3 +++ src/containers/post/PostViewer.tsx | 1 + 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/components/post/PostCommentItem.tsx b/src/components/post/PostCommentItem.tsx index 00d46366..60316ea4 100644 --- a/src/components/post/PostCommentItem.tsx +++ b/src/components/post/PostCommentItem.tsx @@ -129,6 +129,7 @@ export interface PostCommentItemProps { comment: Comment; ownComment: boolean; onRemove: (id: string) => any; + ownPost: boolean; } interface TogglerProps { @@ -152,6 +153,7 @@ const PostCommentItem: React.FC = ({ comment, ownComment, onRemove, + ownPost, }) => { const { id, user, created_at, text, replies_count, deleted, level } = comment; const [open, onToggleOpen] = useBoolean(false); @@ -192,6 +194,11 @@ const PostCommentItem: React.FC = ({ onRemove(id)}>삭제
)} + {ownPost && !(ownComment && !editing) && ( +
+ onRemove(id)}>삭제 +
+ )} {editing ? ( = ({ {level < 2 && ( )} - {open && } + {open && ( + + )} ); diff --git a/src/components/post/PostCommentsList.tsx b/src/components/post/PostCommentsList.tsx index 4ca8a70b..c3a4c953 100644 --- a/src/components/post/PostCommentsList.tsx +++ b/src/components/post/PostCommentsList.tsx @@ -9,21 +9,24 @@ export interface PostCommentsListProps { comments: Comment[]; currentUserId: null | string; onRemove: (id: string) => any; + ownPost: boolean; } const PostCommentsList: React.FC = ({ comments, currentUserId, onRemove, + ownPost, }) => { return ( - {comments.map(comment => ( + {comments.map((comment) => ( ))} diff --git a/src/components/post/PostReplies.tsx b/src/components/post/PostReplies.tsx index 289f2f3a..8cae2219 100644 --- a/src/components/post/PostReplies.tsx +++ b/src/components/post/PostReplies.tsx @@ -57,6 +57,7 @@ export interface PostRepliesProps { onReply: (text: string) => any; onHide: () => void; onRemove: (id: string) => any; + ownPost: boolean; } const PostReplies: React.FC = ({ @@ -64,6 +65,7 @@ const PostReplies: React.FC = ({ onReply, onHide, onRemove, + ownPost, }) => { const [writing, onToggle] = useBoolean(false); const currentUserId = useUserId(); @@ -90,6 +92,7 @@ const PostReplies: React.FC = ({ comments={comments} currentUserId={currentUserId} onRemove={onRemove} + ownPost={ownPost} /> {hasComments && } {writing || !hasComments ? ( diff --git a/src/containers/post/PostComments.tsx b/src/containers/post/PostComments.tsx index b3bdde02..f97da7d1 100644 --- a/src/containers/post/PostComments.tsx +++ b/src/containers/post/PostComments.tsx @@ -17,6 +17,7 @@ export interface PostCommentsProps { comments: Comment[]; postId: string; count: number; + ownPost: boolean; } const MarginTop = styled.div` @@ -27,6 +28,7 @@ const PostComments: React.FC = ({ comments, postId, count, + ownPost, }) => { const [askRemove, onToggleAskRemove] = useBoolean(false); const [removeId, setRemoveId] = useState(''); @@ -64,6 +66,7 @@ const PostComments: React.FC = ({ comments={comments} currentUserId={currentUserId} onRemove={onRemove} + ownPost={ownPost} /> diff --git a/src/containers/post/PostRepliesContainer.tsx b/src/containers/post/PostRepliesContainer.tsx index 4b4170d3..78f82bc8 100644 --- a/src/containers/post/PostRepliesContainer.tsx +++ b/src/containers/post/PostRepliesContainer.tsx @@ -16,11 +16,13 @@ import PopupOKCancel from '../../components/common/PopupOKCancel'; export interface PostRepliesProps { commentId: string; onHide: () => void; + ownPost: boolean; } const PostRepliesContainer: React.FC = ({ commentId, onHide, + ownPost, }) => { const [askRemove, onToggleAskRemove] = useBoolean(false); const [removeId, setRemoveId] = useState(''); @@ -76,6 +78,7 @@ const PostRepliesContainer: React.FC = ({ <> = ({ count={post.comments_count} comments={post.comments} postId={post.id} + ownPost={post.user.id === userId} /> {showRecommends && (userId !== null || isVeryOld) && ( Date: Sun, 24 Jul 2022 18:17:23 +0900 Subject: [PATCH 041/199] feat: shows likes from post card --- src/components/common/FlatPostCard.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/common/FlatPostCard.tsx b/src/components/common/FlatPostCard.tsx index a0dc0ab6..aac2c75c 100644 --- a/src/components/common/FlatPostCard.tsx +++ b/src/components/common/FlatPostCard.tsx @@ -13,6 +13,7 @@ import RatioImage from './RatioImage'; import media from '../../lib/styles/media'; import PrivatePostLabel from './PrivatePostLabel'; import optimizeImage from '../../lib/optimizeImage'; +import { LikeIcon } from '../../static/svg'; const PostCardBlock = styled.div` padding-top: 4rem; @@ -106,6 +107,16 @@ const PostCardBlock = styled.div` margin-left: 0.5rem; margin-right: 0.5rem; } + + .likes { + display: flex; + align-items: center; + svg { + width: 0.875rem; + height: 0.875rem; + margin-right: 0.25rem; + } + } } .tags-wrapper { margin-bottom: -0.875rem; @@ -186,6 +197,11 @@ const FlatPostCard = ({ post, hideUser }: PostCardProps) => { {formatDate(post.released_at)}
·
{post.comments_count}개의 댓글 +
·
+ + + {post.likes} + {post.is_private && ( <>
·
From be916b283ecedc918fbd95c19560a443b0cee854 Mon Sep 17 00:00:00 2001 From: velopert Date: Mon, 5 Sep 2022 17:54:21 +0900 Subject: [PATCH 042/199] disables ads --- src/components/common/PostCardGrid.tsx | 1 + src/containers/post/PostViewer.tsx | 18 ++++++++++-------- src/server/Html.tsx | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/common/PostCardGrid.tsx b/src/components/common/PostCardGrid.tsx index f1fcd4cc..868d979d 100644 --- a/src/components/common/PostCardGrid.tsx +++ b/src/components/common/PostCardGrid.tsx @@ -28,6 +28,7 @@ function PostCardGrid({ posts, loading, forHome, forPost }: PostCardGridProps) { }, []); const postsWithAds = useMemo(() => { + if (1 === 1) return posts; // disable adsense if (user) return posts; // hide ads to users if (adBlocked) return posts; if (!forHome) return posts; diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 83179cee..c1924b92 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -404,13 +404,14 @@ const PostViewer: React.FC = ({ - 1000 * 60 * 60 * 24 * 21 + false + // !isVeryOld && + // Date.now() - new Date(post.released_at).getTime() > + // 1000 * 60 * 60 * 24 * 21 } /> )} - {isVeryOld && userId === null && } + {/* {isVeryOld && userId === null && } */} = ({ - 1000 * 60 * 60 * 24 * 30 + false + // !isVeryOld && + // post.user.id !== userId && + // Date.now() - new Date(post.released_at).getTime() > + // 1000 * 60 * 60 * 24 * 30 } /> )} diff --git a/src/server/Html.tsx b/src/server/Html.tsx index e418b9bb..fb56ed65 100644 --- a/src/server/Html.tsx +++ b/src/server/Html.tsx @@ -67,11 +67,11 @@ function Html({ /> ))} - + > */} */} + + + diff --git a/src/containers/base/Core.tsx b/src/containers/base/Core.tsx index 44cdbedc..0daacf4b 100644 --- a/src/containers/base/Core.tsx +++ b/src/containers/base/Core.tsx @@ -31,7 +31,7 @@ const Core: React.FC = ({ layer }) => { // adds setTimeout for page title sync // is there any better solution? setTimeout(() => { - gtag('config', 'UA-125599395-1', { + gtag('config', 'G-8D0MD2S4PK', { page_path: location.pathname + location.search, }); }, 1000); diff --git a/src/server/Html.tsx b/src/server/Html.tsx index b7198592..b20448e1 100644 --- a/src/server/Html.tsx +++ b/src/server/Html.tsx @@ -79,15 +79,15 @@ function Html({ > From b5600d4b119923cd06265b756c93dd3c69940ed7 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 30 Aug 2023 15:16:17 +0900 Subject: [PATCH 073/199] feat: redirect to v3 client host --- .env => .env.development | 4 +++- .env.production | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) rename .env => .env.development (61%) create mode 100644 .env.production diff --git a/.env b/.env.development similarity index 61% rename from .env rename to .env.development index 97223465..99329aff 100644 --- a/.env +++ b/.env.development @@ -4,5 +4,7 @@ REACT_APP_CLIENT_V3_HOST=http://localhost:3001 REACT_APP_API_HOST=http://localhost:5002/ -REACT_APP_GRAPHQL_HOST=https://v2cdn.velog.io/ +REACT_APP_CLIENT_V3_HOST=http://localhost:3001/ + +REACT_APP_GRAPHQL_HOST=http://localhost:5002/ REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..a118abc2 --- /dev/null +++ b/.env.production @@ -0,0 +1,8 @@ +PUBLIC_URL=/ + +REACT_APP_API_HOST=http://localhost:5002/ + +REACT_APP_CLIENT_V3_HOST=http://localhost:3001/ + +REACT_APP_GRAPHQL_HOST=https://v2.velog.io/ +REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ \ No newline at end of file From 71fd338ff4358317974f8d90fba71d9aab6a6bb1 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 30 Aug 2023 15:57:25 +0900 Subject: [PATCH 074/199] chore: remove if condition in main page --- src/pages/home/HomePage.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index dae68c44..956ee2ba 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -12,9 +12,7 @@ import FloatingHeader from '../../components/base/FloatingHeader'; export type HomePageProps = {}; function HomePage(props: HomePageProps) { - if (process.env.NODE_ENV === 'production') { - window.location.href = process.env.REACT_APP_CLIENT_V3_HOST!; - } + window.location.href = process.env.REACT_APP_CLIENT_V3_HOST!; return (
From aa496a434cfda612dd749a4ecceadb30a7a957c2 Mon Sep 17 00:00:00 2001 From: winverse Date: Wed, 6 Sep 2023 01:05:43 +0900 Subject: [PATCH 075/199] chore: change css --- src/components/common/LabelInput.tsx | 2 +- src/components/register/RegisterForm.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/common/LabelInput.tsx b/src/components/common/LabelInput.tsx index 38541dce..606564a3 100644 --- a/src/components/common/LabelInput.tsx +++ b/src/components/common/LabelInput.tsx @@ -61,7 +61,7 @@ const LabelInputBlock = styled.div<{ focus: boolean }>` border-color: ${themedPalette.primary1}; `} input { - width: 1; + width: 100%; } svg { font-size: 1.5rem; diff --git a/src/components/register/RegisterForm.tsx b/src/components/register/RegisterForm.tsx index 9b3cdbaa..d780b7f5 100644 --- a/src/components/register/RegisterForm.tsx +++ b/src/components/register/RegisterForm.tsx @@ -78,7 +78,7 @@ const RegisterForm: React.FC = ({ label="아이디" placeholder="아이디를 입력하세요" value={form.username} - size={15} + size={20} /> Date: Fri, 8 Sep 2023 13:23:23 +0900 Subject: [PATCH 076/199] chore: clean up env --- .env.development | 3 --- .env.production | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.env.development b/.env.development index 99329aff..c370e8e7 100644 --- a/.env.development +++ b/.env.development @@ -1,10 +1,7 @@ PUBLIC_URL=/ REACT_APP_CLIENT_V3_HOST=http://localhost:3001 - REACT_APP_API_HOST=http://localhost:5002/ -REACT_APP_CLIENT_V3_HOST=http://localhost:3001/ - REACT_APP_GRAPHQL_HOST=http://localhost:5002/ REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ \ No newline at end of file diff --git a/.env.production b/.env.production index a118abc2..51ff374c 100644 --- a/.env.production +++ b/.env.production @@ -1,8 +1,7 @@ PUBLIC_URL=/ +REACT_APP_CLIENT_V3_HOST=http://localhost:3001 REACT_APP_API_HOST=http://localhost:5002/ -REACT_APP_CLIENT_V3_HOST=http://localhost:3001/ - REACT_APP_GRAPHQL_HOST=https://v2.velog.io/ REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ \ No newline at end of file From 766074eabc7cc072f7d2570f105c1b8287bea977 Mon Sep 17 00:00:00 2001 From: winverse Date: Wed, 13 Sep 2023 16:51:11 +0900 Subject: [PATCH 077/199] feat: Implement follow button in Post page --- .gitignore | 2 +- src/components/common/UserProfile.tsx | 42 ++++++++++++++++-------- src/components/post/MobileLikeButton.tsx | 9 ++++- src/components/post/PostFollowButton.tsx | 38 +++++++++++++++++++++ src/components/post/PostHead.tsx | 20 +++++------ 5 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 src/components/post/PostFollowButton.tsx diff --git a/.gitignore b/.gitignore index c70b7cf4..b91ca42f 100755 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,4 @@ jspm_packages # Serverless directories .serverless -.webpack \ No newline at end of file +.webpack diff --git a/src/components/common/UserProfile.tsx b/src/components/common/UserProfile.tsx index 22b5d00d..7d72f4a9 100644 --- a/src/components/common/UserProfile.tsx +++ b/src/components/common/UserProfile.tsx @@ -16,6 +16,7 @@ import SkeletonTexts from './SkeletonTexts'; import { Link } from 'react-router-dom'; import media from '../../lib/styles/media'; import optimizeImage from '../../lib/optimizeImage'; +import PostFollowButton from '../post/PostFollowButton'; const UserProfileBlock = styled.div` ${media.medium} { @@ -26,7 +27,17 @@ const UserProfileBlock = styled.div` const Section = styled.div` display: flex; - align-items: center; + justify-content: space-between; + .left { + display: flex; + align-items: center; + } + + .right { + display: flex; + align-items: center; + } + ${media.small} { flex-direction: column; align-items: flex-start; @@ -169,18 +180,23 @@ const UserProfile: React.FC = ({ return (
- - profile - - -
- {displayName} -
-
{description}
-
+
+ + profile + + +
+ {displayName} +
+
{description}
+
+
+
+ +
diff --git a/src/components/post/MobileLikeButton.tsx b/src/components/post/MobileLikeButton.tsx index da6f412c..3ebb8988 100644 --- a/src/components/post/MobileLikeButton.tsx +++ b/src/components/post/MobileLikeButton.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled, { css } from 'styled-components'; import { themedPalette } from '../../lib/styles/themes'; import { LikeIcon } from '../../static/svg'; +import media from '../../lib/styles/media'; export type MobileLikeButtonProps = { likes: number; @@ -19,11 +20,12 @@ function MobileLikeButton({ likes, onToggle, liked }: MobileLikeButtonProps) { } const Button = styled.button<{ liked: boolean }>` + display: none; + align-items: center; background: ${themedPalette.bg_element1}; border: 1px solid ${themedPalette.border2}; padding-left: 0.75rem; padding-right: 0.75rem; - display: flex; align-items: center; height: 1.5rem; border-radius: 0.75rem; @@ -49,6 +51,11 @@ const Button = styled.button<{ liked: boolean }>` color: white; } `} + + ${media.medium} { + display: flex; + margin-left: 0.5rem; + } `; export default MobileLikeButton; diff --git a/src/components/post/PostFollowButton.tsx b/src/components/post/PostFollowButton.tsx new file mode 100644 index 00000000..9cdb6f03 --- /dev/null +++ b/src/components/post/PostFollowButton.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import styled from 'styled-components'; +import media from '../../lib/styles/media'; +import { buttonColorMap } from '../../lib/styles/palette'; + +export interface PostFollowButtonProps {} + +const FollowButtonBlock = styled.button` + outline: none; + border: none; + font-size: 1rem; + cursor: pointer; + padding-left: 1rem; + padding-right: 1rem; + height: 2rem; + border-radius: 1rem; + background: ${buttonColorMap['teal'].background}; + color: ${buttonColorMap['teal'].color}; + font-weight: 600; + &:hover { + background: ${buttonColorMap['teal'].hoverBackground}; + } + + ${media.medium} { + font-size: 0.875rem; + border-radius: 0.75rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + border-radius: 0.75rem; + height: 24px; + } +`; + +const PostFollowButton: React.FC = () => { + return 팔로우; +}; + +export default PostFollowButton; diff --git a/src/components/post/PostHead.tsx b/src/components/post/PostHead.tsx index 2728ba86..1c795881 100644 --- a/src/components/post/PostHead.tsx +++ b/src/components/post/PostHead.tsx @@ -12,6 +12,7 @@ import TagList from '../common/TagList'; import { Link } from 'react-router-dom'; import PrivatePostLabel from '../common/PrivatePostLabel'; import optimizeImage from '../../lib/optimizeImage'; +import PostFollowButton from './PostFollowButton'; const PostHeadBlock = styled(VelogResponsive)` margin-top: 5.5rem; @@ -44,12 +45,12 @@ const PostHeadBlock = styled(VelogResponsive)` `; const SubInfo = styled.div` - align-items: center; font-size: 1rem; color: ${themedPalette.text2}; /* font-family: 'Spoqa Han Sans'; */ display: flex; justify-content: space-between; + align-items: center; .information { .username { color: ${themedPalette.text1}; @@ -77,6 +78,10 @@ const SubInfo = styled.div` } `; +const SubInfoRight = styled.div` + display: flex; +`; + const EditRemoveGroup = styled.div` display: flex; justify-content: flex-end; @@ -119,14 +124,6 @@ const Thumbnail = styled.img` } `; -const MobileOnly = styled.div` - align-items: center; - display: none; - ${media.medium} { - display: flex; - } -`; - export interface PostHeadProps { title: string; tags: string[]; @@ -200,7 +197,10 @@ const PostHead: React.FC = ({ )} - {mobileLikeButton} + + {!ownPost && } + {mobileLikeButton} + {shareButtons} From 0d89c22ba557ebbfd9ca84949eb5d13a0d436455 Mon Sep 17 00:00:00 2001 From: winverse Date: Wed, 13 Sep 2023 20:10:01 +0900 Subject: [PATCH 078/199] styles: change postFollowButton css --- src/components/common/UserProfile.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/components/common/UserProfile.tsx b/src/components/common/UserProfile.tsx index 7d72f4a9..43a9abcd 100644 --- a/src/components/common/UserProfile.tsx +++ b/src/components/common/UserProfile.tsx @@ -28,20 +28,15 @@ const UserProfileBlock = styled.div` const Section = styled.div` display: flex; justify-content: space-between; + align-items: center; .left { display: flex; - align-items: center; - } - .right { - display: flex; - align-items: center; + ${media.small} { + flex-direction: column; + } } - ${media.small} { - flex-direction: column; - align-items: flex-start; - } img { display: block; width: 8rem; From 192bd574d944d7df8ae4fa516ad958a81743a5f3 Mon Sep 17 00:00:00 2001 From: winverse Date: Thu, 14 Sep 2023 16:02:13 +0900 Subject: [PATCH 079/199] feat: Add followed value when read post --- src/lib/graphql/post.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index 33190713..3e782cb3 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -319,6 +319,7 @@ export const READ_POST = gql` } } } + followed } } `; From 9bb8b66d261be302dc5718fbd391fc57dd124955 Mon Sep 17 00:00:00 2001 From: winverse Date: Thu, 14 Sep 2023 21:21:16 +0900 Subject: [PATCH 080/199] feat: connect to follow api --- src/components/common/UserProfile.tsx | 7 +-- src/components/post/PostFollowButton.tsx | 55 ++++++++++++---- src/components/post/PostHead.tsx | 5 +- src/containers/post/PostViewer.tsx | 62 +++++++++++++++++++ src/containers/velog/UserProfileContainer.tsx | 8 +-- src/lib/graphql/__data__/post.data.ts | 4 +- src/lib/graphql/post.ts | 1 + src/lib/graphql/user.ts | 12 ++++ 8 files changed, 127 insertions(+), 27 deletions(-) diff --git a/src/components/common/UserProfile.tsx b/src/components/common/UserProfile.tsx index 43a9abcd..fc040ca7 100644 --- a/src/components/common/UserProfile.tsx +++ b/src/components/common/UserProfile.tsx @@ -16,7 +16,6 @@ import SkeletonTexts from './SkeletonTexts'; import { Link } from 'react-router-dom'; import media from '../../lib/styles/media'; import optimizeImage from '../../lib/optimizeImage'; -import PostFollowButton from '../post/PostFollowButton'; const UserProfileBlock = styled.div` ${media.medium} { @@ -143,6 +142,7 @@ export interface UserProfileProps { description: string; profileLinks: ProfileLinks; username: string; + followButton?: React.ReactNode; } function includeProtocol(address: string) { @@ -157,6 +157,7 @@ const UserProfile: React.FC = ({ description, profileLinks, username, + followButton, }) => { const { email, facebook, github, twitter, url } = profileLinks; const [hoverEmail, setHoverEmail] = useState(false); @@ -189,9 +190,7 @@ const UserProfile: React.FC = ({
{description}
-
- -
+ {followButton &&
{followButton}
} diff --git a/src/components/post/PostFollowButton.tsx b/src/components/post/PostFollowButton.tsx index 9cdb6f03..4fbbae10 100644 --- a/src/components/post/PostFollowButton.tsx +++ b/src/components/post/PostFollowButton.tsx @@ -1,11 +1,29 @@ import * as React from 'react'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import media from '../../lib/styles/media'; import { buttonColorMap } from '../../lib/styles/palette'; -export interface PostFollowButtonProps {} +export interface PostFollowButtonProps { + followed: boolean; + onToggle: () => void; +} -const FollowButtonBlock = styled.button` +const PostFollowButton: React.FC = ({ + onToggle, + followed, +}) => { + return ( + + {followed ? '팔로잉' : '팔로우'} + + ); +}; + +const FollowButtonBlock = styled.button<{ followed: boolean }>` outline: none; border: none; font-size: 1rem; @@ -14,12 +32,28 @@ const FollowButtonBlock = styled.button` padding-right: 1rem; height: 2rem; border-radius: 1rem; - background: ${buttonColorMap['teal'].background}; - color: ${buttonColorMap['teal'].color}; + + ${(props) => + !props.followed && + css` + background: ${buttonColorMap['teal'].background}; + color: ${buttonColorMap['teal'].color}; + &:hover { + background: ${buttonColorMap['teal'].hoverBackground}; + } + `} + + ${(props) => + props.followed && + css` + background: ${buttonColorMap['lightGray'].background}; + color: ${buttonColorMap['lightGray'].color}; + &:hover { + background: ${buttonColorMap['lightGray'].hoverBackground}; + } + `} + font-weight: 600; - &:hover { - background: ${buttonColorMap['teal'].hoverBackground}; - } ${media.medium} { font-size: 0.875rem; @@ -30,9 +64,4 @@ const FollowButtonBlock = styled.button` height: 24px; } `; - -const PostFollowButton: React.FC = () => { - return 팔로우; -}; - export default PostFollowButton; diff --git a/src/components/post/PostHead.tsx b/src/components/post/PostHead.tsx index 1c795881..a3cc48cd 100644 --- a/src/components/post/PostHead.tsx +++ b/src/components/post/PostHead.tsx @@ -12,7 +12,6 @@ import TagList from '../common/TagList'; import { Link } from 'react-router-dom'; import PrivatePostLabel from '../common/PrivatePostLabel'; import optimizeImage from '../../lib/optimizeImage'; -import PostFollowButton from './PostFollowButton'; const PostHeadBlock = styled(VelogResponsive)` margin-top: 5.5rem; @@ -145,6 +144,7 @@ export interface PostHeadProps { toc: React.ReactNode; isPrivate?: boolean; mobileLikeButton: React.ReactNode; + followButton: React.ReactNode; onOpenStats(): void; } @@ -165,6 +165,7 @@ const PostHead: React.FC = ({ isPrivate, mobileLikeButton, onOpenStats, + followButton, }) => { const [askRemove, toggleAskRemove] = useToggle(false); @@ -198,7 +199,7 @@ const PostHead: React.FC = ({ )} - {!ownPost && } + {!ownPost && followButton} {mobileLikeButton} diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 760452dd..2159af59 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -37,6 +37,8 @@ import RelatedPost from './RelatedPost'; import optimizeImage from '../../lib/optimizeImage'; import { useSetShowFooter } from '../../components/velog/VelogPageTemplate'; import HorizontalBanner from './HorizontalBanner'; +import { FOLLOW_USER, UN_FOLLOW_USER } from '../../lib/graphql/user'; +import PostFollowButton from '../../components/post/PostFollowButton'; const UserProfileWrapper = styled(VelogResponsive)` margin-top: 16rem; @@ -96,6 +98,9 @@ const PostViewer: React.FC = ({ const [postView] = useMutation(POST_VIEW); const [likePost, { loading: loadingLike }] = useMutation(LIKE_POST); const [unlikePost, { loading: loadingUnlike }] = useMutation(UNLIKE_POST); + const [followUser, { loading: loadingFollowUser }] = useMutation(FOLLOW_USER); + const [unFollowUser, { loading: loadingUnFollowUser }] = + useMutation(UN_FOLLOW_USER); const { showNotFound } = useNotFound(); // const userLogo = useSelector((state: RootState) => state.header.userLogo); // const velogTitle = useMemo(() => { @@ -292,6 +297,51 @@ const PostViewer: React.FC = ({ } }; + const onFollowToggle = async () => { + if (loadingFollowUser || loadingUnFollowUser) return; + + const variables = { + follow_user_id: post.user.id, + }; + + const followFragment = gql` + fragment post on Post { + follwed + } + `; + + try { + if (!user) { + toast.error('로그인 후 이용해주세요.'); + return; + } + + if (post.followed) { + client.writeFragment({ + id: `Post:${post.id}`, + fragment: followFragment, + data: { + followed: false, + __typename: 'Post', + }, + }); + await unFollowUser({ variables }); + } else { + client.writeFragment({ + id: `Post:${post.id}`, + fragment: followFragment, + data: { + followed: true, + __typename: 'Post', + }, + }); + await followUser({ variables }); + } + } catch (e) { + console.log(e); + } + }; + const onShareClick = (type: 'facebook' | 'twitter' | 'clipboard') => { const { url } = match; const link = `https://velog.io${url}`; @@ -387,6 +437,12 @@ const PostViewer: React.FC = ({ onToggle={onLikeToggle} /> } + followButton={ + + } /> {userId === null && isVeryOld ? : null} @@ -397,6 +453,12 @@ const PostViewer: React.FC = ({ description={post.user.profile.short_bio} profileLinks={post.user.profile.profile_links} username={post.user.username} + followButton={ + + } /> diff --git a/src/containers/velog/UserProfileContainer.tsx b/src/containers/velog/UserProfileContainer.tsx index 4b9d5468..9f226ff3 100644 --- a/src/containers/velog/UserProfileContainer.tsx +++ b/src/containers/velog/UserProfileContainer.tsx @@ -32,12 +32,8 @@ const UserProfileContainer: React.FC = ({ if (loading || error || !data || !data.user) return ; - const { - display_name, - short_bio, - profile_links, - thumbnail, - } = data.user.profile; + const { display_name, short_bio, profile_links, thumbnail } = + data.user.profile; const isSeries = location.pathname.includes('/series'); diff --git a/src/lib/graphql/__data__/post.data.ts b/src/lib/graphql/__data__/post.data.ts index 602f73b5..cc0332d2 100644 --- a/src/lib/graphql/__data__/post.data.ts +++ b/src/lib/graphql/__data__/post.data.ts @@ -7,8 +7,7 @@ export const postData: { post: SinglePost } = { released_at: '2018-09-08T10:19:35.556Z', updated_at: '2019-07-30T14:19:14.326Z', tags: ['redux', '상태관리'], - body: - '리액트 생태계에서 사용되는 상태 관리 라이브러리는 대표적으로 Redux 와 MobX 가 있습니다. 이 둘의 특징을 배워보고 직접 사용하면서 알아가봅시다.\n\n## 상태 관리 라이브러리의 필요성\n\n상태 관리 라이브러리란게, 과연 필요할까요? 무조건 필요하지는 않습니다. 하지만 한가지는 확실합니다. 규모가 큰 앱에선 있는게, 확실히 편합니다. 제가 존경하는 개발자이면서도.. 리덕스의 라이브러리의 창시자인 Dan Abramov 는 말합니다. ["You might not need Redux"](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367) [(번역)](https://medium.com/@Dev_Bono/%EB%8B%B9%EC%8B%A0%EC%97%90%EA%B2%8C-redux%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EC%A7%80%EB%8F%84-%EB%AA%A8%EB%A6%85%EB%8B%88%EB%8B%A4-b88dcd175754)\n\n실제로, 여러분은 리덕스 없이도 좋은 앱을 만들 수 있습니다. 상태 관리 라이브러리가 없으면, 이전에는 글로벌 상태 관리를 하기에 조금 번거로웠는데 리액트 16.3 에서 [Context API](https://react-context.vlpt.us/03.html) 가 더욱 좋아지면서 글로벌 상태 관리 또한 별도의 라이브러리 없이 할 수 있게 되었습니다.\n\n> 글로벌 상태 관리란, 컴포넌트 간의 데이터 교류, 특히 부모-자식 관계가 아닌 컴포넌트끼리 데이터 교류를 하는것을 의미합니다.\n\n하지만, 그럼에도 불구하고 저는 상태 관리 라이브러리를 결국에는 배워보는걸 권장합니다. 모르고 안 쓰는거랑, 알고 안 쓰는거랑 다르기 때문이죠.', + body: '리액트 생태계에서 사용되는 상태 관리 라이브러리는 대표적으로 Redux 와 MobX 가 있습니다. 이 둘의 특징을 배워보고 직접 사용하면서 알아가봅시다.\n\n## 상태 관리 라이브러리의 필요성\n\n상태 관리 라이브러리란게, 과연 필요할까요? 무조건 필요하지는 않습니다. 하지만 한가지는 확실합니다. 규모가 큰 앱에선 있는게, 확실히 편합니다. 제가 존경하는 개발자이면서도.. 리덕스의 라이브러리의 창시자인 Dan Abramov 는 말합니다. ["You might not need Redux"](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367) [(번역)](https://medium.com/@Dev_Bono/%EB%8B%B9%EC%8B%A0%EC%97%90%EA%B2%8C-redux%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EC%A7%80%EB%8F%84-%EB%AA%A8%EB%A6%85%EB%8B%88%EB%8B%A4-b88dcd175754)\n\n실제로, 여러분은 리덕스 없이도 좋은 앱을 만들 수 있습니다. 상태 관리 라이브러리가 없으면, 이전에는 글로벌 상태 관리를 하기에 조금 번거로웠는데 리액트 16.3 에서 [Context API](https://react-context.vlpt.us/03.html) 가 더욱 좋아지면서 글로벌 상태 관리 또한 별도의 라이브러리 없이 할 수 있게 되었습니다.\n\n> 글로벌 상태 관리란, 컴포넌트 간의 데이터 교류, 특히 부모-자식 관계가 아닌 컴포넌트끼리 데이터 교류를 하는것을 의미합니다.\n\n하지만, 그럼에도 불구하고 저는 상태 관리 라이브러리를 결국에는 배워보는걸 권장합니다. 모르고 안 쓰는거랑, 알고 안 쓰는거랑 다르기 때문이죠.', short_description: '리액트 생태계에서 사용되는 상태 관리 라이브러리는 대표적으로 Redux 와 MobX 가 있습니다. 이 둘의 특징을 배워보고 직접 사용하면서 알아가봅시다.\n\n상태 관리 라이브러리의 필요성\n\n상태 관리 라이브러리란게, 과연 필요할까요? 무조건 필요하지는 않습니다. 하지만 한가지는 확실합니다. 규모가 큰 앱에선 있는게, 확실히 편합니다. 제가 존경하는 개발자이면...', is_markdown: true, @@ -178,5 +177,6 @@ export const postData: { post: SinglePost } = { }, }, }, + followed: false, }, }; diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index 3e782cb3..d6d0b2a9 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -124,6 +124,7 @@ export interface SinglePost { liked: boolean; likes: number; linked_posts: LinkedPosts; + followed: boolean; } export interface CommentWithReplies { diff --git a/src/lib/graphql/user.ts b/src/lib/graphql/user.ts index f1fc9bc1..7d501f52 100644 --- a/src/lib/graphql/user.ts +++ b/src/lib/graphql/user.ts @@ -182,6 +182,18 @@ export const CONFIRM_CHANGE_EMAIL = gql` } `; +export const FOLLOW_USER = gql` + mutation Follow($follow_user_id: ID!) { + follow(follow_user_id: $follow_user_id) + } +`; + +export const UN_FOLLOW_USER = gql` + mutation UnFollow($follow_user_id: ID!) { + unFollow(follow_user_id: $follow_user_id) + } +`; + export type AcceptIntegrationResponse = { acceptIntegration: string; }; From 6271e1d3a38e92512c8069f9e70b2e1666a8a879 Mon Sep 17 00:00:00 2001 From: carrick Date: Thu, 14 Sep 2023 21:40:27 +0900 Subject: [PATCH 081/199] feat: Add follow feature to post view component --- src/containers/post/PostViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 2159af59..dc5e669e 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -306,7 +306,7 @@ const PostViewer: React.FC = ({ const followFragment = gql` fragment post on Post { - follwed + followed } `; From 834e79acb932a28f3a41c1151b430c491f9de80f Mon Sep 17 00:00:00 2001 From: carrick Date: Mon, 25 Sep 2023 18:46:41 +0900 Subject: [PATCH 082/199] fix: change email bug fix --- src/containers/etc/EmailChange.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/etc/EmailChange.tsx b/src/containers/etc/EmailChange.tsx index 0a763edc..52aa435f 100644 --- a/src/containers/etc/EmailChange.tsx +++ b/src/containers/etc/EmailChange.tsx @@ -39,7 +39,7 @@ const EmailChange: React.FC = ({ location, history }) => { await client.mutate({ mutation: CONFIRM_CHANGE_EMAIL, variables: { - code: query.code, + code: query.code.split(':')[1], }, }); From 24ac52ca08b7629b38c925e814cb218d759ed9f7 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 27 Sep 2023 15:11:59 +0900 Subject: [PATCH 083/199] refactor: change email --- src/containers/etc/EmailChange.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/etc/EmailChange.tsx b/src/containers/etc/EmailChange.tsx index 52aa435f..0a763edc 100644 --- a/src/containers/etc/EmailChange.tsx +++ b/src/containers/etc/EmailChange.tsx @@ -39,7 +39,7 @@ const EmailChange: React.FC = ({ location, history }) => { await client.mutate({ mutation: CONFIRM_CHANGE_EMAIL, variables: { - code: query.code.split(':')[1], + code: query.code, }, }); From 26ad9ef9f1063d85930929c5ec316a5c2eadc107 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 27 Sep 2023 19:11:35 +0900 Subject: [PATCH 084/199] fix: naming convention --- src/containers/post/PostViewer.tsx | 10 +++++----- src/lib/graphql/user.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index dc5e669e..93f0b564 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -37,7 +37,7 @@ import RelatedPost from './RelatedPost'; import optimizeImage from '../../lib/optimizeImage'; import { useSetShowFooter } from '../../components/velog/VelogPageTemplate'; import HorizontalBanner from './HorizontalBanner'; -import { FOLLOW_USER, UN_FOLLOW_USER } from '../../lib/graphql/user'; +import { FOLLOW_USER, UNFOLLOW_USER } from '../../lib/graphql/user'; import PostFollowButton from '../../components/post/PostFollowButton'; const UserProfileWrapper = styled(VelogResponsive)` @@ -99,8 +99,8 @@ const PostViewer: React.FC = ({ const [likePost, { loading: loadingLike }] = useMutation(LIKE_POST); const [unlikePost, { loading: loadingUnlike }] = useMutation(UNLIKE_POST); const [followUser, { loading: loadingFollowUser }] = useMutation(FOLLOW_USER); - const [unFollowUser, { loading: loadingUnFollowUser }] = - useMutation(UN_FOLLOW_USER); + const [unfollowUser, { loading: loadingUnfollowUser }] = + useMutation(UNFOLLOW_USER); const { showNotFound } = useNotFound(); // const userLogo = useSelector((state: RootState) => state.header.userLogo); // const velogTitle = useMemo(() => { @@ -298,7 +298,7 @@ const PostViewer: React.FC = ({ }; const onFollowToggle = async () => { - if (loadingFollowUser || loadingUnFollowUser) return; + if (loadingFollowUser || loadingUnfollowUser) return; const variables = { follow_user_id: post.user.id, @@ -325,7 +325,7 @@ const PostViewer: React.FC = ({ __typename: 'Post', }, }); - await unFollowUser({ variables }); + await unfollowUser({ variables }); } else { client.writeFragment({ id: `Post:${post.id}`, diff --git a/src/lib/graphql/user.ts b/src/lib/graphql/user.ts index 7d501f52..1aec1096 100644 --- a/src/lib/graphql/user.ts +++ b/src/lib/graphql/user.ts @@ -188,9 +188,9 @@ export const FOLLOW_USER = gql` } `; -export const UN_FOLLOW_USER = gql` - mutation UnFollow($follow_user_id: ID!) { - unFollow(follow_user_id: $follow_user_id) +export const UNFOLLOW_USER = gql` + mutation Unfollow($follow_user_id: ID!) { + unfollow(follow_user_id: $follow_user_id) } `; From 6f81cc15d205c3c47257422b6bc8d7c1b7bba58b Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 1 Oct 2023 23:38:30 +0900 Subject: [PATCH 085/199] feat: change banner logic --- src/components/velog/VelogResponsive.tsx | 3 ++ src/containers/post/HorizontalBanner.tsx | 7 ++++- src/containers/post/PostViewer.tsx | 40 +++++++++++++++++++----- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/components/velog/VelogResponsive.tsx b/src/components/velog/VelogResponsive.tsx index 896c69b9..f774267f 100644 --- a/src/components/velog/VelogResponsive.tsx +++ b/src/components/velog/VelogResponsive.tsx @@ -14,18 +14,21 @@ const VelogResponsiveBlock = styled.div` export interface VelogResponsiveProps { className?: string; style?: React.CSSProperties; + onClick?: () => void; } const VelogResponsive: React.FC = ({ children, className, style, + onClick, }) => { return ( ); }; diff --git a/src/containers/post/HorizontalBanner.tsx b/src/containers/post/HorizontalBanner.tsx index 9045cc52..9aa6259b 100644 --- a/src/containers/post/HorizontalBanner.tsx +++ b/src/containers/post/HorizontalBanner.tsx @@ -1,14 +1,19 @@ import React, { useEffect } from 'react'; import styled from 'styled-components'; import VelogResponsive from '../../components/velog/VelogResponsive'; +import gtag from '../../lib/gtag'; function HorizontalBanner() { useEffect(() => { (window.adsbygoogle = window.adsbygoogle || []).push({}); }, []); + const onClick = () => { + gtag('event', 'banner_click'); + }; + return ( - + = ({ }; }, [onScroll]); + const shouldShowBanner = useMemo(() => { + if (!data?.post) return; + + const post = data.post; + const isOwnPost = post.user.id === userId; + const isVeryOld = + Date.now() - new Date(post.released_at).getTime() > + 1000 * 60 * 60 * 24 * 30; + + if (isOwnPost) return false; + if (!isVeryOld) return false; + return true; + }, [data?.post]); + + useEffect(() => { + if (!data?.post?.id) return; + if (!shouldShowBanner) return; + gtag('event', 'banner_view'); + console.log('banner_view'); + }, [data?.post?.id, shouldShowBanner]); + const onRemove = async () => { if (!data || !data.post) return; try { @@ -320,9 +348,7 @@ const PostViewer: React.FC = ({ const { post } = data; - const isVeryOld = - Date.now() - new Date(post.released_at).getTime() > - 1000 * 60 * 60 * 24 * 180; + const isContentLongEnough = post.body.length > 500; const url = `https://velog.io/@${username}/${post.url_slug}`; @@ -388,7 +414,7 @@ const PostViewer: React.FC = ({ /> } /> - {userId === null && isVeryOld ? : null} + {shouldShowBanner ? : null} = ({ } /> )} */} - {userId === null && isVeryOld && post.body.length > 300 ? ( - - ) : null} + {shouldShowBanner && isContentLongEnough ? : null} Date: Thu, 5 Oct 2023 01:40:11 +0900 Subject: [PATCH 086/199] feat: hide follow button by post owner --- src/components/common/UserProfile.tsx | 6 +++++- src/containers/post/PostViewer.tsx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/common/UserProfile.tsx b/src/components/common/UserProfile.tsx index fc040ca7..aa4068bf 100644 --- a/src/components/common/UserProfile.tsx +++ b/src/components/common/UserProfile.tsx @@ -143,6 +143,7 @@ export interface UserProfileProps { profileLinks: ProfileLinks; username: string; followButton?: React.ReactNode; + ownPost?: boolean; } function includeProtocol(address: string) { @@ -158,6 +159,7 @@ const UserProfile: React.FC = ({ profileLinks, username, followButton, + ownPost = false, }) => { const { email, facebook, github, twitter, url } = profileLinks; const [hoverEmail, setHoverEmail] = useState(false); @@ -190,7 +192,9 @@ const UserProfile: React.FC = ({
{description}
- {followButton &&
{followButton}
} + {!ownPost && followButton && ( +
{followButton}
+ )} diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 93f0b564..754a9ec5 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -453,6 +453,7 @@ const PostViewer: React.FC = ({ description={post.user.profile.short_bio} profileLinks={post.user.profile.profile_links} username={post.user.username} + ownPost={post.user.id === userId} followButton={ Date: Fri, 6 Oct 2023 20:24:45 +0900 Subject: [PATCH 087/199] fix: RelatedPost props --- src/containers/post/PostViewer.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 6b7e5c08..651bdc50 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -457,7 +457,9 @@ const PostViewer: React.FC = ({ } /> )} */} - {showRecommends ? : null} + {showRecommends ? ( + + ) : null} ); }; From 10029e831b1ec06bc9ddb8fca88f3f0e96455751 Mon Sep 17 00:00:00 2001 From: velopert Date: Fri, 6 Oct 2023 21:13:11 +0900 Subject: [PATCH 088/199] fix: ad-code --- src/components/common/AdFeed.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/common/AdFeed.tsx b/src/components/common/AdFeed.tsx index 7e40b56b..18259136 100644 --- a/src/components/common/AdFeed.tsx +++ b/src/components/common/AdFeed.tsx @@ -64,9 +64,9 @@ function AdFeed({ forPost, index }: { forPost?: boolean; index: number }) { className="adsbygoogle" style={{ display: 'block' }} data-ad-format="fluid" - data-ad-layout-key="-6u+e5+1a-3q+77" - data-ad-client="ca-pub-5574866530496701" - data-ad-slot="2841722540" + data-ad-layout-key="-6c+ce+2v-x+66" + data-ad-client="ca-pub-9161852896103498" + data-ad-slot="9446928451" >
); @@ -87,18 +87,18 @@ function AdFeed({ forPost, index }: { forPost?: boolean; index: number }) { className="adsbygoogle" style={{ display: 'block' }} data-ad-format="fluid" - data-ad-layout-key="-6u+e5+1a-3q+77" - data-ad-client="ca-pub-5574866530496701" - data-ad-slot="8480422066" + data-ad-layout-key="-6c+ce+2v-x+66" + data-ad-client="ca-pub-9161852896103498" + data-ad-slot="2793890198" > ) : ( )} {/* {isMobile ? ( From 66745f0423a7d5875535d719164c410ec8803ecc Mon Sep 17 00:00:00 2001 From: velopert Date: Sun, 8 Oct 2023 19:51:41 +0900 Subject: [PATCH 089/199] fix: ad logic --- src/containers/post/HorizontalBanner.tsx | 33 ++++++++++++++------ src/containers/post/PostViewer.tsx | 39 ++++++++---------------- src/containers/post/RelatedPost.tsx | 13 ++------ 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/src/containers/post/HorizontalBanner.tsx b/src/containers/post/HorizontalBanner.tsx index 9aa6259b..621e43a1 100644 --- a/src/containers/post/HorizontalBanner.tsx +++ b/src/containers/post/HorizontalBanner.tsx @@ -3,7 +3,11 @@ import styled from 'styled-components'; import VelogResponsive from '../../components/velog/VelogResponsive'; import gtag from '../../lib/gtag'; -function HorizontalBanner() { +type Props = { + isDisplayAd?: boolean; +}; + +function HorizontalBanner({ isDisplayAd = false }: Props) { useEffect(() => { (window.adsbygoogle = window.adsbygoogle || []).push({}); }, []); @@ -14,14 +18,25 @@ function HorizontalBanner() { return ( - + {isDisplayAd ? ( + + ) : ( + + )} ); } diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 651bdc50..5b00a36d 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -208,14 +208,24 @@ const PostViewer: React.FC = ({ if (isOwnPost) return false; if (!isVeryOld) return false; return true; - }, [data?.post]); + }, [data?.post, userId]); + + const shouldShowFooterBanner = useMemo(() => { + if (shouldShowBanner) return false; + if (!data?.post) return false; + if (userId) return false; + return true; + }, [userId, data?.post]); useEffect(() => { if (!data?.post?.id) return; if (!shouldShowBanner) return; gtag('event', 'banner_view'); console.log('banner_view'); - }, [data?.post?.id, shouldShowBanner]); + if (userId) { + gtag('event', 'banner_view_user'); + } + }, [data?.post?.id, shouldShowBanner, userId]); const onRemove = async () => { if (!data || !data.post) return; @@ -426,37 +436,14 @@ const PostViewer: React.FC = ({ /> - {/* {showRecommends && userId === null && !isVeryOld && ( - - // 1000 * 60 * 60 * 24 * 21 - } - /> - )} */} {shouldShowBanner && isContentLongEnough ? : null} - + {shouldShowFooterBanner ? : null} - {/* {showRecommends && (userId !== null || isVeryOld) && ( - - // 1000 * 60 * 60 * 24 * 30 - } - /> - )} */} {showRecommends ? ( ) : null} diff --git a/src/containers/post/RelatedPost.tsx b/src/containers/post/RelatedPost.tsx index 07e5a98b..84911829 100644 --- a/src/containers/post/RelatedPost.tsx +++ b/src/containers/post/RelatedPost.tsx @@ -39,19 +39,12 @@ function RelatedPost({ const cloned: (PartialPost | undefined)[] = [ ...data.post.recommended_posts, ]; - // get random number between 0 and length of array - const randomIndex = () => Math.floor(Math.random() * 8); + // get random number between 0 and 3 + const randomIndex = () => Math.floor(Math.random() * 3); const firstAdIndex = randomIndex(); - const secondAdIndex = (() => { - let index = randomIndex(); - while (index === firstAdIndex) { - index = randomIndex(); - } - return index; - })(); cloned[firstAdIndex] = undefined; - cloned[secondAdIndex] = undefined; + return cloned; }, [data, showAds, adBlocked]); From 37ceae3157acc0d70e1e62a2dcf8c9e8b00d67a2 Mon Sep 17 00:00:00 2001 From: velopert Date: Thu, 12 Oct 2023 01:35:43 +0900 Subject: [PATCH 090/199] fix: do not stringify if it is already string --- src/lib/storage.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/storage.ts b/src/lib/storage.ts index af20e37f..474b9e9a 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -16,7 +16,7 @@ class FallbackStorage { valid: boolean = checkLocalStorage(); setItem(key: string, value: any) { - const string = JSON.stringify(value); + const string = typeof value === 'string' ? value : JSON.stringify(value); if (this.valid) { localStorage.setItem(key, string); return; @@ -28,11 +28,12 @@ class FallbackStorage { let value = this.valid ? localStorage.getItem(key) : this.fallbackStorage[key]; + if (!value) return null; try { const parsed = JSON.parse(value || ''); return parsed; } catch (e) { - return null; + return value || null; } } From 74a1f238e51cdf6d945bb91f9724c802820190ce Mon Sep 17 00:00:00 2001 From: velopert Date: Thu, 12 Oct 2023 02:49:42 +0900 Subject: [PATCH 091/199] fix: refresh when moving to home --- src/components/base/HeaderLogo.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/base/HeaderLogo.tsx b/src/components/base/HeaderLogo.tsx index b05e265b..c097b88c 100644 --- a/src/components/base/HeaderLogo.tsx +++ b/src/components/base/HeaderLogo.tsx @@ -22,9 +22,9 @@ const HeaderLogo: React.FC = ({ if (!custom) { return ( - + - + ); } @@ -33,7 +33,7 @@ const HeaderLogo: React.FC = ({ const velogPath = `/@${username}`; return ( - + @@ -74,7 +74,7 @@ const HeaderLogoBlock = styled.div` } `; -const VelogLogoLink = styled(Link)` +const VelogLogoLink = styled.a` color: inherit; svg { From 7b4aa14b352d100df10f403a3414d65529bc6c40 Mon Sep 17 00:00:00 2001 From: velopert Date: Thu, 12 Oct 2023 03:24:03 +0900 Subject: [PATCH 092/199] fix: redirect new home onlogin --- src/containers/etc/EmailLogin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/etc/EmailLogin.tsx b/src/containers/etc/EmailLogin.tsx index 5062b238..d32bc981 100644 --- a/src/containers/etc/EmailLogin.tsx +++ b/src/containers/etc/EmailLogin.tsx @@ -42,7 +42,7 @@ const EmailLogin: React.FC = ({ location, history }) => { }); storage.setItem('CURRENT_USER', response.data.auth); - history.replace('/'); + window.location.href = '/'; } catch (e) { // TODO: show 401 toast.error('잘못된 접근입니다.'); From 954b376e7a6ca282b98e1ae13d8c1427427a481b Mon Sep 17 00:00:00 2001 From: carrick Date: Mon, 23 Oct 2023 12:40:37 +0900 Subject: [PATCH 093/199] chore: handle env --- .env.production | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.production b/.env.production index 51ff374c..034cf768 100644 --- a/.env.production +++ b/.env.production @@ -3,5 +3,5 @@ PUBLIC_URL=/ REACT_APP_CLIENT_V3_HOST=http://localhost:3001 REACT_APP_API_HOST=http://localhost:5002/ -REACT_APP_GRAPHQL_HOST=https://v2.velog.io/ +REACT_APP_GRAPHQL_HOST=https://v2cdn.velog.io/ REACT_APP_GRAPHQL_HOST_NOCDN=https://v2.velog.io/ \ No newline at end of file From e402a0f0e55c5c05d6d0238caf807d8d4672813b Mon Sep 17 00:00:00 2001 From: carrick Date: Sun, 29 Oct 2023 23:13:29 +0900 Subject: [PATCH 094/199] refactor: follow --- src/containers/post/PostViewer.tsx | 10 +++++----- src/lib/graphql/post.ts | 2 +- src/lib/graphql/user.ts | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 754a9ec5..9cc49ddb 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -98,8 +98,8 @@ const PostViewer: React.FC = ({ const [postView] = useMutation(POST_VIEW); const [likePost, { loading: loadingLike }] = useMutation(LIKE_POST); const [unlikePost, { loading: loadingUnlike }] = useMutation(UNLIKE_POST); - const [followUser, { loading: loadingFollowUser }] = useMutation(FOLLOW_USER); - const [unfollowUser, { loading: loadingUnfollowUser }] = + const [follow, { loading: loadingFollowUser }] = useMutation(FOLLOW_USER); + const [unfollow, { loading: loadingUnfollowUser }] = useMutation(UNFOLLOW_USER); const { showNotFound } = useNotFound(); // const userLogo = useSelector((state: RootState) => state.header.userLogo); @@ -301,7 +301,7 @@ const PostViewer: React.FC = ({ if (loadingFollowUser || loadingUnfollowUser) return; const variables = { - follow_user_id: post.user.id, + following_user_id: post.user.id, }; const followFragment = gql` @@ -325,7 +325,7 @@ const PostViewer: React.FC = ({ __typename: 'Post', }, }); - await unfollowUser({ variables }); + await unfollow({ variables }); } else { client.writeFragment({ id: `Post:${post.id}`, @@ -335,7 +335,7 @@ const PostViewer: React.FC = ({ __typename: 'Post', }, }); - await followUser({ variables }); + await follow({ variables }); } } catch (e) { console.log(e); diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index d6d0b2a9..8704a0d5 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -252,6 +252,7 @@ export const READ_POST = gql` url_slug likes liked + followed user { id username @@ -320,7 +321,6 @@ export const READ_POST = gql` } } } - followed } } `; diff --git a/src/lib/graphql/user.ts b/src/lib/graphql/user.ts index 1aec1096..12e402da 100644 --- a/src/lib/graphql/user.ts +++ b/src/lib/graphql/user.ts @@ -183,14 +183,14 @@ export const CONFIRM_CHANGE_EMAIL = gql` `; export const FOLLOW_USER = gql` - mutation Follow($follow_user_id: ID!) { - follow(follow_user_id: $follow_user_id) + mutation Follow($following_user_id: ID!) { + follow(following_user_id: $following_user_id) } `; export const UNFOLLOW_USER = gql` - mutation Unfollow($follow_user_id: ID!) { - unfollow(follow_user_id: $follow_user_id) + mutation Unfollow($following_user_id: ID!) { + unfollow(following_user_id: $following_user_id) } `; From 65e739e7a603932bae3a706ae54d9b5c137e2bba Mon Sep 17 00:00:00 2001 From: carrick Date: Mon, 30 Oct 2023 10:31:49 +0900 Subject: [PATCH 095/199] chore: change logo href --- src/components/base/HeaderLogo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/base/HeaderLogo.tsx b/src/components/base/HeaderLogo.tsx index def4ff63..4d365fd3 100644 --- a/src/components/base/HeaderLogo.tsx +++ b/src/components/base/HeaderLogo.tsx @@ -34,7 +34,7 @@ const HeaderLogo: React.FC = ({ const velogPath = `/@${username}`; return ( - + From 2e468267f98101b9c947779f520f70ee303e2989 Mon Sep 17 00:00:00 2001 From: carrick Date: Mon, 30 Oct 2023 10:43:22 +0900 Subject: [PATCH 096/199] fix: resolve eslint error --- src/containers/post/PostViewer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index eb981566..1380d0cc 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -213,14 +213,14 @@ const PostViewer: React.FC = ({ if (isOwnPost) return false; if (!isVeryOld) return false; return true; - }, [data?.post]); + }, [data, userId]); useEffect(() => { if (!data?.post?.id) return; if (!shouldShowBanner) return; gtag('event', 'banner_view'); console.log('banner_view'); - }, [data?.post?.id, shouldShowBanner]); + }, [data, shouldShowBanner]); const onRemove = async () => { if (!data || !data.post) return; From 96e5cde3527e5f02a2bcf174872a6df65dc694a1 Mon Sep 17 00:00:00 2001 From: carrick Date: Fri, 10 Nov 2023 06:16:00 +0900 Subject: [PATCH 097/199] fix: follow button style --- src/components/post/PostFollowButton.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/post/PostFollowButton.tsx b/src/components/post/PostFollowButton.tsx index 4fbbae10..51eaea85 100644 --- a/src/components/post/PostFollowButton.tsx +++ b/src/components/post/PostFollowButton.tsx @@ -32,6 +32,7 @@ const FollowButtonBlock = styled.button<{ followed: boolean }>` padding-right: 1rem; height: 2rem; border-radius: 1rem; + word-break: keep-all; ${(props) => !props.followed && From 7198295ce16a1b8fa47ffe32f3f2adf3814ae887 Mon Sep 17 00:00:00 2001 From: carrick Date: Fri, 10 Nov 2023 07:02:20 +0900 Subject: [PATCH 098/199] feat: apply VLink to Header --- src/components/base/HeaderUserMenu.tsx | 2 +- src/components/base/HeaderUserMenuItem.tsx | 34 +++++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/components/base/HeaderUserMenu.tsx b/src/components/base/HeaderUserMenu.tsx index 09c1138f..0dc6b88c 100644 --- a/src/components/base/HeaderUserMenu.tsx +++ b/src/components/base/HeaderUserMenu.tsx @@ -44,7 +44,7 @@ const HeaderUserMenu: React.FC = ({
- + 내 벨로그
diff --git a/src/components/base/HeaderUserMenuItem.tsx b/src/components/base/HeaderUserMenuItem.tsx index 5c0412f0..87ffd09c 100644 --- a/src/components/base/HeaderUserMenuItem.tsx +++ b/src/components/base/HeaderUserMenuItem.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import styled from 'styled-components'; import { Link } from 'react-router-dom'; import { themedPalette } from '../../lib/styles/themes'; +import VLink from '../common/VLink'; const WrapperLink = styled(Link)` display: block; @@ -9,6 +10,12 @@ const WrapperLink = styled(Link)` text-decoration: none; `; +const WrapperVLink = styled(VLink)` + display: block; + color: inherit; + text-decoration: none; +`; + const HeaderUserMenuItemBlock = styled.div` color: ${themedPalette.text1}; padding: 0.75rem 1rem; @@ -24,25 +31,38 @@ const HeaderUserMenuItemBlock = styled.div` interface HeaderUserMenuItemProps { to?: string; onClick?: () => void; + isMigrated?: boolean; } const HeaderUserMenuItem: React.FC = ({ children, to, onClick, + isMigrated = false, }) => { const jsx = ( {children} ); - return to ? ( - - {jsx} - - ) : ( - jsx - ); + + if (to && !isMigrated) { + return ( + + {jsx} + + ); + } + + if (to && isMigrated) { + return ( + + {jsx} + + ); + } + + return jsx; }; export default HeaderUserMenuItem; From 53179993b7a8696ce1b29a519edbe52c806e3031 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 29 Nov 2023 00:40:51 +0900 Subject: [PATCH 099/199] feat: implement follow button in userProfile component --- src/components/post/PostFollowButton.tsx | 219 ++++++++++++++++++----- src/containers/post/PostViewer.tsx | 58 +----- src/lib/graphql/__data__/post.data.ts | 2 +- src/lib/graphql/post.ts | 4 +- src/lib/graphql/user.ts | 1 + src/pages/velog/UserPage.tsx | 3 +- 6 files changed, 186 insertions(+), 101 deletions(-) diff --git a/src/components/post/PostFollowButton.tsx b/src/components/post/PostFollowButton.tsx index 51eaea85..ef29181e 100644 --- a/src/components/post/PostFollowButton.tsx +++ b/src/components/post/PostFollowButton.tsx @@ -1,68 +1,199 @@ import * as React from 'react'; import styled, { css } from 'styled-components'; import media from '../../lib/styles/media'; -import { buttonColorMap } from '../../lib/styles/palette'; +import { debounce } from 'throttle-debounce'; +import { useApolloClient, useMutation } from '@apollo/react-hooks'; +import { FOLLOW_USER, UNFOLLOW_USER } from '../../lib/graphql/user'; +import { gql } from 'apollo-boost'; +import useUser from '../../lib/hooks/useUser'; +import { toast } from 'react-toastify'; +import { themedPalette } from '../../lib/styles/themes'; export interface PostFollowButtonProps { + followingUserId: string; followed: boolean; - onToggle: () => void; } const PostFollowButton: React.FC = ({ - onToggle, + followingUserId, followed, }) => { + const client = useApolloClient(); + const currentUser = useUser(); + const [follow, { loading: loadingFollowUser }] = useMutation(FOLLOW_USER); + const [unfollow, { loading: loadingUnfollowUser }] = + useMutation(UNFOLLOW_USER); + + const [initialFollowState, setInitialFollowState] = + React.useState(followed); + const [currentFollowState, setCurrentFollowState] = + React.useState(followed); + + const [buttonText, setButtonText] = React.useState('팔로잉'); + + const onFollowButtonMouseLeave = () => { + setInitialFollowState(currentFollowState || false); + }; + + const onUnfollowButtonMouseEnter = () => { + setButtonText('언팔로우'); + }; + + const onUnfollowButtonMouseLeave = () => { + setButtonText('팔로잉'); + }; + + const onClick = debounce(300, async () => { + if (loadingFollowUser || loadingUnfollowUser) return; + + const variables = { + following_user_id: followingUserId, + }; + + const followFragment = gql` + fragment user on User { + is_followed + } + `; + + try { + if (!currentUser) { + toast.error('로그인 후 이용해주세요.'); + return; + } + + if (currentFollowState) { + client.writeFragment({ + id: `User:${followingUserId}`, + fragment: followFragment, + data: { + is_followed: false, + __typename: 'User', + }, + }); + await unfollow({ variables }); + } else { + client.writeFragment({ + id: `User:${followingUserId}`, + fragment: followFragment, + data: { + is_followed: true, + __typename: 'User', + }, + }); + await follow({ variables }); + setButtonText('팔로잉'); + } + + setInitialFollowState(!currentFollowState); + setCurrentFollowState(!currentFollowState); + } catch (error) { + console.log('handle follow state error', error); + } + }); + + React.useEffect(() => { + setInitialFollowState(followed); + setCurrentFollowState(followed); + }, [followed]); + return ( - {followed ? '팔로잉' : '팔로우'} + {!initialFollowState ? ( + + ) : ( + + )} ); }; -const FollowButtonBlock = styled.button<{ followed: boolean }>` - outline: none; - border: none; - font-size: 1rem; - cursor: pointer; - padding-left: 1rem; - padding-right: 1rem; - height: 2rem; - border-radius: 1rem; - word-break: keep-all; - - ${(props) => - !props.followed && - css` - background: ${buttonColorMap['teal'].background}; - color: ${buttonColorMap['teal'].color}; - &:hover { - background: ${buttonColorMap['teal'].hoverBackground}; - } - `} - - ${(props) => - props.followed && - css` - background: ${buttonColorMap['lightGray'].background}; - color: ${buttonColorMap['lightGray'].color}; - &:hover { - background: ${buttonColorMap['lightGray'].hoverBackground}; - } - `} - - font-weight: 600; +const FollowButtonBlock = styled.div<{ + followed: boolean; + unfollowed: boolean; +}>` + width: 96px; + height: 32px; + font-size: 16px; - ${media.medium} { - font-size: 0.875rem; - border-radius: 0.75rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - border-radius: 0.75rem; + ${media.small} { + width: 80px; height: 24px; + font-size: 14px; + } + + ${media.custom(425)} { + width: 72px; + font-size: 12px; + } + + .button { + display: flex; + box-shadow: none; + align-items: center; + justify-content: center; + background-color: white; + cursor: pointer; + border-radius: 16px; + font-weight: 700; + width: 100%; + height: 100%; + white-space: nowrap; + outline: none; + font-size: 16px; + + ${media.small} { + font-size: 14px; + } + + ${media.custom(425)} { + font-size: 12px; + } + } + + .follow-button { + color: ${themedPalette.primary1}; + border: 1px solid ${themedPalette.primary1}; + + ${(props) => + props.followed && + css` + color: ${themedPalette.bg_element6}; + border: 1px solid ${themedPalette.bg_element6}; + `} + } + + .unfollow-button { + color: ${themedPalette.bg_element6}; + border: 1px solid ${themedPalette.bg_element6}; + + ${(props) => + props.unfollowed && + css` + &:hover, + &:active { + color: ${themedPalette.destructive1}; + border: 1px solid ${themedPalette.destructive1}; + } + `} } `; + export default PostFollowButton; diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 1380d0cc..2c400ef2 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -44,7 +44,6 @@ import optimizeImage from '../../lib/optimizeImage'; import { useSetShowFooter } from '../../components/velog/VelogPageTemplate'; import HorizontalBanner from './HorizontalBanner'; import gtag from '../../lib/gtag'; -import { FOLLOW_USER, UNFOLLOW_USER } from '../../lib/graphql/user'; import PostFollowButton from '../../components/post/PostFollowButton'; const UserProfileWrapper = styled(VelogResponsive)` @@ -105,10 +104,8 @@ const PostViewer: React.FC = ({ const [postView] = useMutation(POST_VIEW); const [likePost, { loading: loadingLike }] = useMutation(LIKE_POST); const [unlikePost, { loading: loadingUnlike }] = useMutation(UNLIKE_POST); - const [follow, { loading: loadingFollowUser }] = useMutation(FOLLOW_USER); - const [unfollow, { loading: loadingUnfollowUser }] = - useMutation(UNFOLLOW_USER); const { showNotFound } = useNotFound(); + // const userLogo = useSelector((state: RootState) => state.header.userLogo); // const velogTitle = useMemo(() => { // if (!userLogo || !userLogo.title) return `${username}.log`; @@ -325,51 +322,6 @@ const PostViewer: React.FC = ({ } }; - const onFollowToggle = async () => { - if (loadingFollowUser || loadingUnfollowUser) return; - - const variables = { - following_user_id: post.user.id, - }; - - const followFragment = gql` - fragment post on Post { - followed - } - `; - - try { - if (!user) { - toast.error('로그인 후 이용해주세요.'); - return; - } - - if (post.followed) { - client.writeFragment({ - id: `Post:${post.id}`, - fragment: followFragment, - data: { - followed: false, - __typename: 'Post', - }, - }); - await unfollow({ variables }); - } else { - client.writeFragment({ - id: `Post:${post.id}`, - fragment: followFragment, - data: { - followed: true, - __typename: 'Post', - }, - }); - await follow({ variables }); - } - } catch (e) { - console.log(e); - } - }; - const onShareClick = (type: 'facebook' | 'twitter' | 'clipboard') => { const { url } = match; const link = `https://velog.io${url}`; @@ -465,8 +417,8 @@ const PostViewer: React.FC = ({ } followButton={ } /> @@ -482,8 +434,8 @@ const PostViewer: React.FC = ({ ownPost={post.user.id === userId} followButton={ } /> diff --git a/src/lib/graphql/__data__/post.data.ts b/src/lib/graphql/__data__/post.data.ts index cc0332d2..3686a95b 100644 --- a/src/lib/graphql/__data__/post.data.ts +++ b/src/lib/graphql/__data__/post.data.ts @@ -22,6 +22,7 @@ export const postData: { post: SinglePost } = { user: { id: 'c76ccc50-b34d-11e8-b01f-598f1220d1c8', username: 'velopert', + is_followed: true, profile: { id: 'c7caf1e0-b34d-11e8-b01f-598f1220d1c8', display_name: 'Minjun Kim', @@ -177,6 +178,5 @@ export const postData: { post: SinglePost } = { }, }, }, - followed: false, }, }; diff --git a/src/lib/graphql/post.ts b/src/lib/graphql/post.ts index 8704a0d5..6cad2ed4 100644 --- a/src/lib/graphql/post.ts +++ b/src/lib/graphql/post.ts @@ -102,6 +102,7 @@ export interface SinglePost { user: { id: string; username: string; + is_followed: boolean; profile: { id: string; display_name: string; @@ -124,7 +125,6 @@ export interface SinglePost { liked: boolean; likes: number; linked_posts: LinkedPosts; - followed: boolean; } export interface CommentWithReplies { @@ -252,10 +252,10 @@ export const READ_POST = gql` url_slug likes liked - followed user { id username + is_followed profile { id display_name diff --git a/src/lib/graphql/user.ts b/src/lib/graphql/user.ts index 12e402da..1d8d8162 100644 --- a/src/lib/graphql/user.ts +++ b/src/lib/graphql/user.ts @@ -28,6 +28,7 @@ export type User = { is_certified: boolean; profile: UserProfile; velogConfig: VelogConfig | null; + is_followed: boolean; }; export const GET_CURRENT_USER = gql` diff --git a/src/pages/velog/UserPage.tsx b/src/pages/velog/UserPage.tsx index 2ba87b91..c9cc4b20 100644 --- a/src/pages/velog/UserPage.tsx +++ b/src/pages/velog/UserPage.tsx @@ -17,7 +17,8 @@ export interface UserPageProps const UserPage: React.FC = ({ match, location }) => { const { username, tab } = match.params; - + window.location.href = `${process.env + .REACT_APP_CLIENT_V3_HOST!}/@${username}/posts`; return ( From 766b5aa725b806e98a3885cc937a452dbcbdd0d5 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 29 Nov 2023 00:51:48 +0900 Subject: [PATCH 100/199] chore: rename follow button component --- .../PostFollowButton.tsx => common/FollowButton.tsx} | 10 +++++----- src/containers/post/PostViewer.tsx | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) rename src/components/{post/PostFollowButton.tsx => common/FollowButton.tsx} (95%) diff --git a/src/components/post/PostFollowButton.tsx b/src/components/common/FollowButton.tsx similarity index 95% rename from src/components/post/PostFollowButton.tsx rename to src/components/common/FollowButton.tsx index ef29181e..57def48e 100644 --- a/src/components/post/PostFollowButton.tsx +++ b/src/components/common/FollowButton.tsx @@ -14,7 +14,7 @@ export interface PostFollowButtonProps { followed: boolean; } -const PostFollowButton: React.FC = ({ +const FollowButton: React.FC = ({ followingUserId, followed, }) => { @@ -43,7 +43,7 @@ const PostFollowButton: React.FC = ({ setButtonText('팔로잉'); }; - const onClick = debounce(300, async () => { + const onClick = debounce(300, () => { if (loadingFollowUser || loadingUnfollowUser) return; const variables = { @@ -71,7 +71,7 @@ const PostFollowButton: React.FC = ({ __typename: 'User', }, }); - await unfollow({ variables }); + unfollow({ variables }); } else { client.writeFragment({ id: `User:${followingUserId}`, @@ -81,7 +81,7 @@ const PostFollowButton: React.FC = ({ __typename: 'User', }, }); - await follow({ variables }); + follow({ variables }); setButtonText('팔로잉'); } @@ -196,4 +196,4 @@ const FollowButtonBlock = styled.div<{ } `; -export default PostFollowButton; +export default FollowButton; diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 2c400ef2..a6b34f9e 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -44,7 +44,7 @@ import optimizeImage from '../../lib/optimizeImage'; import { useSetShowFooter } from '../../components/velog/VelogPageTemplate'; import HorizontalBanner from './HorizontalBanner'; import gtag from '../../lib/gtag'; -import PostFollowButton from '../../components/post/PostFollowButton'; +import FollowButton from '../../components/common/FollowButton'; const UserProfileWrapper = styled(VelogResponsive)` margin-top: 16rem; @@ -266,7 +266,7 @@ const PostViewer: React.FC = ({ history.push(`/post-stats/${post.id}`); }; - const onLikeToggle = async () => { + const onLikeToggle = () => { if (loadingLike || loadingUnlike) return; const variables = { @@ -300,7 +300,7 @@ const PostViewer: React.FC = ({ __typename: 'Post', }, }); - await unlikePost({ + unlikePost({ variables, }); } else { @@ -313,7 +313,7 @@ const PostViewer: React.FC = ({ __typename: 'Post', }, }); - await likePost({ + likePost({ variables, }); } @@ -416,7 +416,7 @@ const PostViewer: React.FC = ({ /> } followButton={ - @@ -433,7 +433,7 @@ const PostViewer: React.FC = ({ username={post.user.username} ownPost={post.user.id === userId} followButton={ - From d2d553bbfe96d82cb7cc4f6dfbd304c5183b7070 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 29 Nov 2023 01:22:30 +0900 Subject: [PATCH 101/199] feat: apply vlink to the velog page --- src/components/base/HeaderLogo.tsx | 9 ++++----- src/components/common/FlatPostCard.tsx | 9 +++++---- src/components/common/PostCard.tsx | 5 +++-- src/components/common/UserProfile.tsx | 10 +++++----- src/components/post/PostCommentItem.tsx | 10 +++++----- src/components/post/PostHead.tsx | 4 ++-- src/containers/velog/SeriesPosts.tsx | 8 ++++---- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/components/base/HeaderLogo.tsx b/src/components/base/HeaderLogo.tsx index 4d365fd3..52b19d08 100644 --- a/src/components/base/HeaderLogo.tsx +++ b/src/components/base/HeaderLogo.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import styled from 'styled-components'; -import { Link } from 'react-router-dom'; import { Logo, VelogIcon } from '../../static/svg'; import { UserLogo } from '../../modules/header'; import { themedPalette } from '../../lib/styles/themes'; @@ -31,15 +30,15 @@ const HeaderLogo: React.FC = ({ } if (!userLogo) return
; if (!username) return
; - const velogPath = `/@${username}`; + const velogPath = `/@${username}/posts`; return ( - + {userLogo.title || createFallbackTitle(username)} - + ); }; @@ -75,7 +74,7 @@ const HeaderLogoBlock = styled.div` } `; -const VelogLogoLink = styled(Link)` +const VelogLogoLink = styled(VLink)` color: inherit; svg { diff --git a/src/components/common/FlatPostCard.tsx b/src/components/common/FlatPostCard.tsx index aac2c75c..8ab7b6fc 100644 --- a/src/components/common/FlatPostCard.tsx +++ b/src/components/common/FlatPostCard.tsx @@ -14,6 +14,7 @@ import media from '../../lib/styles/media'; import PrivatePostLabel from './PrivatePostLabel'; import optimizeImage from '../../lib/optimizeImage'; import { LikeIcon } from '../../static/svg'; +import VLink from './VLink'; const PostCardBlock = styled.div` padding-top: 4rem; @@ -150,7 +151,7 @@ const FlatPostCard = ({ post, hideUser }: PostCardProps) => { }; const url = `/@${post.user.username}/${post.url_slug}`; - const velogUrl = `/@${post.user.username}`; + const velogUrl = `/@${post.user.username}/posts`; if (!post.user.profile) { console.log(post); @@ -159,7 +160,7 @@ const FlatPostCard = ({ post, hideUser }: PostCardProps) => { {!hideUser && (
- + { )} alt="thumbnail" /> - +
- {post.user.username} + {post.user.username}
)} diff --git a/src/components/common/PostCard.tsx b/src/components/common/PostCard.tsx index f73f7660..21c278e3 100644 --- a/src/components/common/PostCard.tsx +++ b/src/components/common/PostCard.tsx @@ -14,6 +14,7 @@ import { mediaQuery } from '../../lib/styles/media'; import { Link } from 'react-router-dom'; import usePrefetchPost from '../../lib/hooks/usePrefetchPost'; import gtag from '../../lib/gtag'; +import VLink from './VLink'; export type PostCardProps = { post: PartialPost; @@ -73,7 +74,7 @@ function PostCard({ post, forHome, forPost }: PostCardProps) {