0
0
Fork 0
This commit is contained in:
Xeltica 2021-09-14 04:11:43 +09:00
parent 587136a82d
commit 7eeb90eddc
13 changed files with 392 additions and 54 deletions

View file

@ -1,25 +1,29 @@
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
import { AlertMode } from '../../common/types/alert-mode';
import React, { useCallback, useEffect, useReducer, useState } from 'react';
import { alertModes } from '../../common/types/alert-mode';
import { IUser } from '../../common/types/user';
import { Visibility } from '../../common/types/visibility';
import { visibilities, Visibility } from '../../common/types/visibility';
import { useGetSessionQuery } from '../services/session';
import { defaultTemplate } from '../../common/default-template';
import { Card } from './Card';
import { Theme } from '../misc/theme';
import { API_ENDPOINT, LOCALSTORAGE_KEY_TOKEN } from '../const';
import { useDispatch } from 'react-redux';
import { changeTheme, showModal } from '../store/slices/screen';
import { useSelector } from '../store';
type SettingDraftType = Pick<IUser,
type SettingDraftType = Partial<Pick<IUser,
| 'alertMode'
| 'visibility'
| 'localOnly'
| 'remoteFollowersOnly'
| 'template'
>;
>>;
type DraftReducer = React.Reducer<SettingDraftType, Partial<SettingDraftType>>;
export const SettingPage: React.VFC = () => {
const session = useGetSessionQuery(undefined);
const dispatch = useDispatch();
const data = session.data;
@ -48,23 +52,38 @@ export const SettingPage: React.VFC = () => {
},
];
const [currentTheme, setCurrentTheme] = useState<Theme>('light');
const currentTheme = useSelector(state => state.screen.theme);
const [currentLang, setCurrentLang] = useState<string>('ja-JP');
const updateSetting = useCallback(() => {
fetch(`${API_ENDPOINT}session`, {
const availableVisibilities: Visibility[] = [
'public',
'home',
'followers'
];
const updateSetting = useCallback((obj: SettingDraftType) => {
dispatchDraft(obj);
return fetch(`${API_ENDPOINT}session`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${localStorage[LOCALSTORAGE_KEY_TOKEN]}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(draft),
})
.then(() => alert('設定を保存しました。'))
body: JSON.stringify(obj),
});
}, []);
const updateSettingWithDialog = useCallback((obj: SettingDraftType) => {
updateSetting(obj)
.then(() => dispatch(showModal({
type: 'dialog',
icon: 'info',
message: '保存しました。'
})))
.catch(e => {
alert(e.message);
});
}, [draft]);
}, [updateSetting]);
useEffect(() => {
if (data) {
@ -78,26 +97,70 @@ export const SettingPage: React.VFC = () => {
}
}, [data]);
const saveButton = useMemo(() => (
<button className="btn primary" style={{alignSelf: 'flex-end'}} onClick={updateSetting}>
</button>
), [updateSetting]);
const onClickSendAlert = useCallback(() => {
dispatch(showModal({
type: 'dialog',
title: 'アラートをテスト送信しますか?',
message: '現在の設定でアラートを送信します。設定が保存済みであるかどうか、実行前に必ずご確認ください。',
icon: 'question',
buttons: 'yesNo',
onSelect(i) {
if (i === 0) {
fetch(`${API_ENDPOINT}session/alert`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage[LOCALSTORAGE_KEY_TOKEN]}`,
},
}).then(() => {
dispatch(showModal({
type: 'dialog',
message: '送信しました。',
icon: 'info',
}));
}).catch((e) => {
console.error(e);
dispatch(showModal({
type: 'dialog',
message: '送信に失敗しました。',
icon: 'error',
}));
});
}
},
}));
}, [dispatch]);
const onClickLogout = useCallback(() => {
dispatch(showModal({
type: 'dialog',
message: 'WIP',
}));
}, [dispatch]);
const onClickDeleteAccount = useCallback(() => {
dispatch(showModal({
type: 'dialog',
message: 'WIP',
}));
}, [dispatch]);
return session.isLoading || !data ? (
<div className="skeleton" style={{width: '100%', height: '128px'}}></div>
) : (
<div className="vstack fade">
<Card bodyClassName="vstack">
<h1></h1>
<h1></h1>
<div>
<select name="alertMode" className="input-field" value={draft.alertMode} onChange={(e) => {
dispatchDraft({ alertMode: e.target.value as AlertMode });
}}>
<option value="note">稿</option>
<option value="notification">Misskeyに通知()</option>
<option value="nothing"></option>
</select>
{
alertModes.map((mode) => (
<label key={mode} className="input-check">
<input type="radio" checked={mode === draft.alertMode} onChange={() => {
updateSetting({ alertMode: mode });
}} />
<span>{mode}</span>
</label>
))
}
{draft.alertMode === 'notification' && (
<div className="alert bg-danger mt-2">
<i className="icon bi bi-exclamation-circle"></i>
@ -108,21 +171,24 @@ export const SettingPage: React.VFC = () => {
{ draft.alertMode === 'note' && (
<div>
<label htmlFor="visibility" className="input-field"></label>
<select name="visibility" className="input-field" value={draft.visibility} onChange={(e) => {
dispatchDraft({ visibility: e.target.value as Visibility });
}}>
<option value="public"></option>
<option value="home"></option>
<option value="followers"></option>
</select>
<label className="input-switch mt-2">
<input type="checkbox" />
<div className="switch"></div>
{
availableVisibilities.map((visibility) => (
<label key={visibility} className="input-check">
<input type="radio" checked={visibility === draft.visibility} onChange={() => {
updateSetting({ visibility });
}} />
<span>{visibility}</span>
</label>
))
}
<label className="input-check mt-2">
<input type="checkbox" checked={draft.localOnly} onChange={(e) => {
updateSetting({ localOnly: e.target.checked });
}} />
<span></span>
</label>
</div>
)}
{saveButton}
</Card>
<Card bodyClassName="vstack">
<h1></h1>
@ -131,7 +197,7 @@ export const SettingPage: React.VFC = () => {
{
themes.map(({ theme, name }) => (
<label key={theme} className="input-check">
<input type="radio" name={theme} value={theme} checked={theme === currentTheme} onChange={(e) => setCurrentTheme(e.target.value as Theme)} />
<input type="radio" value={theme} checked={theme === currentTheme} onChange={(e) => dispatch(changeTheme(e.target.value as Theme))} />
<span>{name}</span>
</label>
))
@ -147,8 +213,8 @@ export const SettingPage: React.VFC = () => {
<Card bodyClassName="vstack">
<h1></h1>
<p>稿使280</p>
<textarea className="input-field" value={draft.template ?? defaultTemplate} style={{height: 228}} onChange={(e) => {
<p>稿</p>
<textarea className="input-field" value={draft.template ?? defaultTemplate} placeholder={defaultTemplate} style={{height: 228}} onChange={(e) => {
dispatchDraft({ template: e.target.value });
}} />
<small className="text-dimmed">
@ -160,24 +226,29 @@ export const SettingPage: React.VFC = () => {
<li><code>{'{'}notesCount{'}'}</code>稿</li>
</ul>
</details>
{saveButton}
<div className="hstack" style={{justifyContent: 'flex-end'}}>
<button className="btn danger" onClick={() => dispatchDraft({ template: null })}></button>
<button className="btn primary" onClick={() => {
updateSettingWithDialog({ template: draft.template === '' ? null : draft.template });
}}></button>
</div>
</Card>
<Card bodyClassName="vstack">
<button className="btn block"></button>
<button className="btn block" onClick={onClickSendAlert}></button>
<p className="text-dimmed">
</p>
</Card>
<Card bodyClassName="vstack">
<button className="btn block"></button>
<button className="btn block" onClick={onClickLogout}></button>
<p className="text-dimmed">
</p>
</Card>
<Card bodyClassName="vstack">
<button className="btn danger block"></button>
<button className="btn danger block" onClick={onClickDeleteAccount}></button>
<p className="text-dimmed">
Misskeyとの連携設定を含むみす廃アラートのアカウントを削除します
Misskeyとの連携設定を含むみす廃アラートのアカウントを削除します
</p>
</Card>
</div>