diff --git a/public/index.html b/public/index.html index cd3bf771d..1c10b58ed 100644 --- a/public/index.html +++ b/public/index.html @@ -2,7 +2,10 @@ - + diff --git a/src/App.tsx b/src/App.tsx index 50feb8e73..e1c15ad0d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect } from 'react'; import '@karrotframe/navigator/index.css'; import { Navigator, Screen } from '@karrotframe/navigator'; import { Home } from 'pages/Home'; @@ -10,68 +10,19 @@ import { KarrotClickerGame } from 'pages/KarrotClicker/Game'; import { KarrotClickerLeaderboard } from 'pages/KarrotClicker/Leaderboard'; import { Survey } from 'pages/Survey'; import { Mission } from 'pages/Mission'; -// import { LoadingScreen } from 'components/LoadingScreen'; - -import { - createFirebaseAnalytics, - loadFromEnv as loadFirebaseAnalyticsConfig, -} from 'services/analytics/firebase'; -import { AnalyticsContext, emptyAnalytics } from 'services/analytics'; -import { - KarrotMarketMiniContext, - emptyKarrotMarketMini, -} from 'services/karrotMarketMini'; -import { - createKarrotMarketMini, - loadFromEnv as loadKarrotMarketMiniConfig, -} from 'services/karrotMarket/mini'; - -import { - useAccessToken, - useSignAccessToken, - useUserData, - useUser, -} from 'hooks'; +import { useAccessToken, useSignAccessToken, useUser, useMini } from 'hooks'; import { useMinigameApi } from 'services/api/minigameApi'; - +import { useAnalytics } from 'services/analytics/firebase'; import { v4 as uuidv4 } from 'uuid'; +import { RefererEnum } from 'redux/user'; const App: React.FC = () => { - // const dispatch = useDispatch(); + const karrotMini = useMini(); const minigameApi = useMinigameApi(); - const { setRegionInfo, setTownInfo, setIsInstalled } = useUserData(); + const analytics = useAnalytics(); const { accessToken } = useAccessToken(); const { signAccessToken, removeCookie } = useSignAccessToken(); - const [analytics, setAnalytics] = useState(emptyAnalytics); - const [karrotMarketMini, setKarrotMarketMini] = useState( - emptyKarrotMarketMini - ); - - const { saveUserInfo, setMissionPreference } = useUser(); - - // Firebase Analytics가 설정되어 있으면 인스턴스를 초기화하고 교체합니다. - useEffect(() => { - try { - // check analytics - const config = loadFirebaseAnalyticsConfig(); - const analytics = createFirebaseAnalytics(config); - setAnalytics(analytics); - } catch (error) { - console.error(error); - } - }, []); - // Mini... - useEffect(() => { - try { - // check karrot-mini - const karrotMarketMiniConfig = loadKarrotMarketMiniConfig(); - const karrotMarketMini = createKarrotMarketMini(karrotMarketMiniConfig); - setKarrotMarketMini(karrotMarketMini); - } catch (error) { - console.error(error); - // no-op - } - }, []); + const { setUser, setTown, setMission, setSubscription } = useUser(); const getQueryParams = () => { const searchParams = new URLSearchParams(window.location.search); @@ -90,14 +41,19 @@ const App: React.FC = () => { data: { data }, } = await minigameApi.regionApi.getTownInfoUsingGET(regionId); if (data) { - setTownInfo(data.townId, data.name1, data.name2, data.name3); + setTown({ + id: data.townId, + name1: data.name1, + name2: data.name2, + name3: data.name3, + }); } } catch (error) { console.error(error); } }, - [minigameApi.regionApi, setTownInfo] + [minigameApi.regionApi, setTown] ); const isSubscribed = (installed: string | null) => { @@ -122,88 +78,86 @@ const App: React.FC = () => { } }; + const saveQueryString = ({ + uuid, + regionId, + isSubscribed, + referer, + }: { + uuid: string; + regionId: string; + isSubscribed: boolean; + referer: RefererEnum; + }) => { + setUser({ uuid, regionId, referer }); + setSubscription({ isSubscribed }); + }; const checkLocalStorage = () => { const missionPreference = localStorage.getItem('missionPreference'); if (missionPreference !== null) { const parsedMissionPreference = JSON.parse(missionPreference); - setMissionPreference({ - isMissionCheckedOut: parsedMissionPreference.isMissionCheckedOut, - hasMissionPopupSeen: parsedMissionPreference.hasMissionPopupSeen, + setMission({ + page: { isCheckedOut: parsedMissionPreference.isMissionCheckedOut }, + popup: { hasSeen: parsedMissionPreference.hasMissionPopupSeen }, }); } }; useEffect(() => { - retrieveUUID(); - if (accessToken) { - removeCookie('accessToken'); - } const [preload, code, regionId, installed, referer] = getQueryParams(); // if (code)... returning user handler // else... new user handler - analytics.logEvent('launch_app'); + if (preload === null) { + retrieveUUID(); + if (accessToken) { + removeCookie('accessToken'); + } + analytics.logEvent('launch_app'); + + setUser({ regionId: regionId as string }); + getDistrictInfo(regionId as string); + console.log(preload, code, regionId, installed, referer); - setRegionInfo(regionId as string); - getDistrictInfo(regionId as string); - setIsInstalled(isSubscribed(installed)); - console.log(preload, code, regionId, installed, referer); + saveQueryString({ + uuid: localStorage.getItem('uuid') as string, + regionId: regionId as string, + isSubscribed: isSubscribed(installed), + referer: referer?.toUpperCase() as RefererEnum, + }); + checkLocalStorage(); + fetchData( + localStorage.getItem('uuid') as string, + code as string, + regionId as string + ); + } - saveUserInfo({ - uuid: localStorage.getItem('uuid'), - regionId: regionId as string, - isSubscribed: isSubscribed(installed), - referer: referer?.toUpperCase() as - | 'FEED' - | 'SUBSCRIBE_FEED_1' - | 'SUBSCRIBE_FEED_2' - | 'SUBSCRIBE_FEED_3' - | 'NEAR_BY' - | 'SHARE_GAME_2048' - | 'SHARE_GAME_KARROT' - | 'SHARE_PLATFORM' - | 'SHARE_COMMUNITY' - | 'LOGIN' - | 'UNKNOWN', - }); - checkLocalStorage(); - fetchData( - localStorage.getItem('uuid') as string, - code as string, - regionId as string - ); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( - - - { - karrotMarketMini.close(); - }} - > - - {/* Game 2048 */} - - - - {/* Karrot Clicker */} - - - - - - - - + { + karrotMini.ejectApp(); + }} + > + + {/* Game 2048 */} + + + + {/* Karrot Clicker */} + + + + + + ); }; diff --git a/src/assets/Icon/BackIcon.tsx b/src/assets/Icon/BackIcon.tsx index 2750aa545..0f0abaaf2 100644 --- a/src/assets/Icon/BackIcon.tsx +++ b/src/assets/Icon/BackIcon.tsx @@ -43,9 +43,9 @@ export const CircleBackIcon: React.FC = () => { width="48" height="48" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > - + { - const karrotMarketMini = useKarrotMarketMini(); - const handleAppEjection = () => { - karrotMarketMini.close(); - }; - return ( - - ); -}; - -export const BackButton = () => { - const { pop } = useNavigator(); - const goBackOnePage = () => { - pop(); - }; - return ( - - ); -}; diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts index 62c111a69..5ca96532c 100644 --- a/src/components/Button/index.ts +++ b/src/components/Button/index.ts @@ -1,3 +1,2 @@ export * from './Button'; -export * from './NavigationButton'; export * from './RefreshButton'; diff --git a/src/components/Navigation/TopNav.tsx b/src/components/Navigation/TopNav.tsx deleted file mode 100644 index 7a202d1ba..000000000 --- a/src/components/Navigation/TopNav.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/** @jsxImportSource @emotion/react */ -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; -import { BackIcon } from 'assets/Icon'; -import { AppEjectionButton } from 'components/Button'; -const Nav = styled.div` - left: 0; - width: 100%; - top: 0; - display: flex; - flex-flow: row; - align-items: center; - justify-content: space-between; - width: 100%; - height: 80px; - padding: 0 30px; - background: transparent; -`; - -const customNavIcon = css` - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - opacity: 1; - transition: opacity 300ms; - width: 2.25rem; - height: 2.75rem; - text-decoration: none; - outline: none; - z-index: 10; -`; - -interface TopNavprops { - action: string; - handleNavBackAction?: () => void; -} -const TopNav = ({ action, handleNavBackAction }: TopNavprops) => { - return ( - - ); -}; - -export default TopNav; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 74b411643..2ac6eec5c 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,4 +1,3 @@ -export * from './useUserData'; export * from './useMini'; export * from './useAccessToken'; export * from './useUser'; diff --git a/src/hooks/useMini.ts b/src/hooks/useMini.ts index a349bdae9..62824c5f2 100644 --- a/src/hooks/useMini.ts +++ b/src/hooks/useMini.ts @@ -1,4 +1,4 @@ -import { useSignAccessToken, useUserData, useUser } from 'hooks'; +import { useSignAccessToken, useUser } from 'hooks'; import { useCallback } from 'react'; import { useMinigameApi } from 'services/api/minigameApi'; import { @@ -8,24 +8,20 @@ import { export const useMini = () => { const mini = getMini(); - // const analytics = useAnalytics(); const minigameApi = useMinigameApi(); - const { regionId, setUserInfo } = useUserData(); const { signAccessToken } = useSignAccessToken(); + const { user, setUser } = useUser(); + const appId = karrotMarketMiniConfig().appId; const presetUrl = karrotMarketMiniConfig().presetUrl; const installationUrl = karrotMarketMiniConfig().installationUrl; - const { uuid } = useUser(); - const updateUserInfo = async () => { const { data: { data }, } = await minigameApi.userApi.getUserInfoUsingGET(); if (data) { - setUserInfo(data.id, data.nickname); - // FA: track user with set user id - // analytics.setUserId(data.id); + setUser({ userId: data.id, nickname: data.nickname }); } }; @@ -51,7 +47,11 @@ export const useMini = () => { onSuccess: async function (result) { if (result && result.code) { // console.log('1', result.code); - const response = await signAccessToken(uuid, result.code, regionId); + const response = await signAccessToken( + user.uuid as string, + result.code, + user.regionId as string + ); // console.log('2', response); if (response === true) { // console.log('3'); diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index 294c2e329..65e9743d8 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -1,120 +1,80 @@ import { useCallback } from 'react'; -import { useDispatch, useSelector, shallowEqual } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { RootState } from 'store'; import { - saveUserInfo as saveUserInfoAction, - trackVisitor as trackVisitorAction, - setMissionPreference as setMissionPreferenceAction, - setNotificationPreference as setNotificationPreferenceAction, + setUser as setUserAction, + setTown as setTownAction, + setMission as setMissionAction, + setSubscription as setSubscriptionAction, + setNewGame as setNewGameAction, } from '../redux/user/user'; +import type { User, Town, Mission, Subscription, NewGame } from '../redux/user'; export const useUser = () => { - const { uuid, regionId, isSubscribed, referer } = useSelector( - (state: RootState) => ({ - uuid: state.user.uuid, - regionId: state.user.regionId, - isSubscribed: state.user.isSubscribed, - referer: state.user.referer, - }), - shallowEqual + // state + const user = useSelector((state: RootState) => state.user.user); + const town = useSelector((state: RootState) => state.user.town); + const subscription = useSelector( + (state: RootState) => state.user.subscription ); + const mission = useSelector((state: RootState) => state.user.mission); + const newGame = useSelector((state: RootState) => state.user.newGame); - const { isMissionCheckedOut, hasMissionPopupSeen } = useSelector( - (state: RootState) => ({ - isMissionCheckedOut: state.user.isMissionCheckedOut, - hasMissionPopupSeen: state.user.hasMissionPopupSeen, - }), - shallowEqual - ); - - const { notification } = useSelector((state: RootState) => ({ - notification: { - newGame: { - isNotificationOn: state.user.notification.newGame.isNotificationOn, - }, - nextMission: { - isNotificationOn: state.user.notification.nextMission.isNotificationOn, - }, - }, - })); - + // dispatch const dispatch = useDispatch(); - const saveUserInfo = useCallback( - ({ - uuid, - regionId, - isSubscribed, - referer, - }: { - uuid?: string | null; - regionId?: string; - isSubscribed?: boolean; - referer?: - | 'FEED' - | 'SUBSCRIBE_FEED_1' - | 'SUBSCRIBE_FEED_2' - | 'SUBSCRIBE_FEED_3' - | 'NEAR_BY' - | 'SHARE_GAME_2048' - | 'SHARE_GAME_KARROT' - | 'SHARE_PLATFORM' - | 'SHARE_COMMUNITY' - | 'LOGIN' - | 'UNKNOWN'; - }) => { - dispatch(saveUserInfoAction({ uuid, regionId, isSubscribed, referer })); + const setUser = useCallback( + ({ uuid, userId, regionId, referer, nickname, referralCode }: User) => { + dispatch( + setUserAction({ + uuid, + userId, + regionId, + referer, + nickname, + referralCode, + }) + ); }, [dispatch] ); - const trackVisitor = () => { - dispatch(trackVisitorAction()); - }; + const setTown = useCallback( + ({ id, name1, name2, name3 }: Town) => { + dispatch(setTownAction({ id, name1, name2, name3 })); + }, + [dispatch] + ); - const setMissionPreference = useCallback( - ({ - isMissionCheckedOut, - hasMissionPopupSeen, - }: { - isMissionCheckedOut?: boolean; - hasMissionPopupSeen?: boolean; - }) => { - dispatch( - setMissionPreferenceAction({ isMissionCheckedOut, hasMissionPopupSeen }) - ); + const setMission = useCallback( + ({ notification, page, popup }: Mission) => { + dispatch(setMissionAction({ notification, page, popup })); + }, + [dispatch] + ); + const setSubscription = useCallback( + ({ isSubscribed }: Subscription) => { + dispatch(setSubscriptionAction({ isSubscribed })); }, [dispatch] ); - const setNotificationPreference = useCallback( - ({ - isNewGameNotificationOn, - isNextMissionNotificationOn, - }: { - isNewGameNotificationOn?: boolean; - isNextMissionNotificationOn?: boolean; - }) => { - dispatch( - setNotificationPreferenceAction({ - isNewGameNotificationOn, - isNextMissionNotificationOn, - }) - ); + const setNewGame = useCallback( + ({ notification }: NewGame) => { + dispatch(setNewGameAction({ notification })); }, [dispatch] ); return { - uuid, - regionId, - isSubscribed, - referer, - isMissionCheckedOut, - hasMissionPopupSeen, - notification, - saveUserInfo, - trackVisitor, - setMissionPreference, - setNotificationPreference, + user, + town, + subscription, + mission, + newGame, + setUser, + setTown, + setMission, + setSubscription, + setNewGame, }; }; diff --git a/src/hooks/useUserData.ts b/src/hooks/useUserData.ts deleted file mode 100644 index 18a67809e..000000000 --- a/src/hooks/useUserData.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { RootState } from 'store'; -import { - setUserInfoAction, - setRegionInfoAction, - setTownInfoAction, - setIsInstalledAction, -} from 'reducers/userDataReducer'; - -export const useUserData = () => { - const { - userId, - nickname, - regionId, - townId, - townName1, - townName2, - townName3, - isInstalled, - } = useSelector((state: RootState) => ({ - userId: state.userDataReducer.userId, - nickname: state.userDataReducer.nickname, - regionId: state.userDataReducer.regionId, - townId: state.userDataReducer.townId, - townName1: state.userDataReducer.townName1, - townName2: state.userDataReducer.townName2, - townName3: state.userDataReducer.townName3, - isInstalled: state.userDataReducer.isInstalled, - })); - const dispatch = useDispatch(); - - const setRegionInfo = useCallback( - (regionId: string) => { - dispatch(setRegionInfoAction(regionId)); - }, - [dispatch] - ); - - const setTownInfo = useCallback( - ( - townId: string, - townName1: string, - townName2: string, - townName3: string - ) => { - dispatch(setTownInfoAction(townId, townName1, townName2, townName3)); - }, - [dispatch] - ); - - const setUserInfo = useCallback( - (userId: string, nickname: string) => { - dispatch(setUserInfoAction(userId, nickname)); - }, - [dispatch] - ); - - const setIsInstalled = useCallback( - (isInstalled: boolean) => { - dispatch(setIsInstalledAction(isInstalled)); - }, - [dispatch] - ); - - return { - userId, - nickname, - regionId, - townId, - townName1, - townName2, - townName3, - isInstalled, - - setRegionInfo, - setTownInfo, - setUserInfo, - setIsInstalled, - }; -}; diff --git a/src/index.tsx b/src/index.tsx index cbb46f60d..b13a7bdc8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { CookiesProvider } from 'react-cookie'; import ReactDOM from 'react-dom'; +import { CookiesProvider } from 'react-cookie'; import { Provider } from 'react-redux'; -import { MinigameApiProvider } from 'services/api/minigameApi'; import store from 'store'; +import { MinigameApiProvider } from 'services/api/minigameApi'; +import { AnalyticsProvider } from 'services/analytics/firebase'; import App from './App'; import './index.css'; @@ -12,7 +13,9 @@ ReactDOM.render( - + + + diff --git a/src/pages/Game2048/Game/Game.tsx b/src/pages/Game2048/Game/Game.tsx index 223ba2654..f1d22c686 100644 --- a/src/pages/Game2048/Game/Game.tsx +++ b/src/pages/Game2048/Game/Game.tsx @@ -15,7 +15,7 @@ import { } from './Score'; import refreshGameUrl from 'assets/svg/game2048/refresh_game.svg'; import { useAnalytics } from 'services/analytics'; -import { useMini, useUserData } from 'hooks'; +import { useMini, useUser } from 'hooks'; import { useDebouncedCallback } from 'use-debounce'; import ReactModal from 'react-modal'; @@ -24,7 +24,7 @@ export const Game: React.FC = () => { const { isTop } = useCurrentScreen(); const minigameApi = useMinigameApi(); const { isInWebEnvironment } = useMini(); - const { userId, setUserInfo } = useUserData(); + const { user, setUser } = useUser(); const { score: myBestScore, rank: myCurrentRank, @@ -49,30 +49,31 @@ export const Game: React.FC = () => { const [isGameOver, setIsGameOver] = useState(gameOverStatus); // update user-info - const updateUserInfo = useCallback(async () => { - if (userId) { - return; - } else { - try { - const { - data: { data }, - } = await minigameApi.userApi.getUserInfoUsingGET(); - if (data) { - setUserInfo(data.id, data.nickname); - // FA: track user with set user id - // analytics.setUserId(data.id); + const updateUserInfo = useCallback( + async ({ userId }: { userId: string }) => { + if (userId) { + return; + } else { + try { + const { + data: { data }, + } = await minigameApi.userApi.getUserInfoUsingGET(); + if (data) { + setUser({ userId: data.id, nickname: data.nickname }); + } + } catch (error) { + console.error(error); } - } catch (error) { - console.error(error); } - } - }, [minigameApi.userApi, setUserInfo, userId]); + }, + [minigameApi.userApi, setUser] + ); useEffect(() => { - if (userId === '') { - updateUserInfo(); + if (user.userId === '') { + updateUserInfo({ userId: user.userId }); } - }, [updateUserInfo, userId]); + }, [updateUserInfo, user.userId]); // FA view_game_page useEffect(() => { diff --git a/src/pages/Game2048/Game/Modal/CommentModal.tsx b/src/pages/Game2048/Game/Modal/CommentModal.tsx index 1e5b30faf..d20771689 100644 --- a/src/pages/Game2048/Game/Modal/CommentModal.tsx +++ b/src/pages/Game2048/Game/Modal/CommentModal.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { useEffect, useState } from 'react'; import { Button, DisabledButton } from 'components/Button'; import { useCurrentScreen, useNavigator } from '@karrotframe/navigator'; -import { useUserData } from 'hooks'; +import { useUser } from 'hooks'; import { useMyGame2048Data } from 'pages/Game2048/hooks'; import { rem } from 'polished'; import { ReactComponent as Wow } from 'assets/svg/game2048/wow.svg'; @@ -21,7 +21,7 @@ export const CommentModal: React.FC = (props) => { const { isTop } = useCurrentScreen(); const { replace } = useNavigator(); const minigameApi = useMinigameApi(); - const { townName2: districtName } = useUserData(); + const { town } = useUser(); const { isInWebEnvironment } = useMini(); const { comment: prevComment, updateMyComment } = useMyGame2048Data(); const [comment, setComment] = useState(''); @@ -97,7 +97,7 @@ export const CommentModal: React.FC = (props) => { maxLength={20} placeholder={ prevComment === '' || prevComment === null - ? `예) 오예~${districtName}짱! :)` + ? `예) 오예~${town.name2}짱! :)` : `${prevComment}` } onChange={(e) => handleCommentInput(e)} diff --git a/src/pages/Game2048/Game/Modal/GameOverModal.tsx b/src/pages/Game2048/Game/Modal/GameOverModal.tsx index 8f1b03327..6d51cc20d 100644 --- a/src/pages/Game2048/Game/Modal/GameOverModal.tsx +++ b/src/pages/Game2048/Game/Modal/GameOverModal.tsx @@ -6,7 +6,7 @@ import gameOverSvgUrl from 'assets/svg/game2048/gameover.svg'; import { Button } from 'components/Button'; import { useMinigameApi } from 'services/api/minigameApi'; import { useMyGame2048Data } from 'pages/Game2048/hooks'; -import { useMini, useUserData } from 'hooks'; +import { useMini, useUser } from 'hooks'; import { rem } from 'polished'; import { useAnalytics } from 'services/analytics'; import { AnimatePresence, motion } from 'framer-motion'; @@ -29,7 +29,7 @@ export const GameOverModal: React.FC = (props) => { const analytics = useAnalytics(); const minigameApi = useMinigameApi(); const { isInWebEnvironment, shareApp } = useMini(); - const { nickname } = useUserData(); + const { user } = useUser(); const { gameType } = useMyGame2048Data(); // const [shouldModalOpen, setShouldModalOpen] = useState(false); const [isCommentModalOpen, setIsCommentModalOpen] = useState(false); @@ -118,7 +118,7 @@ export const GameOverModal: React.FC = (props) => { location: 'game_over_modal', }); const url = 'https://daangn.onelink.me/HhUa/37719e67'; - const text = `${nickname}님은 2048 퍼즐에서 전국 ${myCurrentRank.rank}등!`; + const text = `${user.nickname}님은 2048 퍼즐에서 전국 ${myCurrentRank.rank}등!`; shareApp(url, text); }; diff --git a/src/pages/Game2048/Leaderboard/Leaderboard.tsx b/src/pages/Game2048/Leaderboard/Leaderboard.tsx index c02f37bab..3d33be5db 100644 --- a/src/pages/Game2048/Leaderboard/Leaderboard.tsx +++ b/src/pages/Game2048/Leaderboard/Leaderboard.tsx @@ -9,7 +9,7 @@ import { CloseIcon } from 'assets/Icon'; import { MyInfo } from './MyInfo'; import { useMinigameApi } from 'services/api/minigameApi'; import { useMyGame2048Data } from '../hooks'; -import { useMini, useUserData } from 'hooks'; +import { useMini, useUser } from 'hooks'; import { Refresh } from './Refresh'; import { useThrottledCallback } from 'use-debounce/lib'; import { useAnalytics } from 'services/analytics'; @@ -26,8 +26,7 @@ export const Leaderboard = () => { const minigameApi = useMinigameApi(); const analytics = useAnalytics(); const { shareApp, handleSubscribe } = useMini(); - const { nickname, isInstalled, setIsInstalled } = useUserData(); - + const { user, subscription, setSubscription } = useUser(); const { rank, gameType, @@ -154,7 +153,7 @@ export const Leaderboard = () => { location: 'leaderboard_page', }); const url = 'https://daangn.onelink.me/HhUa/37719e67'; - const text = `${nickname}님은 2048 퍼즐에서 전국 ${rank}등!`; + const text = `${user.nickname}님은 2048 퍼즐에서 전국 ${rank}등!`; shareApp(url, text); }; @@ -175,18 +174,19 @@ export const Leaderboard = () => { location: 'leaderboard_page', is_voluntary: false, }); - setIsInstalled(true); + setSubscription({ isSubscribed: true }); subscribeToastEmitter(); - }, [analytics, setIsInstalled]); + }, [analytics, setSubscription]); const turnOffSubscribeNotification = useCallback(async () => { await minigameApi.notificationApi.saveNotificationUsingPOST({ type: 'SUBSCRIBE_OFF' as NotificationRequestDtoTypeEnum, }); }, [minigameApi.notificationApi]); + useEffect(() => { const showSubscribe = async () => { - if (isInstalled === false) { + if (subscription.isSubscribed === false) { const response = await isSubscribeNotificationOff(); if (response !== undefined && response === false) { analytics.logEvent('show_subscribe_button', { @@ -202,30 +202,6 @@ export const Leaderboard = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // const turnOffSubscribeSuggestion = () => { - // localStorage.setItem( - // 'subscribePreference', - // JSON.stringify({ - // hasSuggestionSeen: true, - // }) - // ); - // }; - // const showSubscribe = useCallback(() => { - // const subscribePreference = localStorage.getItem('subscribePreference'); - // const parsedSubscribePreference = JSON.parse(subscribePreference!); - // if (isInstalled === false && parsedSubscribePreference.hasSuggestionSeen) { - // analytics.logEvent('show_subscribe_button', { - // game_type: '2048_puzzle', - // location: 'leaderboard_page', - // is_voluntary: false, - // }); - // handleSubscribe(onSubscribeSuccess, turnOffSubscribeSuggestion); - // } - // }, [analytics, handleSubscribe, isInstalled, onSubscribeSuccess]); - - // useEffect(() => { - // showSubscribe(); - // }, [showSubscribe]); return (
= (props) => { - const { townName2: myTown } = useUserData(); + const { town } = useUser(); return ( = (props) => { }} > - {props.districtLeaderboardData.slice(0, 10).map((district) => { - return myTown === district.name2 ? ( + {props.districtLeaderboardData.slice(0, 10).map((district, i) => { + return town.name2 === district.name2 ? ( = (props) => { /> ) : ( = (props) => { /> ); })} - {props.districtLeaderboardData.slice(10).map((district) => { - return myTown === district.name2 ? ( + {props.districtLeaderboardData.slice(10).map((district, i) => { + return town.name2 === district.name2 ? ( = (props) => { /> ) : ( = (props) => { }} > - {props.userLeaderboardData.slice(0, 10).map((user) => { + {props.userLeaderboardData.slice(0, 10).map((user, i) => { return ( = (props) => { 이웃들에게 한 마디를 남겨보세요!

- {props.userLeaderboardData.slice(10).map((user) => { + {props.userLeaderboardData.slice(10).map((user, i) => { return ( { - const { - nickname, - townName1: cityName, - townName2: districtName, - } = useUserData(); + const { user, town } = useUser(); const { score, rank, highestScore, highestRank } = useMyGame2048Data(); return ( @@ -19,10 +15,13 @@ export const MyInfo: React.FC = () => { {rank} - {nickname} + {user.nickname} - {cityName.replace(/(특별시|광역시|특별자치시|특별자치도)$/, '')} -  {districtName} + {town.name1!.replace( + /(특별시|광역시|특별자치시|특별자치도)$/, + '' + )} +  {town.name2} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 6aec5c4ec..df28b4e15 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from '@emotion/styled'; import { useCurrentScreen, useNavigator } from '@karrotframe/navigator'; import { useMinigameApi } from 'services/api/minigameApi'; -import { useAccessToken, useMini, useUserData, useUser } from 'hooks'; +import { useAccessToken, useMini, useUser } from 'hooks'; import { Nav } from 'components/Navigation/Nav'; import { CloseIcon } from 'assets/Icon'; import { rem } from 'polished'; @@ -18,16 +18,11 @@ import WhatsNextCardImgUrl from 'assets/svg/whats_next_card_img.svg'; import ComingSoonCardImgUrl from 'assets/svg/coming_soon_card_img.svg'; import ArrowGame2048Url from 'assets/svg/arrow_game_2048.svg'; import ArrowKarrotClickerUrl from 'assets/svg/arrow_karrot_clicker.svg'; -import { ReactComponent as Bookmark } from 'assets/svg/bookmark_icon.svg'; -import { ReactComponent as BookmarkDone } from 'assets/svg/bookmark_done_icon.svg'; import { ReactComponent as Share } from 'assets/svg/share_icon.svg'; import { ReactComponent as Circle2048Puzzle } from 'assets/svg/platform/comment_icon_2048_puzzle.svg'; import { ReactComponent as CircleKarrotClicker } from 'assets/svg/platform/comment_icon_karrot_clicker.svg'; import { NotificationRequestDtoTypeEnum } from 'services/openapi_generator'; -import { - SubscribeToastContainer, - subscribeToastEmitter, -} from 'components/Toast'; +import { SubscribeToastContainer } from 'components/Toast'; import { Swiper, SwiperSlide } from 'swiper/react/swiper-react.js'; import 'swiper/swiper.scss'; import { Autoplay } from 'swiper'; @@ -37,28 +32,15 @@ import missionEnvelopeClosed from 'assets/svg/mission/mission_envelope_closed.sv import missionEnvelopeClosed1 from 'assets/svg/mission/mission_envelope_closed_1.svg'; import ReactModal from 'react-modal'; import { Popup as MissionPopup } from './Mission'; - +import { RefererEnum } from 'redux/user'; export const Home: React.FC = () => { const analytics = useAnalytics(); const minigameApi = useMinigameApi(); const { isTop } = useCurrentScreen(); const { push } = useNavigator(); - const { - isInWebEnvironment, - ejectApp, - handleThirdPartyAgreement, - handleSubscribe, - shareApp, - } = useMini(); + const { isInWebEnvironment, ejectApp, handleThirdPartyAgreement, shareApp } = + useMini(); const { accessToken } = useAccessToken(); - const { - userId, - nickname, - setUserInfo, - townName3, - isInstalled, - setIsInstalled, - } = useUserData(); const { updateMyScore: updateMyGame2048Score, updateMyComment: updateMyGame2048Comment, @@ -69,18 +51,11 @@ export const Home: React.FC = () => { updateMyComment: updateMyKarrotClickerComment, setGameTypeToKarrotClicker, } = useMyKarrotClickerData(); - const { - uuid, - regionId, - referer, - isMissionCheckedOut, - hasMissionPopupSeen, - notification, - setNotificationPreference, - } = useUser(); + const { user, town, mission, newGame, setUser, setMission, setNewGame } = + useUser(); const [shouldMissionPopupShown, setShouldMissionPopupShown] = - useState(!hasMissionPopupSeen); + useState(!mission.popup?.hasSeen); // Update user info const updateUserInfo = useCallback( @@ -93,44 +68,31 @@ export const Home: React.FC = () => { data: { data }, } = await minigameApi.userApi.getUserInfoUsingGET(); if (data) { - setUserInfo(data.id, data.nickname); - // FA: track user with set user id - // analytics.setUserId(data.id); + setUser({ userId: data.id, nickname: data.nickname }); } } catch (error) { console.error(error); } } }, - [minigameApi.userApi, setUserInfo] + [minigameApi.userApi, setUser] ); // Track user with uuid const trackUser = useCallback( async ({ - uUID, + uuid, regionId, referer, }: { - uUID: string; + uuid: string; regionId: string; - referer?: - | 'FEED' - | 'SUBSCRIBE_FEED_1' - | 'SUBSCRIBE_FEED_2' - | 'SUBSCRIBE_FEED_3' - | 'NEAR_BY' - | 'SHARE_GAME_2048' - | 'SHARE_GAME_KARROT' - | 'SHARE_PLATFORM' - | 'SHARE_COMMUNITY' - | 'LOGIN' - | 'UNKNOWN'; + referer?: RefererEnum; }) => { try { analytics.setUserId(uuid); const data = await minigameApi.visitorApi.visitUsingPOST( - uUID, + uuid, regionId, referer ); @@ -140,47 +102,16 @@ export const Home: React.FC = () => { console.error(error); } }, - [analytics, minigameApi.visitorApi, uuid] + [analytics, minigameApi.visitorApi] ); useEffect(() => { - trackUser({ uUID: uuid, regionId: regionId, referer: referer }); - }, [referer, regionId, trackUser, uuid]); - - // const checkNotificationStatus = useCallback(async () => { - // if (notification.newGame.isNotificationOn) { - // return; - // } else { - // try { - // const { - // data: { data }, - // } = await minigameApi.notificationApi.checkNotificationUsingGET( - // 'OPEN_GAME' - // ); - // if (data && data.check) { - // setNotificationPreference({ - // isNewGameNotificationOn: data.check, - // }); - // } - // } catch (error) { - // console.error(error); - // } - // } - // }, [ - // minigameApi.notificationApi, - // notification.newGame.isNotificationOn, - // setNotificationPreference, - // ]); - - // const newGameNotificationPromise = () => { - // if (notification.newGame.isNotificationOn) { - // return notification.newGame.isNotificationOn; - // } else { - // const data = - // minigameApi.notificationApi.checkNotificationUsingGET('OPEN_GAME'); - // if (data !== undefined) return data; - // } - // }; + trackUser({ + uuid: user.uuid as string, + regionId: user.regionId as string, + referer: user.referer, + }); + }, [trackUser, user.uuid, user.referer, user.regionId]); // Check user's notification status // available notifications: new-game, next-mission @@ -195,19 +126,25 @@ export const Home: React.FC = () => { ]); try { const notificationStatus = await notificationPromise; - setNotificationPreference({ - isNewGameNotificationOn: notificationStatus[0].data.data?.check, - isNextMissionNotificationOn: notificationStatus[1].data.data?.check, + setNewGame({ + notification: { + isOn: notificationStatus[0].data.data?.check as boolean, + }, + }); + setMission({ + notification: { + isOn: notificationStatus[1].data.data?.check as boolean, + }, }); } catch (error) { console.error(error); } - }, [minigameApi.notificationApi, setNotificationPreference]); + }, [minigameApi.notificationApi, setMission, setNewGame]); useEffect(() => { - updateUserInfo({ userId: userId }); + updateUserInfo({ userId: user.userId as string }); checkNotificationStatus(); - }, [checkNotificationStatus, updateUserInfo, userId]); + }, [checkNotificationStatus, updateUserInfo, user.userId]); useEffect(() => { if (isTop) { @@ -321,7 +258,7 @@ export const Home: React.FC = () => { location: 'platform_page', }); const url = 'https://daangn.onelink.me/HhUa/2da74f80'; - const text = `${nickname}님이 이웃님을 동네대회에 초대했어요! 같이 게임할래요?`; + const text = `${user.nickname}님이 이웃님을 동네대회에 초대했어요! 같이 게임할래요?`; shareApp(url, text); }; const triggerShareHandler = () => { @@ -332,31 +269,6 @@ export const Home: React.FC = () => { } }; - // Installation handler - // ================================================================= - const onSubscribeSucess = () => { - analytics.logEvent('click_subscribe_button', { - location: 'platform_page', - is_voluntary: true, - }); - setIsInstalled(true); - subscribeToastEmitter(); - }; - const runOnSuccess = () => { - analytics.logEvent('click_third_party_agreement_button', { - location: 'platform_page', - button_type: 'subscribe_button', - }); - handleSubscribe(onSubscribeSucess); - }; - const triggerInstallationHandler = () => { - if (accessToken) { - handleSubscribe(onSubscribeSucess); - } else { - handleThirdPartyAgreement(runOnSuccess); - } - }; - // New game notification handler // ================================================================= const onSuccessHandler = () => { @@ -364,9 +276,7 @@ export const Home: React.FC = () => { location: 'platform_page', button_type: 'notification_button', }); - setNotificationPreference({ - isNewGameNotificationOn: true, - }); + setNewGame({ notification: { isOn: true } }); }; const handleNewGameNotification = async () => { if (accessToken) { @@ -378,9 +288,7 @@ export const Home: React.FC = () => { analytics.logEvent('click_notification_button', { notification_type: 'new_game', }); - setNotificationPreference({ - isNewGameNotificationOn: true, - }); + setNewGame({ notification: { isOn: true } }); } } else { handleThirdPartyAgreement(onSuccessHandler); @@ -463,22 +371,8 @@ export const Home: React.FC = () => { border={`1px solid #ECECEC`} appendLeft={} onClickLeft={leaveMiniApp} - appendRight={ -
- - {isInstalled ? ( - - ) : ( - - )} -
- } + appendRight={} + onClickRight={triggerShareHandler} style={{ background: `#fff`, }} @@ -486,7 +380,7 @@ export const Home: React.FC = () => {
- {townName3} 이웃들과 + {town.name3} 이웃들과
같이 게임해요!
@@ -653,7 +547,7 @@ export const Home: React.FC = () => {
새로운 게임을 준비 중이에요 - {notification.newGame.isNotificationOn ? ( + {newGame.notification?.isOn ? ( { onClick={goToMissionPage} style={{ position: `absolute`, right: 0, bottom: 0, zIndex: 99 }} > - {isMissionCheckedOut ? ( + {mission.notification?.isOn ? ( mission-button ) : ( { const { isTop } = useCurrentScreen(); const analytics = useAnalytics(); - const { userId, setUserInfo } = useUserData(); const minigameApi = useMinigameApi(); const { score } = useMyKarrotClickerData(); + const { user, setUser } = useUser(); const { clickCount, updateAnimationPlayState, shouldPause } = useGame(); const [isPaused, setIsPaused] = useState(false); @@ -51,30 +51,31 @@ export const Game: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [score]); - const updateUserInfo = useCallback(async () => { - if (userId) { - return; - } else { - try { - const { - data: { data }, - } = await minigameApi.userApi.getUserInfoUsingGET(); - if (data) { - setUserInfo(data.id, data.nickname); - // FA: track user with set user id - // analytics.setUserId(data.id); + const updateUserInfo = useCallback( + async ({ userId }: { userId: string }) => { + if (userId) { + return; + } else { + try { + const { + data: { data }, + } = await minigameApi.userApi.getUserInfoUsingGET(); + if (data) { + setUser({ userId: data.id, nickname: data.nickname }); + } + } catch (error) { + console.error(error); } - } catch (error) { - console.error(error); } - } - }, [minigameApi.userApi, setUserInfo, userId]); + }, + [minigameApi.userApi, setUser] + ); useEffect(() => { - if (userId === '') { - updateUserInfo(); + if (user.userId === '') { + updateUserInfo({ userId: user.userId }); } - }, [updateUserInfo, userId]); + }, [updateUserInfo, user.userId]); useEffect(() => { if (isTop) { diff --git a/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx b/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx index 0b4e7b888..46edaba4e 100644 --- a/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx +++ b/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx @@ -7,7 +7,7 @@ import karrotImageUrl from 'assets/svg/KarrotClicker/small_circle_karrot.svg'; import React, { useEffect, useState } from 'react'; import { OldButton, OldDisabledButton } from 'components/Button'; import { useCurrentScreen, useNavigator } from '@karrotframe/navigator'; -import { useUserData } from 'hooks'; +import { useUser } from 'hooks'; import { useMyKarrotClickerData } from 'pages/KarrotClicker/hooks'; import { useMinigameApi } from 'services/api/minigameApi'; import { useAnalytics } from 'services/analytics'; @@ -21,6 +21,7 @@ export const CommentModal: React.FC = (props) => { const { replace } = useNavigator(); const minigameApi = useMinigameApi(); const analytics = useAnalytics(); + const { town } = useUser(); const { score, rank, @@ -33,7 +34,6 @@ export const CommentModal: React.FC = (props) => { length: prevComment.length, }); - const { townName2: districtName } = useUserData(); // Page navigation const goToLeaderboardPage = () => { replace(`/karrot-clicker/leaderboard`); @@ -84,7 +84,7 @@ export const CommentModal: React.FC = (props) => {

- {districtName} 이웃들에게 + {town.name2} 이웃들에게
하고 싶은 말을 남겨보세요

@@ -95,7 +95,7 @@ export const CommentModal: React.FC = (props) => { type="text" onChange={handleCommentInput} value={currentComment.comment} - placeholder={`예) 내가 ${districtName}짱!`} + placeholder={`예) 내가 ${town.name2}짱!`} maxLength={20} />

{currentComment.length}/20

diff --git a/src/pages/KarrotClicker/Game/NewUser/Guide.tsx b/src/pages/KarrotClicker/Game/NewUser/Guide.tsx index 0bbaba576..e60651734 100644 --- a/src/pages/KarrotClicker/Game/NewUser/Guide.tsx +++ b/src/pages/KarrotClicker/Game/NewUser/Guide.tsx @@ -3,9 +3,8 @@ import { css } from '@emotion/react'; import { useCurrentScreen } from '@karrotframe/navigator'; import { ReactComponent as PointingFinger } from 'assets/svg/KarrotClicker/pointing_finger.svg'; import { OldButton } from 'components/Button/Button'; -import { useUserData } from 'hooks'; +import { useUser } from 'hooks'; import { useEffect } from 'react'; -// import { useAnalytics } from 'services/analytics'; import { useMinigameApi } from 'services/api/minigameApi'; import { useGame } from '../hooks'; @@ -26,9 +25,8 @@ type Props = { }; export const Guide: React.FC = (props) => { const { isTop } = useCurrentScreen(); - // const analytics = useAnalytics(); const minigameApi = useMinigameApi(); - const { setUserInfo } = useUserData(); + const { setUser } = useUser(); const { updateAnimationPlayState } = useGame(); const updateUserInfo = async () => { @@ -36,9 +34,7 @@ export const Guide: React.FC = (props) => { data: { data }, } = await minigameApi.userApi.getUserInfoUsingGET(); if (data) { - setUserInfo(data.id, data.nickname); - // FA: track user with set user id - // analytics.setUserId(data.id); + setUser({ userId: data.id, nickname: data.nickname }); } }; diff --git a/src/pages/KarrotClicker/Home/Home.tsx b/src/pages/KarrotClicker/Home/Home.tsx index 0d9a224e8..65fdb00c8 100644 --- a/src/pages/KarrotClicker/Home/Home.tsx +++ b/src/pages/KarrotClicker/Home/Home.tsx @@ -4,10 +4,9 @@ import { BackIcon } from 'assets/Icon'; import { ActiveUserCount } from 'components/ActiveUserCount'; import { OldButton } from 'components/Button'; import { Nav } from 'components/Navigation/Nav'; -import { useAccessToken, useMini, useUserData } from 'hooks'; +import { useAccessToken, useMini, useUser } from 'hooks'; import { rem } from 'polished'; import { useAnalytics } from 'services/analytics'; -// import { useMinigameApi } from 'services/api/minigameApi'; import { useGame } from '../Game/hooks'; import { useMyKarrotClickerData } from '../hooks'; import { LeaderboardTabs } from '../Leaderboard/LeaderboardTabs'; @@ -55,7 +54,7 @@ export const Home = () => { const { accessToken } = useAccessToken(); const minigameApi = useMinigameApi(); const { isInWebEnvironment, handleThirdPartyAgreement } = useMini(); - const { nickname, townName2: districtName } = useUserData(); + const { user, town } = useUser(); const { gameType, rank, score, comment } = useMyKarrotClickerData(); const { resumeGame, onResetCount } = useGame(); @@ -135,11 +134,11 @@ export const Home = () => { {score === 0 ? null : ( )} diff --git a/src/pages/KarrotClicker/Leaderboard/Leaderboard.tsx b/src/pages/KarrotClicker/Leaderboard/Leaderboard.tsx index 7ac2a68ed..82f99e94f 100644 --- a/src/pages/KarrotClicker/Leaderboard/Leaderboard.tsx +++ b/src/pages/KarrotClicker/Leaderboard/Leaderboard.tsx @@ -5,7 +5,7 @@ import { useCurrentScreen, useNavigator } from '@karrotframe/navigator'; import { OldButton } from 'components/Button'; import { DefaultUserRow, TopUserRow } from './LeaderboardTabs/Row'; import { LeaderboardTabs } from './LeaderboardTabs'; -import { useMini, useUserData } from 'hooks'; +import { useMini, useUser } from 'hooks'; import { useMyKarrotClickerData } from '../hooks'; import { useMinigameApi } from 'services/api/minigameApi'; import { Nav } from 'components/Navigation/Nav'; @@ -50,7 +50,7 @@ export const Leaderboard = () => { const analytics = useAnalytics(); const minigameApi = useMinigameApi(); const karrotMarketMini = useMini(); - const { nickname, townName2: districtName } = useUserData(); + const { user, town } = useUser(); const { gameType, score, rank, comment, updateMyKarrotClickerData } = useMyKarrotClickerData(); const { onResetCount, resumeGame } = useGame(); @@ -79,7 +79,7 @@ export const Leaderboard = () => { const handleShare = () => { const url = 'https://daangn.onelink.me/HhUa/8db9923d'; - const text = `${nickname}님은 당근모아에서 전국 ${rank}등!`; + const text = `${user.nickname}님은 당근모아에서 전국 ${rank}등!`; karrotMarketMini.shareApp(url, text); analytics.logEvent('click_share_button', { game_type: 'karrot_clicker', @@ -116,7 +116,7 @@ export const Leaderboard = () => {