Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#161: 알림 받을 수 있도록 기능 추가 & 마이페이지 디자인 개선 #164

Merged
merged 20 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0df2238
Fix: 일시적으로 vite-plugin-pwa 사용 중지
pp449 Aug 19, 2023
dffc090
Feat: 서비스워커가 push 알림을 받을 수 있도록 추가
pp449 Aug 19, 2023
0e8ac3f
Feat: 인코딩하는 함수 추가
pp449 Aug 19, 2023
26344da
Feat: 구독버튼 클릭 시 서버에 정보가 가도록 추가
pp449 Aug 19, 2023
e512aa1
Fix: 드래그 못하도록 수정
pp449 Aug 19, 2023
854c558
Feat: 토글 버튼 추가
pp449 Aug 19, 2023
34491b8
Style: 마이페이지 디자인 추가
pp449 Aug 19, 2023
9106b73
Test: MSW로 subscribe 요청 처리 추가
pp449 Aug 19, 2023
bdfdc5b
학과 선택 안한경우, 알림 거부한 경우 에러핸들링 추가
pp449 Aug 19, 2023
c2ce7b7
Feat: 토글 클릭 시 모달창 띄우기 추가 & 구독 내용을 로컬 스토리지에 추가
pp449 Aug 20, 2023
4027706
Feat: 렌더링 시 구독 내용이 있다면 토글을 on 으로 설정하도록 추가
pp449 Aug 20, 2023
8aa0a13
Style: 첫 렌더링 시에는 토글 on 애니메이션이 없고 클릭 시 애니메이션 적용되도록 디자인 변경
pp449 Aug 20, 2023
81c3272
Fix: 구독하는 학과도 같이 서버에 post 하도록 수정
pp449 Aug 20, 2023
ba8d205
Feat: 구독 취소는 POST 서 DELETE 메소드로 변경
pp449 Aug 20, 2023
110515b
Feat: 학과 새로 선택 시 구독 취소하도록 설정
pp449 Aug 20, 2023
2b894ab
Feat: 선택된 학과가 없는경우 처리
pp449 Aug 20, 2023
cc61415
Fix: 애니메이션 적용 에러 수정
pp449 Aug 20, 2023
1530327
Fix: 테스트 통과하도록 수정
pp449 Aug 20, 2023
8a23145
Refactor: 학교 알림과 학과 알림을 구분하기 위해 URI에 /major 추가
pp449 Aug 20, 2023
755b8b8
Fix: 구독 취소시 로컬스토리지에서 제거되도록 수정
pp449 Aug 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"name": "부림이",
"short_name": "부림이",
"start_url": "/",
"display": "fullscreen",
"background_color": "#ffffff",
"lang": "en",
"scope": "/",
"orientation": "portrait",
"theme_color": "#ffffff",
"icons": [
{
"src": "icons/icon-48x48.png",
"sizes": "48x48",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{ "src": "icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" }
]
}
8 changes: 8 additions & 0 deletions sw.js → public/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ self.addEventListener('activate', (e) => {
self.addEventListener('fetch', (e) => {
console.log('[Service Worker] fetched resource ' + e.request.url);
});

self.addEventListener('push', (e) => {
const data = e.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: data.icon,
});
});
50 changes: 50 additions & 0 deletions src/components/Button/Toggle/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import styled from '@emotion/styled';
import { THEME } from '@styles/ThemeProvider/theme';
import { MouseEventHandler } from 'react';

interface Props {
isOn: boolean;
changeState: MouseEventHandler<HTMLElement>;
animation: boolean;
}

interface Circle {
isOn: boolean;
animation: boolean;
}

const ToggleButton = (props: Props) => {
const { isOn, changeState, animation } = props;

return (
<Button onClick={changeState} isOn={isOn} animation={animation}>
<Circle isOn={isOn} animation={animation} />
</Button>
);
};

export default ToggleButton;

const Button = styled.button<Circle>`
position: relative;
border: none;
width: 3.2rem;
height: 1.8rem;

transition: ${(prop) => (prop.animation ? 'all 0.3s ease-in-out' : 'none')};

background-color: ${(prop) => (prop.isOn ? THEME.PRIMARY : THEME.BACKGROUND)};
border-radius: 1rem;
`;

