import insertTextAtCursor from 'insert-text-at-cursor'; import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { alertModes } from '../../common/types/alert-mode'; import { IUser } from '../../common/types/user'; import { Visibility } from '../../common/types/visibility'; import { LOCALSTORAGE_KEY_ACCOUNTS, LOCALSTORAGE_KEY_TOKEN } from '../const'; import { $post, $put } from '../misc/api'; import { useGetScoreQuery, useGetSessionQuery } from '../services/session'; import { showModal } from '../store/slices/screen'; import { AnnouncementList } from './AnnouncementList'; import { Ranking } from './Ranking'; import { Skeleton } from './Skeleton'; import './MisshaiPage.scss'; import { DeveloperInfo } from './DeveloperInfo'; const variables = [ 'notesCount', 'followingCount', 'followersCount', 'notesDelta', 'followingDelta', 'followersDelta', 'url', 'username', 'host', 'rating', 'gacha', ] as const; type SettingDraftType = Partial>; type DraftReducer = React.Reducer>; export const MisshaiPage: React.VFC = () => { const dispatch = useDispatch(); const [limit, setLimit] = useState(10); const session = useGetSessionQuery(undefined); const data = session.data; const score = useGetScoreQuery(undefined); const {t} = useTranslation(); const [draft, dispatchDraft] = useReducer((state, action) => { return { ...state, ...action }; }, { alertMode: data?.alertMode ?? 'note', visibility: data?.visibility ?? 'public', localOnly: data?.localOnly ?? false, remoteFollowersOnly: data?.remoteFollowersOnly ?? false, template: data?.template ?? null, useRanking: data?.useRanking ?? false, }); const templateTextarea = useRef(null); const availableVisibilities: Visibility[] = [ 'public', 'home', 'followers' ]; const updateSetting = useCallback((obj: SettingDraftType) => { const previousDraft = draft; dispatchDraft(obj); return $put('session', obj) .catch(e => { dispatch(showModal({ type: 'dialog', icon: 'error', message: t('error'), })); dispatchDraft(previousDraft); }); }, [draft]); const updateSettingWithDialog = useCallback((obj: SettingDraftType) => { updateSetting(obj) .then(() => dispatch(showModal({ type: 'dialog', icon: 'info', message: t('saved'), }))); }, [updateSetting]); useEffect(() => { if (data) { dispatchDraft({ alertMode: data.alertMode, visibility: data.visibility, localOnly: data.localOnly, remoteFollowersOnly: data.remoteFollowersOnly, template: data.template, useRanking: data.useRanking }); } }, [data]); const onClickInsertVariables = useCallback((e) => { dispatch(showModal({ type: 'menu', screenX: e.clientX, screenY: e.clientY, items: variables.map(key => ({ name: t('_template._variables.' + key), onClick: () => { if (templateTextarea.current) { insertTextAtCursor(templateTextarea.current, `{${key}}`); } }, })), })); }, [dispatch, t, variables, templateTextarea.current]); const onClickInsertVariablesHelp = useCallback(() => { dispatch(showModal({ type: 'dialog', icon: 'info', message: t('_template.insertVariablesHelp'), })); }, [dispatch, t]); const onClickSendAlert = useCallback(() => { dispatch(showModal({ type: 'dialog', title: t('_sendTest.title'), message: t('_sendTest.message'), icon: 'question', buttons: [ { text: t('_sendTest.yes'), style: 'primary', }, { text: t('_sendTest.no'), }, ], onSelect(i) { if (i === 0) { $post('session/alert').then(() => { dispatch(showModal({ type: 'dialog', message: t('_sendTest.success'), icon: 'info', })); }).catch((e) => { console.error(e); dispatch(showModal({ type: 'dialog', message: t('_sendTest.failure'), icon: 'error', })); }); } }, })); }, [dispatch, t]); /** * Session APIのエラーハンドリング * このAPIがエラーを返した = トークンが無効 なのでトークンを削除してログアウトする */ useEffect(() => { if (session.error) { console.error(session.error); localStorage.removeItem(LOCALSTORAGE_KEY_TOKEN); const a = localStorage.getItem(LOCALSTORAGE_KEY_ACCOUNTS); if (a) { const accounts = JSON.parse(a) as string[]; if (accounts.length > 0) { localStorage.setItem(LOCALSTORAGE_KEY_TOKEN, accounts[0]); } } location.reload(); } }, [session.error]); const defaultTemplate = t('_template.default'); return session.isLoading || score.isLoading || !session.data || !score.data ? (
) : (

{t('_missHai.data')}

{t('_missHai.dataScore')} {t('_missHai.dataDelta')}
{t('notes')} {score.data.notesCount} {score.data.notesDelta}
{t('following')} {score.data.followingCount} {score.data.followingDelta}
{t('followers')} {score.data.followersCount} {score.data.followersDelta}

{t('_missHai.rating')}{': '} {session.data.rating}

{t('_missHai.ranking')}

{limit && }

{t('alertMode')}

{ alertModes.map((mode) => ( ))}
{ (draft.alertMode === 'notification' || draft.alertMode === 'both') && (
{t('_alertMode.notificationWarning')}
)} { (draft.alertMode === 'note' || draft.alertMode === 'both') && ( <>

{t('visibility')}

{ availableVisibilities.map((visibility) => ( )) }
)}

{t('template')}

{t('_template.description')}