0
0
Fork 0

デザイン調整

This commit is contained in:
Xeltica 2022-01-27 21:12:58 +09:00
parent 7c833c3568
commit bc0eeb5461
12 changed files with 193 additions and 160 deletions

View file

@ -15,7 +15,7 @@ html
meta(name='twitter:site' content='@Xeltica') meta(name='twitter:site' content='@Xeltica')
meta(name='twitter:creator' content='@Xeltica') meta(name='twitter:creator' content='@Xeltica')
link(rel="stylesheet" href="https://koruri.chillout.chat/koruri.css") link(rel="stylesheet" href="https://koruri.chillout.chat/koruri.css")
link(rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css") link(rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css")
style. style.
.loading { .loading {
display: flex; display: flex;

View file

@ -59,7 +59,7 @@ const AppInner : React.VFC = () => {
</div> </div>
) : ( ) : (
<div className="container"> <div className="container">
{$location.pathname !== '/' && <Header hasTopLink />} {$location.pathname !== '/' && <Header hasTopLink className="xarticle mb-2" />}
<Switch> <Switch>
<Route exact path="/" component={IndexPage} /> <Route exact path="/" component={IndexPage} />
<Route exact path="/ranking" component={RankingPage} /> <Route exact path="/ranking" component={RankingPage} />

View file

@ -30,32 +30,34 @@ export const AccountsPage: React.VFC = () => {
<Skeleton /> <Skeleton />
</div> </div>
) : ( ) : (
<article className="fade"> <article className="card fade">
<div> <div className="body">
<strong>{t('_accounts.switchAccount')}</strong> <div>
</div> <strong>{t('_accounts.switchAccount')}</strong>
<div className="menu large fluid mb-2"> </div>
{ <div className="menu large fluid mb-2">
accounts.length === accountTokens.length ? ( {
accounts.map(account => ( accounts.length === accountTokens.length ? (
<button className="item fluid" style={{display: 'flex', flexDirection: 'row', alignItems: 'center'}} onClick={() => switchAccount(account.misshaiToken)}> accounts.map(account => (
<i className="icon bi bi-chevron-right" /> <button className="item fluid" style={{display: 'flex', flexDirection: 'row', alignItems: 'center'}} onClick={() => switchAccount(account.misshaiToken)}>
<i className="icon bi bi-chevron-right" />
@{account.username}@{account.host} @{account.username}@{account.host}
<button className="btn flat text-danger" style={{marginLeft: 'auto'}} onClick={e => { <button className="btn flat text-danger" style={{marginLeft: 'auto'}} onClick={e => {
const filteredAccounts = accounts.filter(ac => ac.id !== account.id); const filteredAccounts = accounts.filter(ac => ac.id !== account.id);
dispatch(setAccounts(filteredAccounts)); dispatch(setAccounts(filteredAccounts));
e.stopPropagation(); e.stopPropagation();
}}> }}>
<i className="bi bi-trash"/> <i className="bi bi-trash"/>
</button>
</button> </button>
</button> ))
)) ) : (
) : ( <div className="item">...</div>
<div className="item">...</div> )
) }
} </div>
<LoginForm />
</div> </div>
<LoginForm />
</article> </article>
); );
}; };

View file

