diff --git a/crabfit-frontend/src/App.tsx b/crabfit-frontend/src/App.tsx
index 19fe3e0..76a2e39 100644
--- a/crabfit-frontend/src/App.tsx
+++ b/crabfit-frontend/src/App.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect, Suspense, lazy } from 'react';
+import { useState, useEffect, useCallback, Suspense, lazy } from 'react';
import {
BrowserRouter,
Switch,
@@ -6,11 +6,13 @@ import {
} from 'react-router-dom';
import { ThemeProvider, Global } from '@emotion/react';
-import { Settings, Loading } from 'components';
+import { Settings, Loading, Egg } from 'components';
import { useSettingsStore } from 'stores';
import theme from 'theme';
+const EGG_PATTERN = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
+
const Home = lazy(() => import('pages/Home/Home'));
const Event = lazy(() => import('pages/Event/Event'));
@@ -20,13 +22,49 @@ const App = () => {
const [isDark, setIsDark] = useState(darkQuery.matches);
const [offline, setOffline] = useState(!window.navigator.onLine);
+ const [eggCount, setEggCount] = useState(0);
+ const [eggVisible, setEggVisible] = useState(false);
+ const [eggKey, setEggKey] = useState(0);
+
+ const eggHandler = useCallback(
+ event => {
+ if (EGG_PATTERN.indexOf(event.key) < 0 || event.key !== EGG_PATTERN[eggCount]) {
+ setEggCount(0);
+ return;
+ }
+ setEggCount(eggCount+1);
+ if (EGG_PATTERN.length === eggCount+1) {
+ setEggKey(eggKey+1);
+ setEggCount(0);
+ setEggVisible(true);
+ }
+ },
+ [eggCount, eggKey]
+ );
+
darkQuery.addListener(e => colortheme === 'System' && setIsDark(e.matches));
useEffect(() => {
- window.addEventListener('offline', () => setOffline(true));
- window.addEventListener('online', () => setOffline(false));
+ const onOffline = () => setOffline(true);
+ const onOnline = () => setOffline(false);
+
+ window.addEventListener('offline', onOffline, false);
+ window.addEventListener('online', onOnline, false);
+
+ return () => {
+ window.removeEventListener('offline', onOffline, false);
+ window.removeEventListener('online', onOnline, false);
+ };
}, []);
+ useEffect(() => {
+ document.addEventListener('keyup', eggHandler, false);
+
+ return () => {
+ document.removeEventListener('keyup', eggHandler, false);
+ };
+ }, [eggHandler]);
+
useEffect(() => {
setIsDark(colortheme === 'System' ? darkQuery.matches : colortheme === 'Dark');
}, [colortheme, darkQuery.matches]);
@@ -84,6 +122,8 @@ const App = () => {
+
+ {eggVisible && setEggVisible(false)} />}
);
diff --git a/crabfit-frontend/src/components/Egg/Egg.tsx b/crabfit-frontend/src/components/Egg/Egg.tsx
new file mode 100644
index 0000000..afa2e82
--- /dev/null
+++ b/crabfit-frontend/src/components/Egg/Egg.tsx
@@ -0,0 +1,21 @@
+import { useState } from 'react';
+
+import { Loading } from 'components';
+import { Image, Wrapper } from './eggStyle';
+
+const Egg = ({ eggKey, onClose }) => {
+ const [isLoading, setIsLoading] = useState(true);
+
+ return (
+ onClose()}>
+ setIsLoading(true)}
+ onLoad={() => setIsLoading(false)}
+ />
+ {isLoading && }
+
+ );
+}
+
+export default Egg;
diff --git a/crabfit-frontend/src/components/Egg/eggStyle.ts b/crabfit-frontend/src/components/Egg/eggStyle.ts
new file mode 100644
index 0000000..19ac1f3
--- /dev/null
+++ b/crabfit-frontend/src/components/Egg/eggStyle.ts
@@ -0,0 +1,23 @@
+import styled from '@emotion/styled';
+
+export const Wrapper = styled.div`
+ position: fixed;
+ background: rgba(0,0,0,.6);
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 100%;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+ cursor: pointer;
+`;
+
+export const Image = styled.img`
+ max-width: 80%;
+ max-height: 80%;
+ position: absolute;
+`;
diff --git a/crabfit-frontend/src/components/index.ts b/crabfit-frontend/src/components/index.ts
index dc7571c..ada1a57 100644
--- a/crabfit-frontend/src/components/index.ts
+++ b/crabfit-frontend/src/components/index.ts
@@ -14,3 +14,4 @@ export { default as Loading } from './Loading/Loading';
export { default as Center } from './Center/Center';
export { default as Donate } from './Donate/Donate';
export { default as Settings } from './Settings/Settings';
+export { default as Egg } from './Egg/Egg';