const Circle = styled.div<Circle>`
position: absolute;
border-radius: 50%;
width: 1.2rem;
height: 1.2rem;
transition: ${(prop) => (prop.animation ? 'all 0.3s ease-in-out' : 'none')};

background-color: #f5f5f5;
top: 0.3rem;
left: ${(prop) => (prop.isOn ? '1.7rem' : '0.4rem')};
`;
10 changes: 9 additions & 1 deletion src/components/List/DepartmentList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Button from '@components/Button';
import Icon from '@components/Icon';
import AlertModal from '@components/Modal/AlertModal';
import ConfirmModal from '@components/Modal/ConfirmModal';
import { SERVER_URL } from '@config/index';
import { MODAL_MESSAGE } from '@constants/modal-messages';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
Expand All @@ -18,7 +19,7 @@ const DepartmentList = () => {
const [selected, setSelected] = useState<string>('');
const [buttonDisable, setButtonDisable] = useState<boolean>(true);
const { routerTo, goBack } = useRouter();
const { setMajor } = useMajor();
const { major, setMajor } = useMajor();
const { college } = useParams();
const { openModal, closeModal } = useModals();

Expand All @@ -40,6 +41,13 @@ const DepartmentList = () => {

const handlerMajorSetModal = () => {
closeModal(ConfirmModal);
const storedSubscribe = localStorage.getItem('subscribe');
if (major && storedSubscribe) {
http.delete(`${SERVER_URL}/api/subscription/major`, {
data: { subscription: JSON.parse(storedSubscribe), major },
});
localStorage.removeItem('subscribe');
}
Comment on lines +44 to +50
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 함수는 이미 선택된 전공이 있고, 수정할 경우를 처리하기 위한 함수가 맞나요~?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 만약 전공을 새로 바꾼다면 바뀐 전공을 구독하는게 아닌 이전 전공 구독을 취소하게 만들었어요

const afterSpace = selected.substring(selected.indexOf(' ') + 1);
localStorage.setItem('major', afterSpace);
setMajor(afterSpace);
Expand Down
14 changes: 14 additions & 0 deletions src/hooks/urlBase64ToUint8Array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const urlBase64ToUint8Array = (base64String: string) => {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');

const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);

for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};

export default urlBase64ToUint8Array;
2 changes: 2 additions & 0 deletions src/mocks/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { setupWorker } from 'msw';
import { announceHandlers } from './handlers/announceHandlers';
import { graduationHandler } from './handlers/graudationHandler';
import { majorHandlers } from './handlers/majorHandlers';
import { subscribeHandler } from './handlers/subscribeHandler';
import { testHandlers } from './handlers/testHandlers';

export const worker = setupWorker(
...majorHandlers,
...testHandlers,
...announceHandlers,
...graduationHandler,
...subscribeHandler,
); // 브라우저 환경 서버
11 changes: 11 additions & 0 deletions src/mocks/handlers/subscribeHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { SERVER_URL } from '@config/index';
import { RequestHandler, rest } from 'msw';

export const subscribeHandler: RequestHandler[] = [
rest.post(SERVER_URL + '/api/subscription', (req, res, ctx) => {
return res(ctx.status(200));
}),
rest.delete(SERVER_URL + '/api/subscription', (req, res, ctx) => {
return res(ctx.status(200));
}),
];
2 changes: 2 additions & 0 deletions src/mocks/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { setupServer } from 'msw/node';

import { announceHandlers } from './handlers/announceHandlers';
import { majorHandlers } from './handlers/majorHandlers';
import { subscribeHandler } from './handlers/subscribeHandler';
import { testHandlers } from './handlers/testHandlers';

export const server = setupServer(
...majorHandlers,
...testHandlers,
...announceHandlers,
...subscribeHandler,
);
2 changes: 1 addition & 1 deletion src/pages/My/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('마이 페이지 동작 테스트', () => {
{ wrapper: MemoryRouter },
);

const majorEditButton = screen.getByTestId('edit');
const majorEditButton = screen.getByText('학과 선택하러가기');
await userEvent.click(majorEditButton);
expect(mockRouterTo).toHaveBeenCalledWith('/major-decision');
});
Expand Down
Loading