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.PWA) #345

Merged
merged 8 commits into from
Aug 12, 2023
Merged
Binary file added frontend/public/icons/android-icon-144x144.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/android-icon-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/android-icon-36x36.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/android-icon-48x48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/android-icon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/android-icon-72x72.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/android-icon-96x96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-114x114.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-120x120.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-144x144.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-152x152.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-180x180.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-57x57.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-60x60.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-72x72.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-76x76.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon-precomposed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/apple-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/public/icons/browserconfig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
Binary file added frontend/public/icons/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/favicon-96x96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/favicon.ico
Binary file not shown.
59 changes: 59 additions & 0 deletions frontend/public/icons/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "VoTogether",
"short_name": "VoTogether",
"icons": [
{
"src": "./android-icon-36x36.png",
"sizes": "36x36",
"type": "image/png",
"density": "0.75",
"purpose": "any maskable"
},
{
"src": "./android-icon-48x48.png",
"sizes": "48x48",
"type": "image/png",
"density": "1.0",
"purpose": "any maskable"
},
{
"src": "./android-icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"density": "1.5",
"purpose": "any maskable"
},
{
"src": "./android-icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"density": "2.0",
"purpose": "any maskable"
},
{
"src": "./android-icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"density": "3.0",
"purpose": "any maskable"
},
{
"src": "./android-icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"density": "4.0",
"purpose": "any maskable"
},
{
"src": "/images/android-icon-512x512.png",
"type": "image/png",
"sizes": "512x512",
"density": "5.0",
"purpose": "any maskable"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff"
}
Binary file added frontend/public/icons/ms-icon-144x144.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/ms-icon-150x150.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/ms-icon-310x310.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/ms-icon-70x70.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/splash-screen-768x1004.png
Binary file added frontend/public/icons/thumbnail.png
40 changes: 40 additions & 0 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,46 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="icons/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="icons/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
<link rel="manifest" href="icons/manifest.json" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
<meta name="theme-color" content="#ffffff" />

<meta name="apple-mobile-web-app-title" content="VoTogether" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="#BE0000" />

<link
rel="apple-touch-startup-image"
sizes="768x1004"
href="icons/splash-screen-768x1004.png"
/>

<meta property="og:type" content="website" />
<meta property="og:url" content="https://votogether.com/" />
<meta property="og:title" content="VoTogether" />
<meta property="og:image" content="icons/thumbnail.jpg" />
<meta
property="og:description"
content='"VoTogether(보투게더)"는 투표를 통해 고민을 해결하고 재미를 찾는 투표 SNS 서비스입니다. 고민이 있으신가요? 글을 써보세요! 익명으로 글을 작성하며 나를 드러내지 않아도 쉽게 고민을 나눌 수 있습니다. 보투게더는 사람들의 다양한 주제로 질문하고 답변하면서, 사람들의 반응을 확인할 수 있다는 점에서 특별합니다. 나의 이야기가 우리의 이야기가 되는 공간, 보투게더에서 우리 함께해요!✌🏻.'
/>
<meta property="og:site_name" content="VoTogether" />
<meta property="og:locale" content="ko_KR" />

<title>VoTogether</title>
</head>
<body>
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/api/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ export const editPost = async (postId: number, updatedPost: FormData) => {
return await multiPutFetch(`${BASE_URL}/posts/${postId}`, updatedPost);
};


export const removePost = async (postId: number) => {
export const deletePost = async (postId: number) => {
return await deleteFetch(`${MOCK_URL}/posts/${postId}`);
};

Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/arrow-up-on-square.svg
3 changes: 3 additions & 0 deletions frontend/src/assets/x_mark_black.svg
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Meta, StoryObj } from '@storybook/react';

import MobileInstallPrompt from '.';

const meta: Meta<typeof MobileInstallPrompt> = {
component: MobileInstallPrompt,
};

export default meta;
type Story = StoryObj<typeof MobileInstallPrompt>;

export const Android: Story = {
render: () => (
<MobileInstallPrompt
platform="android"
handleInstallClick={() => {}}
handleCancelClick={() => {}}
/>
),
};

export const Ios: Story = {
render: () => (
<MobileInstallPrompt
platform="ios"
handleInstallClick={() => {}}
handleCancelClick={() => {}}
/>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import arrowUp from '@assets/arrow-up-on-square.svg';
import logo from '@assets/logo.svg';
import cancel from '@assets/x_mark_black.svg';

import * as S from './style';

interface MobileInstallPromptProps {
platform: 'ios' | 'android';
handleInstallClick: () => void;
handleCancelClick: () => void;
}

export default function MobileInstallPrompt({
platform,
handleInstallClick,
handleCancelClick,
}: MobileInstallPromptProps) {
return (
<S.Container>
<S.Content>
<S.Header>
<S.LogoImage src={logo} alt="보투게더 로고 이미지" />
<S.HeaderContent>
<S.HeaderTop>
<S.Title>VoTogether</S.Title>
<S.CancelButton onClick={handleCancelClick}>
<S.IconImage src={cancel} alt="취소 아이콘" />
</S.CancelButton>
</S.HeaderTop>
<S.Description>
VoTogether는 앱처럼 원활히 사용할 수 있습니다. 설치하시겠습니까?
</S.Description>
</S.HeaderContent>
</S.Header>
{platform === 'ios' && (
<S.IosContainer>
<S.Description>
브라우저 메뉴바에서 <S.IconImage src={arrowUp} alt="추가하기 아이콘" /> 모양 버튼을
눌러 "홈 화면에 추가하기"를 통해 설치를 할 수 있습니다.
</S.Description>
</S.IosContainer>
)}
{platform === 'android' && (
<S.InstallButton onClick={handleInstallClick}>홈 화면에 추가</S.InstallButton>
)}
</S.Content>
</S.Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { styled } from 'styled-components';

import { theme } from '@styles/theme';

export const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;

width: 100vw;
border-top: 1px solid rgba(0, 0, 0, 0.3);

position: fixed;
bottom: 0;
left: 0;

background-color: white;

z-index: ${theme.zIndex.modal};
`;

export const Content = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;

width: max-content;
padding: 30px 20px;
`;

export const Header = styled.div`
display: flex;
justify-content: space-between;

margin-bottom: 50px;
`;

export const LogoImage = styled.img`
border-radius: 16px;

width: 80px;
height: 80px;
`;

export const HeaderContent = styled.div`
display: flex;
flex-direction: column;

margin-left: 24px;
`;

export const HeaderTop = styled.div`
display: flex;
justify-content: space-between;
align-items: center;

margin-bottom: 10px;
`;

export const Title = styled.span`
font-size: 2.4rem;
font-weight: 700;
`;

export const Description = styled.p`
font-size: 1.6rem;
font-weight: 700;
`;

export const CancelButton = styled.button`
padding: 10px;

position: relative;
bottom: 10px;
left: 10px;

cursor: pointer;
`;

export const IconImage = styled.img`
width: 24px;
height: 24px;
`;

export const InstallButton = styled.button`
align-self: end;

border-radius: 6px;

width: 190px;
height: 40px;

font-size: 1.6rem;
font-weight: 500;

color: white;
background-color: #5383ed;

cursor: pointer;
`;

export const IosContainer = styled.div`
display: flex;
align-items: center;
align-self: end;
gap: 8px;
`;
49 changes: 49 additions & 0 deletions frontend/src/components/common/AppInstallPrompt/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Fragment, useEffect, useState } from 'react';

import { BeforeInstallPromptEvent } from '../../../../window';

import MobileInstallPrompt from './MobileInstallPrompt';

export default function AppInstallPrompt() {
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);
const isDeviceIOS = /iPad|iPhone|iPod/.test(window.navigator.userAgent);

useEffect(() => {
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);

return () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
};
}, []);

const handleBeforeInstallPrompt = (event: BeforeInstallPromptEvent) => {
event.preventDefault();
setDeferredPrompt(event);
};

const handleInstallClick = () => {
if (deferredPrompt) {
deferredPrompt.prompt();

deferredPrompt.userChoice.then(() => {
setDeferredPrompt(null);
});
}
};

const handleCancelClick = () => {
setDeferredPrompt(null);
};

return (
<Fragment>
Copy link
Member

Choose a reason for hiding this comment

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

👍👍👍

{deferredPrompt && (
<MobileInstallPrompt
handleInstallClick={handleInstallClick}
handleCancelClick={handleCancelClick}
platform={isDeviceIOS ? 'ios' : 'android'}
/>
)}
</Fragment>
);
}
2 changes: 2 additions & 0 deletions frontend/src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import AppInstallPrompt from '@components/common/AppInstallPrompt';
import Layout from '@components/common/Layout';
import PostListPage from '@components/post/PostListPage';

export default function Home() {
return (
<Layout isSidebarVisible={true}>
<PostListPage />
<AppInstallPrompt />
</Layout>
);
}
9 changes: 8 additions & 1 deletion frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
},
"outDir": "./dist"
},
"include": ["src", "src/custom.d.ts", "__test__", "styled-components.d.ts", "env.d.ts"],
"include": [
"src",
"src/custom.d.ts",
"__test__",
"styled-components.d.ts",
"env.d.ts",
"window.d.ts"
],
"exclude": ["node_modules"]
}
Loading