wip: feat: ミス廃アラート
This commit is contained in:
parent
720895df07
commit
063781478b
@ -6,9 +6,9 @@ import { getMisskey } from '@/libs/misskey.js';
|
||||
import { prisma } from '@/libs/prisma.js';
|
||||
import { connection } from '@/libs/redis.js';
|
||||
import { queues } from '@/queue/index.js';
|
||||
import { format } from '@/services/holic/format';
|
||||
import { format } from '@/services/holic/format.js';
|
||||
import { avg } from '@/utils/avg.js';
|
||||
import { toAcct } from '@/utils/to-acct';
|
||||
import { toAcct } from '@/utils/to-acct.js';
|
||||
|
||||
const NAME = 'holicAggregate';
|
||||
|
||||
@ -24,6 +24,9 @@ export const holicAggregateWorker = new Worker<HolicAggregateQueueType>(NAME, as
|
||||
// ステータスをお問い合わせ
|
||||
const { account } = job.data;
|
||||
const { misskeySession: session } = account;
|
||||
const acct = toAcct(session);
|
||||
|
||||
console.log(`[holic] Aggregating for ${acct}`);
|
||||
const data = await getMisskey(session.host).request('i', {}, session.token);
|
||||
if (!('notesCount' in data)) {
|
||||
job.discard();
|
||||
@ -43,7 +46,9 @@ export const holicAggregateWorker = new Worker<HolicAggregateQueueType>(NAME, as
|
||||
const rating = records1week.length > 0 ? avg([
|
||||
...records1week.map(r => r.notesCount),
|
||||
data.notesCount,
|
||||
]) : data.notesCount;
|
||||
]) : 0;
|
||||
|
||||
console.log(`[holic] RATING of ${acct}: ${rating}`);
|
||||
|
||||
// 今日分のデータ作成
|
||||
const today = await prisma.holicRecord.create({
|
||||
@ -57,15 +62,8 @@ export const holicAggregateWorker = new Worker<HolicAggregateQueueType>(NAME, as
|
||||
},
|
||||
});
|
||||
|
||||
const yesterday: HolicRecord = records1week[0] ?? {
|
||||
id: 0,
|
||||
rating: 0,
|
||||
date: new Date(),
|
||||
accountId: account.misskeySessionId,
|
||||
notesCount: 0,
|
||||
followingCount: 0,
|
||||
followersCount: 0,
|
||||
};
|
||||
|
||||
const yesterday: HolicRecord = records1week[0] ?? today;
|
||||
|
||||
const text = format({
|
||||
today,
|
||||
@ -74,7 +72,6 @@ export const holicAggregateWorker = new Worker<HolicAggregateQueueType>(NAME, as
|
||||
session,
|
||||
});
|
||||
|
||||
const acct = toAcct(session);
|
||||
if (account.alertAsNote) {
|
||||
queues.holicNoteQueue.add(acct, {
|
||||
account,
|
||||
|
@ -5,7 +5,8 @@ import type { MisskeySession, HolicAccount } from '@prisma/client';
|
||||
|
||||
import { getMisskey } from '@/libs/misskey.js';
|
||||
import { connection } from '@/libs/redis.js';
|
||||
import { isVisibility } from '@/utils/is-visibility';
|
||||
import { isVisibility } from '@/utils/is-visibility.js';
|
||||
import { toAcct } from '@/utils/to-acct.js';
|
||||
|
||||
const NAME = 'holicNote';
|
||||
|
||||
@ -20,6 +21,8 @@ export const holicNoteQueue = new Queue<HolicNoteQueueType>(NAME, { connection }
|
||||
export const holicNoteWorker = new Worker<HolicNoteQueueType>(NAME, async (job) => {
|
||||
const { session, account, text } = job.data;
|
||||
|
||||
console.log(`[holic] Processing note job for ${toAcct(session)}`);
|
||||
|
||||
const api = getMisskey(session.host);
|
||||
|
||||
if (!isVisibility(account.noteVisibility)) {
|
||||
@ -36,6 +39,7 @@ export const holicNoteWorker = new Worker<HolicNoteQueueType>(NAME, async (job)
|
||||
localOnly: account.noteLocalOnly,
|
||||
}, session.token);
|
||||
} catch (e) {
|
||||
console.log(`[holic] Failed to create a note: ${e}`);
|
||||
if (!misskey.api.isAPIError(e)) throw e;
|
||||
if (e.code === 'RATE_LIMIT_EXCEEDED') {
|
||||
// delay 1h
|
||||
|
@ -4,6 +4,7 @@ import type { MisskeySession, HolicAccount } from '@prisma/client';
|
||||
|
||||
import { getMisskey } from '@/libs/misskey.js';
|
||||
import { connection } from '@/libs/redis.js';
|
||||
import { toAcct } from '@/utils/to-acct';
|
||||
|
||||
const NAME = 'holicNotification';
|
||||
|
||||
@ -17,6 +18,7 @@ export const holicNotificationQueue = new Queue<HolicNotificationQueueType>(NAME
|
||||
|
||||
export const holicNotificationWorker = new Worker<HolicNotificationQueueType>(NAME, async (job) => {
|
||||
const { session, text } = job.data;
|
||||
console.log(`[holic] Processing note job for ${toAcct(session)}`);
|
||||
|
||||
const api = getMisskey(session.host);
|
||||
await api.request('notifications/create', {
|
||||
|
@ -48,5 +48,5 @@ export const format = (p: VariableParameter): string => {
|
||||
return template.replace(variableRegex, (m, name) => {
|
||||
const v = variables[name];
|
||||
return !v ? m : typeof v === 'function' ? v(p) : v;
|
||||
}) + '\n\n#missholic';
|
||||
}) + '\n\n#misskeholic';
|
||||
};
|
||||
|
@ -13,11 +13,13 @@ const IndexWelcome = lazy(() => import('@/pages/index.welcome'));
|
||||
const Settings = lazy(() => import('@/pages/settings'));
|
||||
const Appearance = lazy(() => import('@/pages/settings/appearance'));
|
||||
const Account = lazy(() => import('@/pages/settings/account'));
|
||||
const AnnouncementsPage = lazy(() => import('@/pages/announcements'));
|
||||
const AboutPage = lazy(() => import('@/pages/about'));
|
||||
const Announcements = lazy(() => import('@/pages/announcements'));
|
||||
const About = lazy(() => import('@/pages/about'));
|
||||
const AppsNoteScheduler = lazy(() => import('@/pages/apps/note-scheduler'));
|
||||
const AppsNoteSchedulerNew = lazy(() => import('@/pages/apps/note-scheduler.new'));
|
||||
const AppsNoteSchedulerEdit = lazy(() => import('@/pages/apps/note-scheduler.edit'));
|
||||
const Misskeholic = lazy(() => import('@/pages/apps/misskeholic'));
|
||||
const MisskeholicUser = lazy(() => import('@/pages/apps/misskeholic.user'));
|
||||
const NotFound = lazy(() => import('@/pages/not-found'));
|
||||
|
||||
export const App : React.FC = () => {
|
||||
@ -34,11 +36,13 @@ export const App : React.FC = () => {
|
||||
<Route path="account" element={<Account />}/>
|
||||
<Route path="*" element={<p>Not Found</p>}/>
|
||||
</Route>
|
||||
<Route path="/announcements/:id" element={<AnnouncementsPage />}/>
|
||||
<Route path="/about" element={<AboutPage />}/>
|
||||
<Route path="/announcements/:id" element={<Announcements />}/>
|
||||
<Route path="/about" element={<About />}/>
|
||||
<Route path="/apps/note-scheduler" element={<AppsNoteScheduler />}/>
|
||||
<Route path="/apps/note-scheduler/new" element={<AppsNoteSchedulerNew />}/>
|
||||
<Route path="/apps/note-scheduler/edit/:id" element={<AppsNoteSchedulerEdit />}/>
|
||||
<Route path="/apps/misskeholic" element={<Misskeholic />}/>
|
||||
<Route path="/apps/misskeholic/:id" element={<MisskeholicUser />}/>
|
||||
<Route path="*" element={<NotFound />}/>
|
||||
</Routes>
|
||||
<VStack as="footer" alignItems="center" css={{ padding: '$2xl $m' }}>
|
||||
|
112
packages/frontend/src/components/domains/holic/HolicSettings.tsx
Normal file
112
packages/frontend/src/components/domains/holic/HolicSettings.tsx
Normal file
@ -0,0 +1,112 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { RouterOutput } from '@/libs/trpc';
|
||||
|
||||
import { HStack } from '@/components/layouts/HStack';
|
||||
import { VStack } from '@/components/layouts/VStack';
|
||||
import { Alert } from '@/components/primitives/Alert';
|
||||
import { Button } from '@/components/primitives/Button';
|
||||
import { Card } from '@/components/primitives/Card';
|
||||
import { Radio } from '@/components/primitives/Radio';
|
||||
import { RadioGroup } from '@/components/primitives/RadioGroup';
|
||||
import { Switch } from '@/components/primitives/Switch';
|
||||
import { Text } from '@/components/primitives/Text';
|
||||
import { Textarea } from '@/components/primitives/Textarea';
|
||||
|
||||
type Account = NonNullable<RouterOutput['holic']['getAccount']>;
|
||||
|
||||
export type HolicSettingsDraft = Omit<Account, 'misskeySessionId'>;
|
||||
|
||||
export type HolicSettingsProp = {
|
||||
draft?: HolicSettingsDraft;
|
||||
onUpdateDraft?: (newDraft: HolicSettingsDraft) => void;
|
||||
};
|
||||
|
||||
export const DRAFT_DEFAULT: HolicSettingsDraft = {
|
||||
alertAsNote: false,
|
||||
alertAsNotification: true,
|
||||
noteVisibility: 'home',
|
||||
noteLocalOnly: false,
|
||||
rankingVisible: false,
|
||||
template: null,
|
||||
};
|
||||
|
||||
|
||||
export const HolicSettings: React.FC<HolicSettingsProp> = (p) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [draft, setDraft] = useState<HolicSettingsDraft>(DRAFT_DEFAULT);
|
||||
const [isHelpDialogOpened, setHelpDialogOpened] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!p.draft) return;
|
||||
setDraft(p.draft);
|
||||
}, [p.draft]);
|
||||
|
||||
const updateDraft = <K extends keyof HolicSettingsDraft, V extends HolicSettingsDraft[K]>(key: K, value: V) => {
|
||||
const newDraft = { ...draft };
|
||||
newDraft[key] = value;
|
||||
setDraft(newDraft);
|
||||
p.onUpdateDraft?.(newDraft);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card pale>
|
||||
<h1>{t('alertMode')}</h1>
|
||||
<VStack>
|
||||
<Switch checked={draft.alertAsNote} onChange={v => updateDraft('alertAsNote', v)}>
|
||||
{t('_misshaiAlert.note')}
|
||||
</Switch>
|
||||
<Switch checked={draft.alertAsNotification} onChange={v => updateDraft('alertAsNotification', v)}>
|
||||
{t('_misshaiAlert.notification')}
|
||||
</Switch>
|
||||
</VStack>
|
||||
</Card>
|
||||
<Card pale>
|
||||
<h1>{t('visibility')}</h1>
|
||||
<VStack>
|
||||
<RadioGroup value={draft.noteVisibility} onValueChange={v => updateDraft('noteVisibility', v)}>
|
||||
<Radio value="home">{t('_visibility.home')}</Radio>
|
||||
<Radio value="followers">{t('_visibility.followers')}</Radio>
|
||||
</RadioGroup>
|
||||
<Switch checked={draft.noteLocalOnly} onChange={v => updateDraft('noteLocalOnly', v)}>
|
||||
{t('noFederation')}
|
||||
</Switch>
|
||||
</VStack>
|
||||
</Card>
|
||||
<Card pale>
|
||||
<h1>{t('_misshaiAlert.holicRanking')}</h1>
|
||||
<VStack>
|
||||
<Switch checked={draft.rankingVisible} onChange={v => updateDraft('rankingVisible', v)}>
|
||||
{t('_misshaiAlert.useRanking')}
|
||||
</Switch>
|
||||
</VStack>
|
||||
</Card>
|
||||
<Card pale>
|
||||
<h1>{t('template')}</h1>
|
||||
<p>
|
||||
{t('_template.description')}<br/>
|
||||
<Text as="span" fontSize="s" color="muted">{t('_template.hashtagAutomaticInsertion')}</Text>
|
||||
</p>
|
||||
<VStack alignItems="left">
|
||||
<HStack>
|
||||
<Button size="small" primary>{t('_template.insertVariables')}</Button>
|
||||
<Button size="small" flat primary css={{ fontSize: 20, padding: 0 }} onClick={() => setHelpDialogOpened(true)}>
|
||||
<i className="ti ti-help-circle"/>
|
||||
</Button>
|
||||
</HStack>
|
||||
<Textarea rows={8} placeholder={t('_template.default')} value={draft.template ?? ''} onChange={e => updateDraft('template', e.target.value)} />
|
||||
</VStack>
|
||||
</Card>
|
||||
|
||||
<Alert
|
||||
description={t('_template.insertVariablesHelp')}
|
||||
open={isHelpDialogOpened}
|
||||
onOpenChange={setHelpDialogOpened}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -14,6 +14,7 @@ export type AlertProp = {
|
||||
title?: string;
|
||||
description: string;
|
||||
cancelText?: string;
|
||||
hasCancel?: boolean;
|
||||
okText?: string;
|
||||
danger?: boolean;
|
||||
onOkClick?: () => void;
|
||||
@ -32,11 +33,13 @@ export const Alert: React.FC<AlertProp> = (p) => {
|
||||
{p.description}
|
||||
</AlertDialogDescription>
|
||||
<HStack justifyContent="right">
|
||||
<$.Cancel asChild>
|
||||
<Button flat>
|
||||
{p.cancelText ?? t('cancel')}
|
||||
</Button>
|
||||
</$.Cancel>
|
||||
{p.hasCancel && (
|
||||
<$.Cancel asChild>
|
||||
<Button flat>
|
||||
{p.cancelText ?? t('cancel')}
|
||||
</Button>
|
||||
</$.Cancel>
|
||||
)}
|
||||
<$.Action asChild>
|
||||
<Button danger={p.danger} primaryGradient={!p.danger} onClick={p.onOkClick}>
|
||||
{p.okText ?? t('ok')}
|
||||
|
@ -50,7 +50,7 @@
|
||||
"upload": "アップロード",
|
||||
"preview": "プレビュー",
|
||||
"dashboard": "ダッシュボード",
|
||||
"missHaiAlert": "ミス廃アラート",
|
||||
"misskeholicAlert": "ミス廃アラート",
|
||||
"avatarCropper": "アバタークロッパー",
|
||||
"questionBox": "質問ボックス",
|
||||
"followingManager": "フォローマネージャー",
|
||||
@ -77,9 +77,9 @@
|
||||
"datetime": "日時",
|
||||
|
||||
"_welcome": {
|
||||
"misshaiAlertTitle": "ミス廃アラート",
|
||||
"misshaiAlertDescription": "Misskeyにのめり込んでいませんか?ミス廃アラートを使えば、毎日のMisskeyでの活動量を定期投稿できます。",
|
||||
"misshaiRankingDescription": "ミス廃ランキングでは、Misskeyでの活動を数値化し、ランキング表示します。",
|
||||
"misskeholicAlertTitle": "ミス廃アラート",
|
||||
"misskeholicAlertDescription": "Misskeyにのめり込んでいませんか?ミス廃アラートを使えば、毎日のMisskeyでの活動量を定期投稿できます。",
|
||||
"misskeholicRankingDescription": "ミス廃ランキングでは、Misskeyでの活動を数値化し、ランキング表示します。",
|
||||
"avatarCropperDescription": "丸形、角丸形、そして猫耳のついた状態をプレビューしながら、アイコンを自在に切り抜けます。魅力的なアバターを、もっと魅力的に。",
|
||||
"questionBoxDescription": "匿名で質問できるページを開設しよう。届いた質問には、お使いのMisskeyアカウントで回答できます。",
|
||||
"followingManagerDescription": "増えすぎたフォロー・フォロワーを管理できる高機能なマネージャー。所属サーバーや、フォローバックされていないアカウントで絞り込むことができ、一括処理にも対応しています。",
|
||||
@ -95,16 +95,17 @@
|
||||
},
|
||||
|
||||
"_widgets": {
|
||||
"misshaiData": "今日のミス廃データ",
|
||||
"holicData": "今日のミス廃データ",
|
||||
"announcements": "お知らせ",
|
||||
"questionBox": "質問ボックス",
|
||||
"apps": "アプリ",
|
||||
"unreadHints": "未読のヒント"
|
||||
},
|
||||
|
||||
"_missHai": {
|
||||
"ranking": "ミス廃ランキング",
|
||||
"rankingDescription": "ユーザーの「ミス廃レート」を算出し、高い順にランキング表示しています。",
|
||||
"_misshaiAlert": {
|
||||
"gettingStarted": "利用を開始する",
|
||||
"holicRanking": "ミス廃ランキング",
|
||||
"holicRankingDescription": "ユーザーの「ミス廃レート」を算出し、高い順にランキング表示しています。",
|
||||
"showAll": "全員分見る",
|
||||
"data": "ミス廃データ",
|
||||
"dataBody": "内容",
|
||||
@ -118,6 +119,15 @@
|
||||
"notification": "Misskeyに通知する",
|
||||
"notificationWarning": "Misskeyのバージョンによっては動作しません。"
|
||||
},
|
||||
"_noteScheduler": {
|
||||
"createNew": "新規作成",
|
||||
"edit": "編集",
|
||||
"delete": "削除",
|
||||
"noNotes": "予約投稿はまだ作成されていません。",
|
||||
"deleteConfirm": "本当に予約投稿「{{note}}」を削除しますか?",
|
||||
"accountToNote": "投稿するアカウント",
|
||||
"schedule": "投稿を予約"
|
||||
},
|
||||
"_accounts": {
|
||||
"switchAccount": "アカウント切り替え",
|
||||
"useAnother": "他のアカウントで登録する"
|
||||
@ -135,7 +145,7 @@
|
||||
},
|
||||
"_template": {
|
||||
"description": "アラートの自動投稿をカスタマイズできます。",
|
||||
"description2": "ハッシュタグ #misshaialert は、テンプレートに関わらず自動付与されます。",
|
||||
"hashtagAutomaticInsertion": "ハッシュタグ #missholicalert は、テンプレートに関わらず自動付与されます。",
|
||||
"default": "昨日のMisskeyの活動は\n\nノート: {notesCount}({notesDelta})\nフォロー : {followingCount}({followingDelta})\nフォロワー :{followersCount}({followersDelta})\n\nでした。\n{url}",
|
||||
"insertVariables": "変数を挿入する",
|
||||
"insertVariablesHelp": "{ } で囲われた文字列は変数と呼ばれ、特別な意味を持ちます。これを含めると、投稿時に自動的に値が埋め込まれます。",
|
||||
@ -185,14 +195,5 @@
|
||||
"no": "キャンセル",
|
||||
"success": "アカウントを解除しました。トップ画面に戻ります。",
|
||||
"failure": "アカウントを解除できませんでした。"
|
||||
},
|
||||
"_noteScheduler": {
|
||||
"createNew": "新規作成",
|
||||
"edit": "編集",
|
||||
"delete": "削除",
|
||||
"noNotes": "予約投稿はまだ作成されていません。",
|
||||
"deleteConfirm": "本当に予約投稿「{{note}}」を削除しますか?",
|
||||
"accountToNote": "投稿するアカウント",
|
||||
"schedule": "投稿を予約"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { blackA, olive, oliveDark, tomato, tomatoDark, whiteA } from '@radix-ui/colors';
|
||||
import {
|
||||
blackA,
|
||||
olive,
|
||||
oliveDark,
|
||||
tomato,
|
||||
tomatoDark,
|
||||
whiteA,
|
||||
} from '@radix-ui/colors';
|
||||
import { createStitches } from '@stitches/react';
|
||||
|
||||
export const {
|
||||
|
61
packages/frontend/src/pages/apps/misskeholic.tsx
Normal file
61
packages/frontend/src/pages/apps/misskeholic.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import type { RouterOutput } from '@/libs/trpc';
|
||||
|
||||
import { VStack } from '@/components/layouts/VStack';
|
||||
import { PageRoot } from '@/components/PageRoot';
|
||||
import { Button } from '@/components/primitives/Button';
|
||||
import { Heading } from '@/components/primitives/Heading';
|
||||
import { styled } from '@/libs/stitches';
|
||||
import { trpc } from '@/libs/trpc';
|
||||
|
||||
const StyledButton = styled('button', {
|
||||
borderRadius: '$2',
|
||||
padding: '$m',
|
||||
background: '$card',
|
||||
boxShadow: '$s',
|
||||
textDecoration: 'none',
|
||||
color: '$fg',
|
||||
});
|
||||
|
||||
type SessionSelectorProp = {
|
||||
onSelect?: (session: RouterOutput['account']['getMisskeySessions'][number]) => void;
|
||||
};
|
||||
|
||||
const SessionSelector: React.FC<SessionSelectorProp> = (p) => {
|
||||
const [sessions] = trpc.account.getMisskeySessions.useSuspenseQuery();
|
||||
const { mutateAsync: run } = trpc.holic.adminForceRunAll.useMutation();
|
||||
|
||||
const onClickForceRunButton = () => run();
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Misskey アカウントを選択してください。</p>
|
||||
<VStack>
|
||||
{sessions.map(s => (
|
||||
<StyledButton key={s.id} as={Link} to={`/apps/misskeholic/${s.id}`} onClick={() => {
|
||||
p.onSelect?.(s);
|
||||
}}>
|
||||
@{s.username}@{s.host}
|
||||
</StyledButton>
|
||||
))}
|
||||
</VStack>
|
||||
<Button danger onClick={onClickForceRunButton}>Force RUN</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const MisskeholicPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<PageRoot title={t('misskeholicAlert')}>
|
||||
<Heading>{t('misskeholicAlert')}</Heading>
|
||||
<SessionSelector/>
|
||||
</PageRoot>
|
||||
);
|
||||
};
|
||||
|
||||
export default MisskeholicPage;
|
56
packages/frontend/src/pages/apps/misskeholic.user.tsx
Normal file
56
packages/frontend/src/pages/apps/misskeholic.user.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { DRAFT_DEFAULT, HolicSettings } from '@/components/domains/holic/HolicSettings';
|
||||
import { VStack } from '@/components/layouts/VStack';
|
||||
import { PageRoot } from '@/components/PageRoot';
|
||||
import { Button } from '@/components/primitives/Button';
|
||||
import { Heading } from '@/components/primitives/Heading';
|
||||
import { trpc } from '@/libs/trpc';
|
||||
|
||||
const MisskeholicUserPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { id } = useParams();
|
||||
|
||||
const [ sessions ] = trpc.account.getMisskeySessions.useSuspenseQuery();
|
||||
const [ holicAccount, { refetch } ] = trpc.holic.getAccount.useSuspenseQuery({ sessionId: id ?? '' });
|
||||
const { mutateAsync: createAccount } = trpc.holic.createAccount.useMutation();
|
||||
|
||||
const session = useMemo(() => sessions.find(s => s.id === id), [id, sessions]);
|
||||
|
||||
const [draft, setDraft] = useState(DRAFT_DEFAULT);
|
||||
|
||||
if (session === null) return null;
|
||||
|
||||
const startHolic = async () => {
|
||||
if (!session) return;
|
||||
await createAccount({
|
||||
sessionId: session.id,
|
||||
...draft,
|
||||
});
|
||||
await refetch();
|
||||
};
|
||||
return (
|
||||
<PageRoot title={t('misskeholicAlert')}>
|
||||
<Heading>{t('misskeholicAlert')}</Heading>
|
||||
{!holicAccount ? (
|
||||
<>
|
||||
<VStack>
|
||||
<p>いくつかの項目を設定し、ミス廃アラートをセットアップしましょう。</p>
|
||||
<HolicSettings draft={draft} onUpdateDraft={setDraft} />
|
||||
<Button primaryGradient size="large" css={{ margin: '$xl auto 0 auto' }} onClick={startHolic}>
|
||||
ミス廃アラートを開始する
|
||||
</Button>
|
||||
</VStack>
|
||||
</>
|
||||
) : (
|
||||
<pre>
|
||||
{JSON.stringify(holicAccount, null, ' ')}
|
||||
</pre>
|
||||
)}
|
||||
</PageRoot>
|
||||
);
|
||||
};
|
||||
|
||||
export default MisskeholicUserPage;
|
@ -111,6 +111,7 @@ const NoteCard: React.FC<{ note: RouterOutput['noteScheduler']['list'][number] }
|
||||
</VStack>
|
||||
<Alert
|
||||
danger
|
||||
hasCancel
|
||||
description={t('_noteScheduler.deleteConfirm', { note: note.text })}
|
||||
open={deleteConfirmDialogOpened}
|
||||
onOpenChange={setDeleteConfirmDialogOpened}
|
||||
|
Loading…
Reference in New Issue
Block a user