diff --git a/src/backend/views/frontend.pug b/src/backend/views/frontend.pug index d5d80b2..a63502a 100644 --- a/src/backend/views/frontend.pug +++ b/src/backend/views/frontend.pug @@ -4,7 +4,7 @@ html meta(charset="UTF-8") meta(name="viewport", content="width=device-width, initial-scale=1.0") block meta - - const title = 'みす廃アラート' + - const title = 'Misskey Tools' - const desc = '✨Misskey での1日のノート数、フォロー数、フォロワー数をカウントし、深夜0時にお知らせする便利サービスです。'; title= title meta(name='description' content=desc) diff --git a/src/frontend/App.tsx b/src/frontend/App.tsx index 2e96e98..7c9c4b0 100644 --- a/src/frontend/App.tsx +++ b/src/frontend/App.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React from 'react'; import { BrowserRouter, Link, Route, Switch, useLocation } from 'react-router-dom'; import { Provider } from 'react-redux'; import { useTranslation } from 'react-i18next'; @@ -7,45 +7,17 @@ import { IndexPage } from './pages'; import { RankingPage } from './pages/ranking'; import { Header } from './components/Header'; import { TermPage } from './pages/term'; -import { store, useSelector } from './store'; +import { store } from './store'; import { ModalComponent } from './Modal'; +import { useTheme } from './misc/theme'; import 'xeltica-ui/dist/css/xeltica-ui.min.css'; import './style.scss'; -import { ActualTheme } from './misc/theme'; const AppInner : React.VFC = () => { const $location = useLocation(); - const theme = useSelector(state => state.screen.theme); - - const [ osTheme, setOsTheme ] = useState('dark'); - - const applyTheme = useCallback(() => { - const actualTheme = theme === 'system' ? osTheme : theme; - if (actualTheme === 'dark') { - document.body.classList.add('dark'); - } else { - document.body.classList.remove('dark'); - } - }, [theme, osTheme]); - - // テーマ変更に追従する - useEffect(() => { - applyTheme(); - }, [theme, osTheme]); - - // システムテーマ変更に追従する - useEffect(() => { - const q = window.matchMedia('(prefers-color-scheme: dark)'); - setOsTheme(q.matches ? 'dark' : 'light'); - - const listener = () => setOsTheme(q.matches ? 'dark' : 'light'); - q.addEventListener('change', listener); - return () => { - q.removeEventListener('change', listener); - }; - }, [osTheme, setOsTheme]); + useTheme(); const {t} = useTranslation(); diff --git a/src/frontend/components/Header.tsx b/src/frontend/components/Header.tsx index 618b7bb..8c7fcd7 100644 --- a/src/frontend/components/Header.tsx +++ b/src/frontend/components/Header.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { welcomeMessage } from '../misc/welcome-message'; import { useTranslation } from 'react-i18next'; diff --git a/src/frontend/components/RankingPage.tsx b/src/frontend/components/RankingPage.tsx new file mode 100644 index 0000000..e9a9e63 --- /dev/null +++ b/src/frontend/components/RankingPage.tsx @@ -0,0 +1,14 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Ranking } from './Ranking'; + +export const RankingPage: React.VFC = () => { + const [limit, setLimit] = useState(10); + const {t} = useTranslation(); + return ( +
+ + {limit && } +
+ ); +}; diff --git a/src/frontend/components/SettingPage.tsx b/src/frontend/components/SettingPage.tsx index 83d5c9c..9332fdf 100644 --- a/src/frontend/components/SettingPage.tsx +++ b/src/frontend/components/SettingPage.tsx @@ -50,6 +50,7 @@ export const SettingPage: React.VFC = () => { ]; const updateSetting = useCallback((obj: SettingDraftType) => { + const previousDraft = draft; dispatchDraft(obj); return fetch(`${API_ENDPOINT}session`, { method: 'PUT', @@ -58,8 +59,16 @@ export const SettingPage: React.VFC = () => { 'Content-Type': 'application/json', }, body: JSON.stringify(obj), - }); - }, []); + }) + .catch(e => { + dispatch(showModal({ + type: 'dialog', + icon: 'error', + message: 'エラー' + })); + dispatchDraft(previousDraft); + }); + }, [draft]); const updateSettingWithDialog = useCallback((obj: SettingDraftType) => { updateSetting(obj) @@ -67,10 +76,7 @@ export const SettingPage: React.VFC = () => { type: 'dialog', icon: 'info', message: '保存しました。' - }))) - .catch(e => { - alert(e.message); - }); + }))); }, [updateSetting]); useEffect(() => { diff --git a/src/frontend/misc/theme.ts b/src/frontend/misc/theme.ts index b05514f..fc6fbc8 100644 --- a/src/frontend/misc/theme.ts +++ b/src/frontend/misc/theme.ts @@ -1,3 +1,6 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useSelector } from '../store'; + export const actualThemes = [ 'light', 'dark', @@ -11,3 +14,35 @@ export const themes = [ export type Theme = typeof themes[number]; export type ActualTheme = typeof actualThemes[number]; + +export const useTheme = () => { + const theme = useSelector(state => state.screen.theme); + + const [ osTheme, setOsTheme ] = useState('dark'); + + const applyTheme = useCallback(() => { + const actualTheme = theme === 'system' ? osTheme : theme; + if (actualTheme === 'dark') { + document.body.classList.add('dark'); + } else { + document.body.classList.remove('dark'); + } + }, [theme, osTheme]); + + // テーマ変更に追従する + useEffect(() => { + applyTheme(); + }, [theme, osTheme]); + + // システムテーマ変更に追従する + useEffect(() => { + const q = window.matchMedia('(prefers-color-scheme: dark)'); + setOsTheme(q.matches ? 'dark' : 'light'); + + const listener = () => setOsTheme(q.matches ? 'dark' : 'light'); + q.addEventListener('change', listener); + return () => { + q.removeEventListener('change', listener); + }; + }, [osTheme, setOsTheme]); +}; diff --git a/src/frontend/pages/index.session.tsx b/src/frontend/pages/index.session.tsx index 0ff372f..3799a6c 100644 --- a/src/frontend/pages/index.session.tsx +++ b/src/frontend/pages/index.session.tsx @@ -2,10 +2,10 @@ import React, { useMemo, useState } from 'react'; import { Header } from '../components/Header'; import { SessionDataPage } from '../components/SessionDataPage'; -import { Ranking } from '../components/Ranking'; import { Tab, TabItem } from '../components/Tab'; import { SettingPage } from '../components/SettingPage'; import { useTranslation } from 'react-i18next'; +import { RankingPage } from '../components/RankingPage'; export const IndexSessionPage: React.VFC = () => { const [selectedTab, setSelectedTab] = useState(0); @@ -20,7 +20,7 @@ export const IndexSessionPage: React.VFC = () => { const component = useMemo(() => { switch (selectedTab) { case 0: return ; - case 1: return ; + case 1: return ; case 2: return ; default: return null; }