0
0
Fork 0
This commit is contained in:
xeltica 2021-09-24 02:18:31 +09:00
parent 230e952c84
commit d7b77ee022
12 changed files with 136 additions and 169 deletions

View file

@ -9,10 +9,11 @@ import { Header } from './components/Header';
import { TermPage } from './pages/term';
import { store, useSelector } from './store';
import { ModalComponent } from './Modal';
import { ActualTheme } from './misc/theme';
import { ErrorCode } from '../common/types/error-code';
import 'xeltica-ui/dist/css/xeltica-ui.min.css';
import './style.scss';
import { ActualTheme } from './misc/theme';
const AppInner : React.VFC = () => {
const $location = useLocation();
@ -49,22 +50,35 @@ const AppInner : React.VFC = () => {
const {t} = useTranslation();
return (
<>
<div className="container">
{$location.pathname !== '/' && <Header hasTopLink />}
<Switch>
<Route exact path="/" component={IndexPage} />
<Route exact path="/ranking" component={RankingPage} />
<Route exact path="/term" component={TermPage} />
</Switch>
<footer className="text-center pa-5">
<p>(C)2020-2021 Xeltica</p>
<p><Link to="/term">{t('termsOfService')}</Link></p>
</footer>
<ModalComponent />
const error = (window as any).__misshaialert?.error;
return error ? (
<div className="container">
<Header hasTopLink />
<div className="xarticle">
<h1>{t('error')}</h1>
<p>{t('_error.sorry')}</p>
<p>
{t('_error.additionalInfo')}
{t(`_error.${error}`)}
</p>
<Link to="/" onClick={() => (window as any).__misshaialert.error = null}>{t('retry')}</Link>
</div>
</>
</div>
) : (
<div className="container">
{$location.pathname !== '/' && <Header hasTopLink />}
<Switch>
<Route exact path="/" component={IndexPage} />
<Route exact path="/ranking" component={RankingPage} />
<Route exact path="/term" component={TermPage} />
</Switch>
<footer className="text-center pa-5">
<p>(C)2020-2021 Xeltica</p>
<p><Link to="/term">{t('termsOfService')}</Link></p>
</footer>
<ModalComponent />
</div>
);
};

View file

@ -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';

View file

@ -121,7 +121,16 @@ export const SettingPage: React.VFC = () => {
const onClickLogout = useCallback(() => {
dispatch(showModal({
type: 'dialog',
message: 'WIP',
title: 'ログアウトしてもよろしいですか?',
message: 'ログアウトしても、アラート送信や、お使いのMisskeyアカウントのデータ収集といった機能は動作し続けます。Misskey Toolsの利用を停止したい場合は、「アカウント連携を解除する」ボタンを押下して下さい。',
icon: 'question',
buttons: 'yesNo',
onSelect(i) {
if (i === 0) {
localStorage.removeItem(LOCALSTORAGE_KEY_TOKEN);
location.reload();
}
},
}));
}, [dispatch]);
@ -149,33 +158,36 @@ export const SettingPage: React.VFC = () => {
</label>
))
}
{draft.alertMode === 'notification' && (
<div className="alert bg-danger mt-2">
<i className="icon bi bi-exclamation-circle"></i>
{t('_alertMode.notificationWarning')}
</div>
)}
</div>
{ draft.alertMode === 'notification' && (
<div className="alert bg-danger mt-2">
<i className="icon bi bi-exclamation-circle"></i>
{t('_alertMode.notificationWarning')}
</div>
)}
{ draft.alertMode === 'note' && (
<div>
<label htmlFor="visibility" className="input-field">{t('visibility')}</label>
{
availableVisibilities.map((visibility) => (
<label key={visibility} className="input-check">
<input type="radio" checked={visibility === draft.visibility} onChange={() => {
updateSetting({ visibility });
}} />
<span>{t(`_visibility.${visibility}`)}</span>
</label>
))
}
<>
<h2>{t('visibility')}</h2>
<div>
{
availableVisibilities.map((visibility) => (
<label key={visibility} className="input-check">
<input type="radio" checked={visibility === draft.visibility} onChange={() => {
updateSetting({ visibility });
}} />
<span>{t(`_visibility.${visibility}`)}</span>
</label>
))
}
</div>
<label className="input-check mt-2">
<input type="checkbox" checked={draft.localOnly} onChange={(e) => {
updateSetting({ localOnly: e.target.checked });
}} />
<span>{t('localOnly')}</span>
</label>
</div>
</>
)}
</Card>
<Card bodyClassName="vstack">
@ -218,7 +230,7 @@ export const SettingPage: React.VFC = () => {
</ul>
</details>
<div className="hstack" style={{justifyContent: 'flex-end'}}>
<button className="btn danger" onClick={() => dispatchDraft({ template: null })}></button>
<button className="btn danger" onClick={() => dispatchDraft({ template: null })}>{t('resetToDefault')}</button>
<button className="btn primary" onClick={() => {
updateSettingWithDialog({ template: draft.template === '' ? null : draft.template });
}}>{t('save')}</button>

View file

@ -4,31 +4,16 @@ import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { App } from './App';
import { LOCALSTORAGE_KEY_TOKEN } from './const';
import { resources } from './langs';
import { getBrowserLanguage, resources } from './langs';
document.body.classList.add('dark');
// cookieにトークンが入ってたらlocalStorageに移し替える
const token = document.cookie
.split('; ')
.find(row => row.startsWith('token'))
?.split('=')[1];
if (token) {
localStorage[LOCALSTORAGE_KEY_TOKEN] = token;
// 今の所はcookieをトークン以外に使用しないため全消去する
// もしcookieの用途が増えるのであればここを良い感じに書き直す必要がある
document.cookie = '';
}
console.log(resources);
i18n
.use(initReactI18next)
.init({
resources,
lng: localStorage['lang'] ?? 'ja_JP',
lng: localStorage['lang'] ?? getBrowserLanguage(),
interpolation: {
escapeValue: false // react already safes from xss
}

View file

@ -29,11 +29,14 @@
failedToFetch: "Failed to fetch",
registeredUsersCount: "Users",
isCalculating: "It is being calculated now. Please check back later!",
retry: "Try again",
ok: "OK",
yes: "Yes",
no: "No",
termsOfService: "Terms of Service",
name: "Name",
resetToDefault: "Reset to default",
error: "Error",
_welcomeMessage: {
pattern1: "Overnoting Misskey?",
pattern2: "Overusing Misskey?",
@ -92,8 +95,19 @@
system: "Follows System Preferences",
},
_template: {
description: "アラートの自動投稿をカスタマイズできます。",
description2: "ハッシュタグ #misshaialert は、テンプレートに関わらず自動付与されます。",
description: "Customize template of your alert.",
description2: "Hashtag '#misshaialert' will be appended regardless of the template.",
},
_error: {
sorry: "Something went wrong. Please retry again.",
additionalInfo: "Additional Info: ",
hitorisskeyIsDenied: "You cannot integrate with hitorisskey.",
teapot: "I'm a teapot.",
sessionRequired: "Session is required.",
tokenRequired: "Token is required.",
invalidParameter: "Invalid parameter.",
notAuthorized: "Not authorized.",
other: "None",
},
},
}

View file

@ -9,4 +9,11 @@ export const resources = {
export const languageName = {
'en_US': 'English',
'ja_JP': '日本語',
} as const;
export type LanguageCode = keyof typeof resources;
export const getBrowserLanguage = () => {
const lang = navigator.language;
return (Object.keys(resources) as LanguageCode[]).find(k => k.startsWith(lang)) ?? 'en_US';
};

View file

@ -34,6 +34,9 @@
no: "いいえ",
termsOfService: "利用規約",
name: "名前",
resetToDefault: "初期値に戻す",
error: "エラー",
retry: "やり直す",
_welcomeMessage: {
'pattern1': 'ついついノートしすぎていませんか?',
'pattern2': 'Misskey, しすぎていませんか?',
@ -95,5 +98,16 @@
description: "アラートの自動投稿をカスタマイズできます。",
description2: "ハッシュタグ #misshaialert は、テンプレートに関わらず自動付与されます。",
},
_error: {
sorry: "問題が発生しました。お手数ですが、やり直してください。",
additionalInfo: "追加情報: ",
hitorisskeyIsDenied: "ひとりすきーは連携できません。",
teapot: "I'm a teapot.",
sessionRequired: "セッションがありません。",
tokenRequired: "トークンがありません。",
invalidParameter: "パラメータが不正です。",
notAuthorized: "権限がありません。",
other: "なし",
},
},
}

View file

@ -7,6 +7,7 @@ import { Tab, TabItem } from '../components/Tab';
import { SettingPage } from '../components/SettingPage';
import { useTranslation } from 'react-i18next';
export const IndexSessionPage: React.VFC = () => {
const [selectedTab, setSelectedTab] = useState<number>(0);
const {t, i18n} = useTranslation();