diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js new file mode 100644 index 00000000..3a2c243f --- /dev/null +++ b/public/mockServiceWorker.js @@ -0,0 +1,284 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const PACKAGE_VERSION = '2.2.10' +const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body], + ) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() + + function passthrough() { + const headers = Object.fromEntries(requestClone.headers.entries()) + + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers['x-msw-intention'] + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) + }) +} + +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} diff --git a/src/assets/svgs/index.tsx b/src/assets/svgs/index.tsx index 9a0ce665..844bb68e 100644 --- a/src/assets/svgs/index.tsx +++ b/src/assets/svgs/index.tsx @@ -36,6 +36,7 @@ export { default as MainIcnArrowWhite } from './mainIcnArrowWhite.svg?react'; export { default as MakeGroupPlusHoverBtn } from './mainMakeGroupBtnHover.svg?react'; export { default as MakeGroupPlusBtn } from './mainMakeGroupPlusBtn.svg?react'; +export { default as MainCarouselNextBtn } from './mainBtnNext.svg?react'; export { default as MainTogglearrowClosedIc } from './mainTogglearrowClosedIc.svg?react'; export { default as MainToggleArrowOpenedIc } from './mainTogglearrowOpenedIc.svg?react'; diff --git a/src/constants/defaultImgUrl.ts b/src/constants/defaultImgUrl.ts index 37346d18..73da9b7a 100644 --- a/src/constants/defaultImgUrl.ts +++ b/src/constants/defaultImgUrl.ts @@ -1 +1 @@ -export const DEFAULT_IMG_URL = 'https://mile-s3.s3.ap-northeast-2.amazonaws.com/test/groupMile.png'; +export const DEFAULT_IMG_URL = 'https://mile-new-s3.s3.ap-northeast-2.amazonaws.com/mile.png'; diff --git a/src/constants/footerLink.ts b/src/constants/footerLink.ts index 62caa90a..34b0c0e3 100644 --- a/src/constants/footerLink.ts +++ b/src/constants/footerLink.ts @@ -1,6 +1,6 @@ export const FOOTER_LINK = { MAIL: 'mailto:unnamedwritings@gmail.com', - INSTAGRAM: 'https://www.instagram.com/milewriting.official/', + INSTAGRAM: 'https://www.instagram.com/milewriting?igsh=YzloYno5a3ZneGlq', NOTION: 'https://milewriting.notion.site/fc4386f655b24be48dfa0946804c8e1e?pvs=4', WALLA: 'https://walla.my/survey/OjhPSWMQWbXmY8Nm9Ehv', }; diff --git a/src/pages/main/Main.tsx b/src/pages/main/Main.tsx index 2003128f..ee70be5c 100644 --- a/src/pages/main/Main.tsx +++ b/src/pages/main/Main.tsx @@ -84,6 +84,7 @@ const GroupCarouselLayout = styled.section` `; const CarouselContainer = styled.div` + /* width: 100.7rem; */ width: 93rem; height: 100%; @@ -94,7 +95,7 @@ const CarouselContainer = styled.div` `; const CarouselBox = styled.div` - cursor: default; + cursor: pointer; `; const CarouselTitle = styled.h1` diff --git a/src/pages/main/components/CarouselContent.tsx b/src/pages/main/components/CarouselContent.tsx index af66eaaa..08941246 100644 --- a/src/pages/main/components/CarouselContent.tsx +++ b/src/pages/main/components/CarouselContent.tsx @@ -47,7 +47,7 @@ const CarouselContent = ({ {postContent} - + {isContainPhoto && ( theme.colors.white}; border-radius: 8px; @@ -119,7 +121,7 @@ const Title = styled.h2` const ContentContainer = styled.div` display: flex; flex-direction: column; - height: 29rem; + height: 100%; `; const SubText = styled.span<{ isContainPhoto: boolean; isLast: boolean }>` @@ -184,7 +186,7 @@ const GroupRoutingText = styled.p` ${({ theme }) => theme.fonts.title8}; `; -const GroupRoutingBtnBox = styled.div` +const GroupRoutingBtnBox = styled.button` cursor: pointer; & > svg:hover { diff --git a/src/pages/main/components/GroupCarousel.tsx b/src/pages/main/components/GroupCarousel.tsx index acd6daaf..b6004653 100644 --- a/src/pages/main/components/GroupCarousel.tsx +++ b/src/pages/main/components/GroupCarousel.tsx @@ -1,18 +1,16 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; + import { useNavigate } from 'react-router-dom'; import Slider from 'react-slick'; import Responsive from '../../../components/commons/Responsive/Responsive'; import '../styles/slick-theme.css'; import '../styles/slick.css'; import CarouselContent from './CarouselContent'; - import { groupPropTypes } from '../types/groupContent'; import { MainGroupRoutingBtn, MainIcnArrowBlack as MainIcnArrowBlackIcon, - MainIcnArrowPurple as MainIcnArrowPurpleIcon, } from '../../../assets/svgs'; import Spacing from '../../../components/commons/Spacing'; import { MOBILE_MEDIA_QUERY } from '../../../styles/mediaQuery'; @@ -40,26 +38,20 @@ const GroupCarousel = ({ data }: carouselItemPropTypes) => { const handleRoutingDetail = (groupId: string, postId: string) => { navigate(`/detail/${groupId}/${postId}`); }; - const [isHovered, setIsHovered] = useState(false); return ( <> {data?.map((moim) => ( - handleButtonOnClick(moim.moimId)} - onMouseOver={() => setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > + handleButtonOnClick(moim.moimId)}> {moim.moimName} - {isHovered ? : } + - + {moim.moimPosts.map((post, index) => ( { {post.topicName} {post.postTitle} - {post.postContent} + + {post.isContainPhoto ? ( + {post.postContent} + ) : ( + {post.postContent} + )} - + {post.isContainPhoto && ( + + )} ), )} @@ -140,7 +139,7 @@ const PostCardImg = styled.img` border-radius: 6px; `; -const Contents = styled.span` +const NoImageContents = styled.span` display: -webkit-box; width: 100%; height: 5.4rem; @@ -158,16 +157,35 @@ const Contents = styled.span` ${({ theme }) => theme.fonts.mBody2}; `; -const Topic = styled.h1` +const Contents = styled.span` + display: -webkit-box; + width: 100%; + + margin-top: 1rem; + overflow: hidden; + + color: ${({ theme }) => theme.colors.gray80}; + text-align: left; + text-overflow: ellipsis; + word-wrap: break-word; + + -webkit-line-clamp: 10; + -webkit-box-orient: vertical; + + ${({ theme }) => theme.fonts.mBody2} +`; + +const Topic = styled.h2` margin-bottom: 0.6rem; color: ${({ theme }) => theme.colors.gray70}; ${({ theme }) => theme.fonts.mSubtitle1}; `; -const Title = styled.h2` - ${({ theme }) => theme.fonts.mTitle1}; +const Title = styled.h1` + ${({ theme }) => theme.fonts.mTitle2}; line-height: 120%; + word-break: break-all; `; const PostCard = styled.article` @@ -186,7 +204,7 @@ const PostCard = styled.article` const CarouselWrapper = styled.div` flex-direction: column; height: 29.4rem; - + cursor: default; @media ${MOBILE_MEDIA_QUERY} { height: 34.4rem; padding: 0 2rem; @@ -211,13 +229,17 @@ const GroupButton = styled.button` background-color: ${({ theme }) => theme.colors.white}; border: 1px solid ${({ theme }) => theme.colors.white}; + + svg path { + stroke: #6139d1; + } } `; const CarouselLayout = styled.div` display: flex; width: 100%; - overflow: scroll; + -ms-overflow-style: none; ::-webkit-scrollbar { @@ -226,9 +248,12 @@ const CarouselLayout = styled.div` `; const CarouselContainer = styled(Slider)` - width: 93rem; + width: 93rem !important; height: 24rem; + .slick-list { + width: 100%; + } .slick-slide.slick-active:last-child { width: 75.4rem !important; } @@ -252,7 +277,7 @@ const PostContainer = styled.div` display: flex; gap: 2rem; width: 100%; - max-width: 81rem; + height: 29rem; overflow: hidden; `; diff --git a/src/pages/main/styles/slick-theme.css b/src/pages/main/styles/slick-theme.css index 38d2f1ec..e597a291 100644 --- a/src/pages/main/styles/slick-theme.css +++ b/src/pages/main/styles/slick-theme.css @@ -9,8 +9,6 @@ display: block; - width: 20px; - height: 20px; padding: 0; -webkit-transform: translate(0, -50%); -ms-transform: translate(0, -50%); @@ -37,10 +35,7 @@ display: block; content: url('/src/assets/svgs/mainBtnBeforeHover.svg'); } -.main .slick-next:hover:before { - display: block; - content: url('/src/assets/svgs/mainBtnNextHover.svg'); -} + .main .slick-prev.slick-disabled:before, .main .slick-next.slick-disabled:before { display: none; @@ -80,7 +75,7 @@ .main .slick-next { display: block; position: absolute; - right: -5rem; + right: -7.5rem; top: 11rem; } [dir='rtl'] .slick-next { @@ -94,6 +89,10 @@ [dir='rtl'] .slick-next:before { display: block; } +.main .slick-next:hover:before { + display: block; + content: url('/src/assets/svgs/mainBtnNextHover.svg'); +} /* Dots */ .slick-dotted.slick-slider { diff --git a/src/pages/postPage/constants/editorDefaultImg.ts b/src/pages/postPage/constants/editorDefaultImg.ts index 778295e9..3cd72ffa 100644 --- a/src/pages/postPage/constants/editorDefaultImg.ts +++ b/src/pages/postPage/constants/editorDefaultImg.ts @@ -1,2 +1 @@ -export const EDITOR_DEFAULT_IMG = - 'https://mile-s3.s3.ap-northeast-2.amazonaws.com/test/groupMile.png'; +export const EDITOR_DEFAULT_IMG = 'https://mile-new-s3.s3.ap-northeast-2.amazonaws.com/mile.png';