@ -24,7 +24,7 @@ export const AnnouncementList: React.VFC = () => {
return ( return (
<> <>
<h2 className="mb-0">{t('announcements')}</h2> <h1 className="mb-0">{t('announcements')}</h1>
<div className="large menu fade"> <div className="large menu fade">
{announcements.map(a => ( {announcements.map(a => (
<Link className="item fluid" key={a.id} to={`/announcements/${a.id}`}> <Link className="item fluid" key={a.id} to={`/announcements/${a.id}`}>

View file

@ -0,0 +1,17 @@
.misshaiPageLayout {
display: flex;
flex-wrap: wrap;
gap: var(--margin);
> .misshaiData {
flex: 1 0 300px;
}
> .misshaiRanking {
flex: 5 0 300px;
}
> .alertModeSetting {
flex: 1 0 300px;
}
> .templateSetting {
flex: 5 0 300px;
}
}

View file

@ -10,10 +10,11 @@ import { $post, $put } from '../misc/api';
import { useGetScoreQuery, useGetSessionQuery } from '../services/session'; import { useGetScoreQuery, useGetSessionQuery } from '../services/session';
import { showModal } from '../store/slices/screen'; import { showModal } from '../store/slices/screen';
import { AnnouncementList } from './AnnouncementList'; import { AnnouncementList } from './AnnouncementList';
import { Card } from './Card';
import { Ranking } from './Ranking'; import { Ranking } from './Ranking';
import { Skeleton } from './Skeleton'; import { Skeleton } from './Skeleton';
import './MisshaiPage.scss';
const variables = [ const variables = [
'notesCount', 'notesCount',
'followingCount', 'followingCount',
@ -174,7 +175,7 @@ export const MisshaiPage: React.VFC = () => {
const defaultTemplate = t('_template.default'); const defaultTemplate = t('_template.default');
return session.isLoading || score.isLoading ? ( return session.isLoading || score.isLoading || !session.data || !score.data ? (
<div className="vstack"> <div className="vstack">
<Skeleton width="100%" height="1rem" /> <Skeleton width="100%" height="1rem" />
<Skeleton width="100%" height="1rem" /> <Skeleton width="100%" height="1rem" />
@ -182,23 +183,16 @@ export const MisshaiPage: React.VFC = () => {
<Skeleton width="100%" height="160px" /> <Skeleton width="100%" height="160px" />
</div> </div>
) : ( ) : (
<div className="fade vstack"> <div className="vstack">
{session.data && ( <section className="card announcement">
<section> <div className="body">
<p>{t('welcomeBack', {acct: `@${session.data.username}@${session.data.host}`})}</p> <AnnouncementList />
<p> </div>
<strong> </section>
{t('_missHai.rating')}{': '} <div className="misshaiPageLayout">
</strong> <section className="card misshaiData">
{session.data.rating} <div className="body">
</p> <h1>{t('_missHai.data')}</h1>
</section>
)}
<AnnouncementList />
{score.data && (
<>
<section>
<h2>{t('_missHai.data')}</h2>
<table className="table fluid"> <table className="table fluid">
<thead> <thead>
<tr> <tr>
@ -225,88 +219,104 @@ export const MisshaiPage: React.VFC = () => {
</tr> </tr>
</tbody> </tbody>
</table> </table>
</section> <p>
<section> <strong>
<h2>{t('_missHai.ranking')}</h2> {t('_missHai.rating')}{': '}
</strong>
{session.data.rating}
</p>
</div>
</section>
<section className="card misshaiRanking">
<div className="body">
<h1>{t('_missHai.ranking')}</h1>
<Ranking limit={limit} /> <Ranking limit={limit} />
{limit && <button className="btn link" onClick={() => setLimit(undefined)}>{t('_missHai.showAll')}</button>} {limit && <button className="btn link" onClick={() => setLimit(undefined)}>{t('_missHai.showAll')}</button>}
</section> </div>
<section className="vstack mt-2"> </section>
<Card bodyClassName="vstack"> </div>
<h1>{t('alertMode')}</h1> <div className="misshaiPageLayout">
<div> <section className="card alertModeSetting">
{ <div className="body">
alertModes.map((mode) => ( <h1 className="mb-2">{t('alertMode')}</h1>
<label key={mode} className="input-check"> <div className="vstack">
<input type="radio" checked={mode === draft.alertMode} onChange={() => { {
updateSetting({ alertMode: mode }); alertModes.map((mode) => (
}} /> <label key={mode} className="input-check">
<span>{t(`_alertMode.${mode}`)}</span> <input type="radio" checked={mode === draft.alertMode} onChange={() => {
</label> updateSetting({ alertMode: mode });
))
}
</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' && (
<>
<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> <span>{t(`_alertMode.${mode}`)}</span>
</label> </label>
</> ))
)} }
</Card> </div>
<Card bodyClassName="vstack"> { draft.alertMode === 'notification' && (
<h1>{t('template')}</h1> <div className="alert bg-danger mt-2">
<p>{t('_template.description')}</p> <i className="icon bi bi-exclamation-circle"></i>
<div className="hstack dense"> {t('_alertMode.notificationWarning')}
<button className="btn" onClick={onClickInsertVariables}>
<i className="bi bi-braces" />&nbsp;
{t('_template.insertVariables')}
</button>
<button className="btn link text-info" onClick={onClickInsertVariablesHelp}>
<i className="bi bi-question-circle" />
</button>
</div> </div>
<textarea ref={templateTextarea} className="input-field" value={draft.template ?? defaultTemplate} placeholder={defaultTemplate} style={{height: 228}} onChange={(e) => { )}
dispatchDraft({ template: e.target.value }); { draft.alertMode === 'note' && (
}} /> <>
<small className="text-dimmed">{t('_template.description2')}</small> <h2 className="mt-2 mb-1">{t('visibility')}</h2>
<div className="hstack" style={{justifyContent: 'flex-end'}}> <div className="vstack">
<button className="btn danger" onClick={() => dispatchDraft({ template: null })}>{t('resetToDefault')}</button> {
<button className="btn primary" onClick={() => { availableVisibilities.map((visibility) => (
updateSettingWithDialog({ template: draft.template === '' ? null : draft.template }); <label key={visibility} className="input-check">
}}>{t('save')}</button> <input type="radio" checked={visibility === draft.visibility} onChange={() => {
</div> updateSetting({ visibility });
</Card> }} />
<Card bodyClassName="vstack"> <span>{t(`_visibility.${visibility}`)}</span>
<button className="btn block" onClick={onClickSendAlert} disabled={draft.alertMode === 'nothing'}>{t('sendAlert')}</button> </label>
<p className="text-dimmed">{t(draft.alertMode === 'nothing' ? 'sendAlertDisabled' : 'sendAlertDescription')}</p> ))
</Card> }
</section> </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>
</section>
<section className="card templateSetting">
<div className="body">
<h1>{t('template')}</h1>
<p>{t('_template.description')}</p>
<div className="hstack dense mb-2">
<button className="btn" onClick={onClickInsertVariables}>
<i className="bi bi-braces" />&nbsp;
{t('_template.insertVariables')}
</button>
<button className="btn link text-info" onClick={onClickInsertVariablesHelp}>
<i className="bi bi-question-circle" />
</button>
</div>
<textarea ref={templateTextarea} className="input-field" value={draft.template ?? defaultTemplate} placeholder={defaultTemplate} style={{height: 228}} onChange={(e) => {
dispatchDraft({ template: e.target.value });
}} />
<small className="text-dimmed">{t('_template.description2')}</small>
<div className="hstack mt-2" style={{justifyContent: 'flex-end'}}>
<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>
</div>
</div>
</section>
</div>
<section className="list-form mt-2">
<button className="item" onClick={onClickSendAlert} disabled={draft.alertMode === 'nothing'}>
<i className="icon bi bi-send" />
<div className="body">
<h1>{t('sendAlert')}</h1>
<p className="desc">{t(draft.alertMode === 'nothing' ? 'sendAlertDisabled' : 'sendAlertDescription')}</p>
</div>
</button>
</section>
</div> </div>
); );
}; };

View file

@ -1,11 +1,9 @@
import React, { ChangeEventHandler, useEffect, useRef, useState } from 'react'; import React, { ChangeEventHandler, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import ReactCrop, { Crop } from 'react-image-crop'; import ReactCrop, { Crop } from 'react-image-crop';
import 'react-image-crop/dist/ReactCrop.css'; import 'react-image-crop/dist/ReactCrop.css';
export const NekomimiPage: React.VFC = () => { export const NekomimiPage: React.VFC = () => {
const {t} = useTranslation();
const [blobUrl, setBlobUrl] = useState<string | null>(null); const [blobUrl, setBlobUrl] = useState<string | null>(null);
const [image, setImage] = useState<HTMLImageElement | null>(null); const [image, setImage] = useState<HTMLImageElement | null>(null);
const [crop, setCrop] = useState<Partial<Crop>>({unit: '%', width: 100, aspect: 1 / 1}); const [crop, setCrop] = useState<Partial<Crop>>({unit: '%', width: 100, aspect: 1 / 1});

View file

@ -129,7 +129,7 @@ export const SettingPage: React.VFC = () => {
</div> </div>
</button> </button>
<button className="item text-danger" onClick={onClickDeleteAccount}> <button className="item text-danger" onClick={onClickDeleteAccount}>
<i className="icon bi bi-trash-fill" /> <i className="icon bi bi-trash" />
<div className="body"> <div className="body">
<h1>{t('deleteAccount')}</h1> <h1>{t('deleteAccount')}</h1>
<p className="desc">{t('deleteAccountDescription')}</p> <p className="desc">{t('deleteAccountDescription')}</p>

View file

@ -54,15 +54,17 @@
"title": "プロ仕様のツールキット", "title": "プロ仕様のツールキット",
"description": "Misskey Toolsでは、Misskeyのために設計された、Misskeyをより楽しめるツールを取り揃えています。", "description": "Misskey Toolsでは、Misskeyのために設計された、Misskeyをより楽しめるツールを取り揃えています。",
"misshaiAlertTitle": "ミス廃アラート", "misshaiAlertTitle": "ミス廃アラート",
"misshaiAlertDescription": "Misskeyにのめり込んでいませんかミス廃アラートを使えば、毎日のMisskeyでの活動量を以下のように定期投稿できます。", "misshaiAlertDescription": "Misskeyにのめり込んでいませんかミス廃アラートを使えば、毎日のMisskeyでの活動量を定期投稿できます。",
"misshaiRankingDescription": "ミス廃ランキングでは、Misskeyでの活動を数値化し、ランキング表示します。", "misshaiRankingDescription": "ミス廃ランキングでは、Misskeyでの活動を数値化し、ランキング表示します。",
"catAdjusterDescription": "CATモードの猫耳がアイコンに合わなくてお悩みのあなたへ。かわいいアイコンを猫耳に合わせてトリミングできます。",
"nextFeaturesTitle": "今後も追加予定。", "nextFeaturesTitle": "今後も追加予定。",
"nextFeaturesDescription": "これだけではありません。今後もアップデートで様々な機能を追加します!" "nextFeaturesDescription": "これだけではありません。今後もアップデートで様々な機能を追加します!"
}, },
"_nav": { "_nav": {
"misshai": "ミス廃", "misshai": "ミス廃",
"accounts": "アカウント", "accounts": "アカウント",
"settings": "設定" "settings": "設定",
"catAdjuster": "ねこみみ"
}, },
"_missHai": { "_missHai": {
"ranking": "ミス廃ランキング", "ranking": "ミス廃ランキング",
@ -157,5 +159,8 @@
"no": "キャンセル", "no": "キャンセル",
"success": "アカウントを解除しました。トップ画面に戻ります。", "success": "アカウントを解除しました。トップ画面に戻ります。",
"failure": "アカウントを解除できませんでした。" "failure": "アカウントを解除できませんでした。"
},
"_catAdjuster": {
"title": "ねこみみアジャスター"
} }
} }

View file

@ -14,8 +14,6 @@ import { useGetSessionQuery } from '../services/session';
import { AdminPage } from '../components/AdminPage'; import { AdminPage } from '../components/AdminPage';
import { $get } from '../misc/api'; import { $get } from '../misc/api';
import { NekomimiPage } from '../components/NekomimiPage'; import { NekomimiPage } from '../components/NekomimiPage';
import { CurrentUser } from '../components/CurrentUser';
import { Card } from '../components/Card';
export const IndexSessionPage: React.VFC = () => { export const IndexSessionPage: React.VFC = () => {
const [selectedTab, setSelectedTab] = useState<string>('misshai'); const [selectedTab, setSelectedTab] = useState<string>('misshai');
@ -59,7 +57,7 @@ export const IndexSessionPage: React.VFC = () => {
<Tab items={items} selected={selectedTab} onSelect={setSelectedTab}/> <Tab items={items} selected={selectedTab} onSelect={setSelectedTab}/>
</div> </div>
</div> </div>
<article className="xarticle mt-2"> <article className="xarticle mt-4">
{component} {component}
</article> </article>
</> </>

View file

@ -1,4 +1,4 @@
import React, { useMemo } from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -8,49 +8,42 @@ import { AnnouncementList } from '../components/AnnouncementList';
export const IndexWelcomePage: React.VFC = () => { export const IndexWelcomePage: React.VFC = () => {
const {t} = useTranslation(); const {t} = useTranslation();
const example = useMemo(() => (
t('_template.default')
.replace('{notesCount}', '32000')
.replace('{notesDelta}', '+190')
.replace('{followingCount}', '510')
.replace('{followingDelta}', '+3')
.replace('{followersCount}', '1020')
.replace('{followersDelta}', '-1')
.replace('{url}', 'https://misskey.tools')
), []);
return ( return (
<> <>
<Header> <Header className="xarticle mb-4">
<article className="mt-4"> <article className="mt-4">
<p>{t('description1')}</p> <p>{t('description1')}</p>
<p>{t('description2')}</p> <p>{t('description2')}</p>
</article> </article>
<LoginForm /> <LoginForm />
</Header> </Header>
<article className="xarticle"> <article className="xarticle card">
<AnnouncementList /> <div className="body">
<AnnouncementList />
</div>
</article> </article>
<hr />
<article className="xarticle vstack pa-2"> <article className="xarticle vstack pa-2">
<header> <header>
<h2>{t('_welcome.title')}</h2> <h2>{t('_welcome.title')}</h2>
<p>{t('_welcome.description')}</p> <p>{t('_welcome.description')}</p>
</header> </header>
<article> <div className="row">
<h3>{t('_welcome.misshaiAlertTitle')}</h3> <article className="col-4 col-12-sm">
<p>{t('_welcome.misshaiAlertDescription')}</p> <h3><i className="bi bi-megaphone-fill"/> {t('_welcome.misshaiAlertTitle')}</h3>
<div className="card ma-2 shadow-2" style={{maxWidth: 360}}> <p>{t('_welcome.misshaiAlertDescription')}</p>
<div className="body"> </article>
<pre>{example}</pre> <article className="col-4 col-12-sm">
</div> <h3><i className="bi bi-bar-chart-fill"/> {t('_missHai.ranking')}</h3>
<p>{t('_welcome.misshaiRankingDescription')}</p>
<Link to="/ranking">{t('_missHai.showRanking')}</Link>
</article>
<div className="col-4 col-12-sm">
<h3><i className="bi bi-crop"/> {t('_catAdjuster.title')}</h3>
<p>{t('_welcome.catAdjusterDescription')}</p>
</div> </div>
</article> </div>
<article> <article className="mt-5">
<h3 className="mb-1">{t('_missHai.ranking')}</h3>
<p>{t('_welcome.misshaiRankingDescription')}</p>
<Link to="/ranking">{t('_missHai.showRanking')}</Link>
</article>
<article>
<h3>{t('_welcome.nextFeaturesTitle')}</h3> <h3>{t('_welcome.nextFeaturesTitle')}</h3>
<p>{t('_welcome.nextFeaturesDescription')}</p> <p>{t('_welcome.nextFeaturesDescription')}</p>
</article> </article>

View file

@ -1,12 +1,22 @@
body { body {
--primary: rgb(134, 179, 0); --primary: rgb(134, 179, 0);
--max-width: 1024px;
font-family: "Koruri", sans-serif; font-family: "Koruri", sans-serif;
font-feature-settings: "pkna"; font-feature-settings: "pkna";
} }
hr {
height: 4px;
background: var(--dimmed);
border: none;
margin: 64px auto;
width: 50%;
max-width: 400px;
}
.xarticle { .xarticle {
margin: auto; margin: auto;
max-width: 1024px; max-width: var(--max-width);
} }
.fade { .fade {