style: use space indentation
feat: no new users allowed fix: change base language to Korean fix: change gacha to random text from devs
This commit is contained in:
parent
320dfc0696
commit
b6a3b0cd53
@ -6,4 +6,4 @@ end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_size = 4
|
||||
indent_size = 2
|
@ -5,16 +5,16 @@ const entities = require('./built/backend/services/db').entities;
|
||||
const config = Object.freeze(JSON.parse(fs.readFileSync(__dirname + '/config.json', 'utf-8')));
|
||||
|
||||
module.exports = {
|
||||
type: 'postgres',
|
||||
host: config.db.host,
|
||||
port: config.db.port,
|
||||
username: config.db.user,
|
||||
password: config.db.pass,
|
||||
database: config.db.db,
|
||||
extra: config.db.extra,
|
||||
entities: entities,
|
||||
migrations: ['migration/*.ts'],
|
||||
cli: {
|
||||
migrationsDir: 'migration'
|
||||
}
|
||||
type: 'postgres',
|
||||
host: config.db.host,
|
||||
port: config.db.port,
|
||||
username: config.db.user,
|
||||
password: config.db.pass,
|
||||
database: config.db.db,
|
||||
extra: config.db.extra,
|
||||
entities: entities,
|
||||
migrations: ['migration/*.ts'],
|
||||
cli: {
|
||||
migrationsDir: 'migration'
|
||||
}
|
||||
};
|
||||
|
@ -16,35 +16,9 @@ export const misskeyAppInfo = {
|
||||
permission: [
|
||||
'read:account',
|
||||
'write:account',
|
||||
'read:blocks',
|
||||
'write:blocks',
|
||||
'read:drive',
|
||||
'write:drive',
|
||||
'read:favorites',
|
||||
'write:favorites',
|
||||
'read:following',
|
||||
'write:following',
|
||||
'read:messaging',
|
||||
'write:messaging',
|
||||
'read:mutes',
|
||||
'write:mutes',
|
||||
'write:notes',
|
||||
'read:notifications',
|
||||
'write:notifications',
|
||||
'read:reactions',
|
||||
'write:reactions',
|
||||
'write:votes',
|
||||
'read:pages',
|
||||
'write:pages',
|
||||
'write:page-likes',
|
||||
'read:page-likes',
|
||||
'read:user-groups',
|
||||
'write:user-groups',
|
||||
'read:channels',
|
||||
'write:channels',
|
||||
'read:gallery',
|
||||
'write:gallery',
|
||||
'read:gallery-likes',
|
||||
'write:gallery-likes',
|
||||
],
|
||||
} as const;
|
||||
|
@ -24,5 +24,5 @@ export class UserSetting {
|
||||
useRanking?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
appendHashtag?: boolean;
|
||||
appendHashtag?: boolean;
|
||||
}
|
||||
|
@ -180,8 +180,12 @@ router.get('(.*)', async (ctx) => {
|
||||
|
||||
async function login(ctx: Context, user: Record<string, unknown>, host: string, token: string) {
|
||||
const isNewcomer = !(await getUser(user.username as string, host));
|
||||
await upsertUser(user.username as string, host, token);
|
||||
if (isNewcomer) {
|
||||
await die(ctx, 'noNewUserAllowed', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
await upsertUser(user.username as string, host, token);
|
||||
const u = await getUser(user.username as string, host);
|
||||
|
||||
if (!u) {
|
||||
|
@ -38,23 +38,23 @@ export const work = async () => {
|
||||
await calculateAllRating(groupedUsers);
|
||||
}
|
||||
catch (e) {
|
||||
printLog('Misskey Tools with LycheeBridge 레이팅 계산에 실패했습니다.', 'error');
|
||||
printLog(e instanceof Error ? errorToString(e) : JSON.stringify(e, null, ' '), 'error');
|
||||
Store.dispatch({ nowCalculating: false });
|
||||
printLog('Misskey Tools with LycheeBridge 레이팅 계산에 실패했습니다.', 'error');
|
||||
printLog(e instanceof Error ? errorToString(e) : JSON.stringify(e, null, ' '), 'error');
|
||||
Store.dispatch({ nowCalculating: false });
|
||||
}
|
||||
finally {
|
||||
Store.dispatch({ nowCalculating: false });
|
||||
printLog(`${users.length}개의 계정 레이팅 계산이 완료되었습니다.`);
|
||||
Store.dispatch({ nowCalculating: false });
|
||||
printLog(`${users.length}개의 계정 레이팅 계산이 완료되었습니다.`);
|
||||
}
|
||||
|
||||
try {
|
||||
printLog(`${users.length}개의 계정에 알림을 전송하고 있습니다.`);
|
||||
printLog(`${users.length}개의 계정에 알림을 전송하고 있습니다.`);
|
||||
await sendAllAlerts(groupedUsers);
|
||||
} catch (e) {
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송에 실패했습니다.', 'error');
|
||||
printLog(e instanceof Error ? errorToString(e) : JSON.stringify(e, null, ' '), 'error');
|
||||
} finally {
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송이 완료되었습니다.');
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송이 완료되었습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
@ -75,10 +75,10 @@ const calculateRating = async (host: string, users: User[]) => {
|
||||
// ユーザーが削除されている場合、レコードからも消してとりやめ
|
||||
printLog(`${toAcct(user)} 게정이 삭제, 정지, 또는 토큰이 제거된 것으로 보이며, 시스템에서 계정이 제거되었습니다.`, 'warn');
|
||||
await deleteUser(user.username, user.host);
|
||||
continue;
|
||||
continue;
|
||||
} else {
|
||||
printLog(`Misskey 오류: ${JSON.stringify(e.error)}`, 'error');
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
} else if (e instanceof TimedOutError) {
|
||||
printLog(`${user.host} 인스턴스로의 연결에 실패하여 레이팅 계산을 중단합니다.`, 'error');
|
||||
@ -105,7 +105,7 @@ const sendAlerts = async (host: string, users: User[]) => {
|
||||
.map(user => {
|
||||
const count = userScoreCache.get(toAcct(user));
|
||||
if (count == null) return null;
|
||||
if (count.notesCount - (user.prevNotesCount ?? 0) <= 1) return null;
|
||||
if (count.notesCount - (user.prevNotesCount ?? 0) <= 1) return null;
|
||||
return {
|
||||
user,
|
||||
count,
|
||||
@ -121,31 +121,31 @@ const sendAlerts = async (host: string, users: User[]) => {
|
||||
|
||||
// 通知
|
||||
for (const {user, count, message} of models.filter(m => m.user.alertMode === 'notification' || m.user.alertMode === 'both')) {
|
||||
try {
|
||||
try {
|
||||
await sendNotificationAlert(message, user);
|
||||
} catch (e) {
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송에 실패했습니다.', 'error');
|
||||
} catch (e) {
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송에 실패했습니다.', 'error');
|
||||
printLog(e instanceof Error ? errorToString(e) : JSON.stringify(e, null, ' '), 'error');
|
||||
} finally {
|
||||
if (user.alertMode === 'notification') {
|
||||
await updateScore(user, count);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (user.alertMode === 'notification') {
|
||||
await updateScore(user, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// アラート
|
||||
for (const {user, count, message} of models.filter(m => m.user.alertMode === 'note' || m.user.alertMode === 'both')) {
|
||||
try {
|
||||
try {
|
||||
await sendNoteAlert(message, user);
|
||||
} catch (e) {
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송에 실패했습니다.', 'error');
|
||||
printLog(e instanceof Error ? errorToString(e) : JSON.stringify(e, null, ' '), 'error');
|
||||
} finally {
|
||||
await Promise.all([
|
||||
updateScore(user, count),
|
||||
delay(1000),
|
||||
]);
|
||||
}
|
||||
} catch (e) {
|
||||
printLog('Misskey Tools with LycheeBridge 알림 전송에 실패했습니다.', 'error');
|
||||
printLog(e instanceof Error ? errorToString(e) : JSON.stringify(e, null, ' '), 'error');
|
||||
} finally {
|
||||
await Promise.all([
|
||||
updateScore(user, count),
|
||||
delay(1000),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
printLog(`${host} 인스턴스의 사용자 ${users.length}명의 알림 전송이 완료되었습니다.`);
|
||||
|
@ -1,41 +1,41 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(charset="UTF-8")
|
||||
meta(name="viewport", content="width=device-width, initial-scale=1.0")
|
||||
block meta
|
||||
- const title = t ? `${t} | Misskey Tools`: 'Misskey Tools with LycheeBridge';
|
||||
- const desc = d || '🌠 연합우주의 모든 Misskey 사용자를 위한 다양한 도구 모음집 🚀';
|
||||
title= title
|
||||
meta(name='description' content=desc)
|
||||
meta(property='og:title' content=title)
|
||||
meta(property='og:site_name' content='Misskey Tools with LycheeBridge')
|
||||
meta(property='og:description' content=desc)
|
||||
meta(property='og:type' content='website')
|
||||
meta(property='og:image' content="https://tools.phater.live/assets/misskey.png")
|
||||
link(rel="icon", href="/assets/lcb.png", type="image/png")
|
||||
link(rel="preload", href="/assets/otadesign_rounded.woff")
|
||||
link(rel="preload", href="/assets/otadesign_rounded.woff2")
|
||||
link(rel="preload", href="/assets/PretendardJPVariable.woff2")
|
||||
script(src='https://kit.fontawesome.com/a4464bc6cd.js' crossorigin='anonymous')
|
||||
link(rel="stylesheet", href="/assets/style.css")
|
||||
body
|
||||
#app: .loading 불러오는 중입니다...
|
||||
head
|
||||
meta(charset="UTF-8")
|
||||
meta(name="viewport", content="width=device-width, initial-scale=1.0")
|
||||
block meta
|
||||
- const title = t ? `${t} | Misskey Tools`: 'Misskey Tools with LycheeBridge';
|
||||
- const desc = d || '🌠 연합우주의 모든 Misskey 사용자를 위한 다양한 도구 모음집 🚀';
|
||||
title= title
|
||||
meta(name='description' content=desc)
|
||||
meta(property='og:title' content=title)
|
||||
meta(property='og:site_name' content='Misskey Tools with LycheeBridge')
|
||||
meta(property='og:description' content=desc)
|
||||
meta(property='og:type' content='website')
|
||||
meta(property='og:image' content="https://tools.phater.live/assets/misskey.png")
|
||||
link(rel="icon", href="/assets/lcb.png", type="image/png")
|
||||
link(rel="preload", href="/assets/otadesign_rounded.woff")
|
||||
link(rel="preload", href="/assets/otadesign_rounded.woff2")
|
||||
link(rel="preload", href="/assets/PretendardJPVariable.woff2")
|
||||
script(src='https://kit.fontawesome.com/a4464bc6cd.js' crossorigin='anonymous')
|
||||
link(rel="stylesheet", href="/assets/style.css")
|
||||
body
|
||||
#app: .loading 불러오는 중입니다...
|
||||
|
||||
if token
|
||||
script.
|
||||
const token = '#{token}';
|
||||
const previousToken = localStorage.getItem('token');
|
||||
const accounts = JSON.parse(localStorage.getItem('accounts') || '[]');
|
||||
if (previousToken && !accounts.includes(previousToken)) {
|
||||
accounts.push(previousToken);
|
||||
}
|
||||
localStorage.setItem('accounts', JSON.stringify(accounts));
|
||||
localStorage.setItem('token', token);
|
||||
history.replaceState(null, null, '/');
|
||||
if token
|
||||
script.
|
||||
const token = '#{token}';
|
||||
const previousToken = localStorage.getItem('token');
|
||||
const accounts = JSON.parse(localStorage.getItem('accounts') || '[]');
|
||||
if (previousToken && !accounts.includes(previousToken)) {
|
||||
accounts.push(previousToken);
|
||||
}
|
||||
localStorage.setItem('accounts', JSON.stringify(accounts));
|
||||
localStorage.setItem('token', token);
|
||||
history.replaceState(null, null, '/');
|
||||
|
||||
if error
|
||||
script.
|
||||
window.__misshaialert = { error: '#{error}' };
|
||||
if error
|
||||
script.
|
||||
window.__misshaialert = { error: '#{error}' };
|
||||
|
||||
script(src=`/assets/fe.${version}.js` async defer)
|
||||
script(src=`/assets/fe.${version}.js` async defer)
|
||||
|
@ -1,13 +1,13 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(charset="UTF-8")
|
||||
body
|
||||
p 클라이언트에 문제가 발생했습니다. 해결을 위해 브라우저에 있는 데이터를 제거했습니다.
|
||||
p 3초 뒤에 메인 페이지로 자동으로 돌아갑니다...
|
||||
hr
|
||||
p There's a problem in client and deleted data on device to solve it.
|
||||
p You will be redirected to the top in 3 seconds.
|
||||
script.
|
||||
localStorage.clear();
|
||||
setTimeout(() => location.href = '/', 3000);
|
||||
head
|
||||
meta(charset="UTF-8")
|
||||
body
|
||||
p 클라이언트에 문제가 발생했습니다. 해결을 위해 브라우저에 있는 데이터를 제거했습니다.
|
||||
p 3초 뒤에 메인 페이지로 자동으로 돌아갑니다...
|
||||
hr
|
||||
p There's a problem in client and deleted data on device to solve it.
|
||||
p You will be redirected to the top in 3 seconds.
|
||||
script.
|
||||
localStorage.clear();
|
||||
setTimeout(() => location.href = '/', 3000);
|
||||
|
@ -1,25 +1,11 @@
|
||||
const allKatakana = [
|
||||
...('アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨ'.split('')),
|
||||
'ウィ', 'ウェ',
|
||||
'キャ', 'キュ', 'キョ',
|
||||
'クァ', 'クォ',
|
||||
'シャ', 'シュ', 'ショ',
|
||||
'チャ', 'チュ', 'チョ',
|
||||
'ヒャ', 'ヒュ', 'ヒョ',
|
||||
'ミャ', 'ミュ', 'ミョ'
|
||||
const allTexts: string[] = [
|
||||
'아무말 빔----',
|
||||
'휴대폰용 보험은 가입하셨나요?',
|
||||
];
|
||||
|
||||
const allInfix = [ '', 'ー', 'ッ' ];
|
||||
|
||||
const getRandomKatakana = () => allKatakana[Math.floor(Math.random() * allKatakana.length)];
|
||||
const getRandomInfix = () => allInfix[Math.floor(Math.random() * allInfix.length)];
|
||||
const getRandomText = () => allTexts[Math.floor(Math.random() * allTexts.length)];
|
||||
|
||||
export const createGacha = () => {
|
||||
return [
|
||||
getRandomKatakana(),
|
||||
getRandomInfix(),
|
||||
getRandomKatakana(),
|
||||
getRandomInfix(),
|
||||
...(new Array(Math.floor(Math.random() * 2 + 1)).fill('').map(() => getRandomKatakana()))
|
||||
].join('');
|
||||
const result = getRandomText();
|
||||
return result;
|
||||
};
|
||||
|
@ -47,7 +47,7 @@ export const format = (user: IUser, count: Count): string => {
|
||||
return !v ? m : typeof v === 'function' ? v(score, user) : v;
|
||||
});
|
||||
if (user.appendHashtag) {
|
||||
result = result + '\n\n#misshaialert'
|
||||
result = result + '\n\n#misshaialert';
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ export const errorCodes = [
|
||||
'notAuthorized',
|
||||
'hostNotFound',
|
||||
'invalidHostFormat',
|
||||
'noNewUserAllowed',
|
||||
'other',
|
||||
] as const;
|
||||
|
||||
|
@ -5,4 +5,4 @@ export const visibilities = [
|
||||
'users' // ログインユーザー (Groundpolis 限定)
|
||||
] as const;
|
||||
|
||||
export type Visibility = typeof visibilities[number];
|
||||
export type Visibility = typeof visibilities[number];
|
||||
|
@ -10,36 +10,36 @@ import { useSelector } from './store';
|
||||
import { setDrawerShown } from './store/slices/screen';
|
||||
|
||||
const Container = styled.div<IsMobileProp>`
|
||||
padding: var(--margin);
|
||||
position: relative;
|
||||
padding: var(--margin);
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const Sidebar = styled.nav`
|
||||
width: 320px;
|
||||
position: fixed;
|
||||
top: var(--margin);
|
||||
left: var(--margin);
|
||||
width: 320px;
|
||||
position: fixed;
|
||||
top: var(--margin);
|
||||
left: var(--margin);
|
||||
`;
|
||||
|
||||
const Main = styled.main<IsMobileProp>`
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
margin-top: 80px;
|
||||
margin-left: ${p => !p.isMobile ? `${320 + 16}px` : 0};
|
||||
min-width: 0;
|
||||
margin-left: ${p => !p.isMobile ? `${320 + 16}px` : 0};
|
||||
min-width: 0;
|
||||
`;
|
||||
|
||||
const MobileHeader = styled.header`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 64px;
|
||||
background: var(--panel);
|
||||
z-index: 1000;
|
||||
> h1 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 64px;
|
||||
background: var(--panel);
|
||||
z-index: 1000;
|
||||
> h1 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const GeneralLayout: React.FC = ({children}) => {
|
||||
|
@ -6,7 +6,7 @@ import { useGetSessionQuery } from './services/session';
|
||||
import { useSelector } from './store';
|
||||
|
||||
export type HeaderProps = {
|
||||
title?: string;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export const Header: React.FC<HeaderProps> = ({title}) => {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
export type CardProps = {
|
||||
className?: string;
|
||||
bodyClassName?: string;
|
||||
className?: string;
|
||||
bodyClassName?: string;
|
||||
};
|
||||
|
||||
export const Card: React.FC<CardProps> = ({children, className, bodyClassName}) => {
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type HashtagTimelineProps = {
|
||||
hashtag: string;
|
||||
hashtag: string;
|
||||
};
|
||||
|
||||
export const HashtagTimeline: React.VFC<HashtagTimelineProps> = ({hashtag}) => {
|
||||
|
@ -7,7 +7,7 @@ const LogItem: React.FC<{log: Log}> = ({log}) => {
|
||||
|
||||
return (
|
||||
<div className={`log ${log.level}`}>
|
||||
[{time}] {log.text}
|
||||
[{time}] {log.text}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Input = styled.input`
|
||||
width: auto;
|
||||
flex: 1;
|
||||
width: auto;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export const LoginForm: React.VFC = () => {
|
||||
|
@ -63,7 +63,7 @@ export const NavigationMenu: React.VFC = () => {
|
||||
{meta && (
|
||||
<section>
|
||||
<a className="item" href={CHANGELOG_URL} onClick={onClickItem}>
|
||||
v{meta.version} {t('changelog')}
|
||||
v{meta.version} {t('changelog')}
|
||||
</a>
|
||||
</section>
|
||||
)}
|
||||
|
@ -3,19 +3,19 @@ import { useTranslation } from 'react-i18next';
|
||||
import { $get } from '../misc/api';
|
||||
|
||||
interface RankingResponse {
|
||||
isCalculating: boolean;
|
||||
userCount: number;
|
||||
ranking: Ranking[];
|
||||
isCalculating: boolean;
|
||||
userCount: number;
|
||||
ranking: Ranking[];
|
||||
}
|
||||
|
||||
interface Ranking {
|
||||
username?: string;
|
||||
host?: string;
|
||||
rating: number;
|
||||
username?: string;
|
||||
host?: string;
|
||||
rating: number;
|
||||
}
|
||||
|
||||
export type RankingProps = {
|
||||
limit?: number;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export const Ranking: React.VFC<RankingProps> = ({limit}) => {
|
||||
@ -60,7 +60,7 @@ export const Ranking: React.VFC<RankingProps> = ({limit}) => {
|
||||
<div className="item flex" key={i}>
|
||||
<div className="text-bold pr-2">{i + 1}</div>
|
||||
<div>
|
||||
-------------<br/>
|
||||
-------------<br/>
|
||||
<span className="text-dimmed text-75">{t('_missHai.rating')}: {r.rating}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
export type SkeletonProps = {
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
};
|
||||
|
||||
export const Skeleton: React.VFC<SkeletonProps> = (p) => {
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
export type TabItem = {
|
||||
label: string;
|
||||
key: string;
|
||||
isNew?: boolean;
|
||||
label: string;
|
||||
key: string;
|
||||
isNew?: boolean;
|
||||
};
|
||||
|
||||
export type TabProps = {
|
||||
items: TabItem[];
|
||||
selected: string;
|
||||
onSelect: (key: string) => void;
|
||||
items: TabItem[];
|
||||
selected: string;
|
||||
onSelect: (key: string) => void;
|
||||
};
|
||||
|
||||
// タブコンポーネント
|
||||
|
@ -50,7 +50,7 @@
|
||||
"update": "Update",
|
||||
"shareMisskeyTools": "Share #MisskeyTools",
|
||||
"shareMisskeyToolsNote": "Try #MisskeyTools !\n\nhttps://t.psec.dev",
|
||||
"instanceUrlPlaceholder": "e.g. misskey.io",
|
||||
"instanceUrlPlaceholder": "k.lapy.link, stella.place, psec.dev...",
|
||||
"settings": "Settings",
|
||||
"accentColor": "Accent Color",
|
||||
"changelog": "Changelog",
|
||||
@ -156,7 +156,7 @@
|
||||
}
|
||||
},
|
||||
"_error": {
|
||||
"sorry": "Something went wrong. Please retry again.",
|
||||
"sorry": "We have some small problem. Please try again later.",
|
||||
"additionalInfo": "Additional Info: ",
|
||||
"hitorisskeyIsDenied": "You cannot integrate with hitorisskey.",
|
||||
"teapot": "I'm a teapot.",
|
||||
@ -164,6 +164,7 @@
|
||||
"tokenRequired": "Token is required.",
|
||||
"invalidParameter": "Invalid parameter.",
|
||||
"notAuthorized": "Not authorized.",
|
||||
"noNewUserAllowed": "You cannot signup to this service for now.",
|
||||
"hostNotFound": "Could not connect to the instance. Make sure that the host name is correct and the instance is live.",
|
||||
"other": "None"
|
||||
},
|
||||
|
@ -10,19 +10,19 @@ const merge = (baseData: Record<string, unknown>, newData: Record<string, unknow
|
||||
});
|
||||
};
|
||||
|
||||
const _enUS = merge(jaJP, enUS);
|
||||
const _koKR = merge(_enUS, koKR)
|
||||
const _enUS = merge(koKR, enUS);
|
||||
const _jaJP = merge(_enUS, jaJP);
|
||||
|
||||
export const resources = {
|
||||
'ja_JP': { translation: jaJP },
|
||||
'ko_KR': { translation: koKR },
|
||||
'en_US': { translation: _enUS },
|
||||
'ko_KR': { translation: _koKR },
|
||||
'ja_JP': { translation: _jaJP },
|
||||
};
|
||||
|
||||
export const languageName = {
|
||||
'ja_JP': '日本語',
|
||||
'en_US': 'English',
|
||||
'ko_KR': '한국어',
|
||||
'en_US': 'English',
|
||||
'ja_JP': '日本語',
|
||||
} as const;
|
||||
|
||||
export type LanguageCode = keyof typeof resources;
|
||||
|
@ -50,7 +50,7 @@
|
||||
"update": "更新する",
|
||||
"shareMisskeyTools": "#MisskeyTools をシェアする",
|
||||
"shareMisskeyToolsNote": "#MisskeyTools はいいぞ\n\nhttps://t.psec.dev",
|
||||
"instanceUrlPlaceholder": "例:misskey.io",
|
||||
"instanceUrlPlaceholder": "k.lapy.link, stella.place, psec.dev...",
|
||||
"settings": "設定",
|
||||
"accentColor": "アクセントカラー",
|
||||
"changelog": "更新履歴",
|
||||
@ -164,6 +164,7 @@
|
||||
"tokenRequired": "トークンがありません。",
|
||||
"invalidParameter": "パラメータが不正です。",
|
||||
"notAuthorized": "権限がありません。",
|
||||
"noNewUserAllowed": "現在、新規登録はできません。",
|
||||
"hostNotFound": "インスタンスに接続できませんでした。ホスト名が正しく入力されているか、接続先のインスタンスが正常に動作しているかご確認ください。",
|
||||
"other": "不明なエラーです。"
|
||||
},
|
||||
|
@ -164,6 +164,7 @@
|
||||
"tokenRequired": "잘못된 토큰이거나, 세션이 존재하지 않습니다.",
|
||||
"invalidParameter": "잘못된 요청 내용입니다. 값을 확인 후 다시 시도하세요.",
|
||||
"notAuthorized": "이 리소스에 접근할 권한이 없습니다.",
|
||||
"noNewUserAllowed": "현재 본 서비스에 신규 가입하실 수 없습니다.",
|
||||
"hostNotFound": "인스턴스에 접속할 수 없습니다. 입력한 인스턴스 주소가 올바른지, API 요청이 가능한지 확인해주세요.",
|
||||
"other": "알 수 없는 오류가 발생했습니다. Mastodon 인스턴스로 로그인을 시도했을 수 있습니다."
|
||||
},
|
||||
|
@ -43,7 +43,7 @@ export const AccountsPage: React.VFC = () => {
|
||||
accounts.map(account => (
|
||||
<button className="item fluid" style={{display: 'flex', flexDirection: 'row', alignItems: 'center'}} onClick={() => switchAccount(account.misshaiToken)}>
|
||||
<i className="icon fas fa-chevron-right" />
|
||||
@{account.username}@{account.host}
|
||||
@{account.username}@{account.host}
|
||||
<button className="btn flat text-danger" style={{marginLeft: 'auto'}} onClick={e => {
|
||||
const filteredAccounts = accounts.filter(ac => ac.id !== account.id);
|
||||
dispatch(setAccounts(filteredAccounts));
|
||||
|
@ -85,9 +85,9 @@ export const AdminPage: React.VFC = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Session APIのエラーハンドリング
|
||||
* このAPIがエラーを返した = トークンが無効 なのでトークンを削除してログアウトする
|
||||
*/
|
||||
* Session APIのエラーハンドリング
|
||||
* このAPIがエラーを返した = トークンが無効 なのでトークンを削除してログアウトする
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
@ -97,8 +97,8 @@ export const AdminPage: React.VFC = () => {
|
||||
}, [error]);
|
||||
|
||||
/**
|
||||
* Edit Modeがオンのとき、Delete Modeを無効化する(誤操作防止)
|
||||
*/
|
||||
* Edit Modeがオンのとき、Delete Modeを無効化する(誤操作防止)
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (isEditMode) {
|
||||
setDeleteMode(false);
|
||||
@ -106,8 +106,8 @@ export const AdminPage: React.VFC = () => {
|
||||
}, [isEditMode]);
|
||||
|
||||
/**
|
||||
* お知らせ取得
|
||||
*/
|
||||
* お知らせ取得
|
||||
*/
|
||||
useEffect(() => {
|
||||
fetchAll();
|
||||
}, []);
|
||||
@ -166,7 +166,7 @@ export const AdminPage: React.VFC = () => {
|
||||
{!isDeleteMode && (
|
||||
<button className="item fluid" onClick={() => setEditMode(true)}>
|
||||
<i className="icon fas fa-plus" />
|
||||
새로운 공지사항 만들기
|
||||
새로운 공지사항 만들기
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@ -174,22 +174,22 @@ export const AdminPage: React.VFC = () => {
|
||||
) : (
|
||||
<div className="vstack">
|
||||
<label className="input-field">
|
||||
제목
|
||||
제목
|
||||
<input type="text" value={draftTitle} onChange={e => setDraftTitle(e.target.value)} />
|
||||
</label>
|
||||
<label className="input-field">
|
||||
내용
|
||||
내용
|
||||
<textarea className="input-field" value={draftBody} rows={10} onChange={e => setDraftBody(e.target.value)}/>
|
||||
</label>
|
||||
<div className="hstack" style={{justifyContent: 'flex-end'}}>
|
||||
<button className="btn primary" onClick={submitAnnouncement} disabled={!draftTitle || !draftBody}>
|
||||
완료
|
||||
완료
|
||||
</button>
|
||||
<button className="btn" onClick={() => {
|
||||
selectAnnouncement(null);
|
||||
setEditMode(false);
|
||||
}}>
|
||||
취소
|
||||
취소
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -199,7 +199,7 @@ export const AdminPage: React.VFC = () => {
|
||||
<h2>노트왕 알림</h2>
|
||||
<div className="vstack">
|
||||
<button className="btn danger" onClick={onClickStartMisshaiAlertWorkerButton}>
|
||||
Misskey Tools 알림을 지금 전송하기
|
||||
Misskey Tools 알림을 지금 전송하기
|
||||
</button>
|
||||
<h3>최근 알림 프로세스의 기록</h3>
|
||||
{misshaiLog && <LogView log={misshaiLog} />}
|
||||
|
@ -31,13 +31,13 @@ const variables = [
|
||||
] as const;
|
||||
|
||||
type SettingDraftType = Partial<Pick<IUser,
|
||||
| 'alertMode'
|
||||
| 'visibility'
|
||||
| 'localOnly'
|
||||
| 'remoteFollowersOnly'
|
||||
| 'template'
|
||||
| 'useRanking'
|
||||
| 'appendHashtag'
|
||||
| 'alertMode'
|
||||
| 'visibility'
|
||||
| 'localOnly'
|
||||
| 'remoteFollowersOnly'
|
||||
| 'template'
|
||||
| 'useRanking'
|
||||
| 'appendHashtag'
|
||||
>>;
|
||||
|
||||
type DraftReducer = React.Reducer<SettingDraftType, Partial<SettingDraftType>>;
|
||||
@ -169,9 +169,9 @@ export const MisshaiPage: React.VFC = () => {
|
||||
}, [dispatch, t]);
|
||||
|
||||
/**
|
||||
* Session APIのエラーハンドリング
|
||||
* このAPIがエラーを返した = トークンが無効 なのでトークンを削除してログアウトする
|
||||
*/
|
||||
* Session APIのエラーハンドリング
|
||||
* このAPIがエラーを返した = トークンが無効 なのでトークンを削除してログアウトする
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (session.error) {
|
||||
console.error(session.error);
|
||||
|
@ -51,7 +51,7 @@ export const IndexSessionPage: React.VFC = () => {
|
||||
<td>{score.data?.followersCount ?? '...'}</td>
|
||||
<td>{score.data?.followersDelta ?? '...'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>{t('_missHai.rating')}</td>
|
||||
<td>{session?.rating ?? '...'}</td>
|
||||
</tr>
|
||||
@ -64,7 +64,7 @@ export const IndexSessionPage: React.VFC = () => {
|
||||
<div className="menu large">
|
||||
<a className="item" href="https://psec.dev/@PSEC" target="_blank" rel="noopener noreferrer">
|
||||
<i className="icon fas fa-at"></i>
|
||||
@PSEC@psec.dev
|
||||
@PSEC@psec.dev
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -10,60 +10,60 @@ import Twemoji from 'react-twemoji';
|
||||
import { useAnnouncements } from '../hooks/useAnnouncements';
|
||||
|
||||
const Hero = styled.div<IsMobileProp>`
|
||||
display: flex;
|
||||
position: relative;
|
||||
background: linear-gradient(-135deg, rgb(1, 169, 46), rgb(134, 179, 0) 35%);
|
||||
color: var(--white);
|
||||
padding: ${f => f.isMobile ? '16px' : '60px 90px'};
|
||||
overflow: hidden;
|
||||
gap: var(--margin);
|
||||
> .hero {
|
||||
flex: 2;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
p {
|
||||
${f => f.isMobile ? 'font-size: 1rem;' : ''}
|
||||
}
|
||||
}
|
||||
> .announcements {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-height: 512px;
|
||||
overflow: auto;
|
||||
padding: var(--margin);
|
||||
border-radius: var(--radius);
|
||||
background: var(--black-50);
|
||||
backdrop-filter: blur(4px) brightness(120%);
|
||||
z-index: 1000;
|
||||
@media screen and (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
> .rects {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
right: 160px;
|
||||
bottom: -120px;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
gap: 8px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
transform-origin: center center;
|
||||
transform: rotate(45deg);
|
||||
opacity: 0.5;
|
||||
> .rect {
|
||||
border: 2px solid var(--white);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 2px 4px var(--shadow-color);
|
||||
}
|
||||
}
|
||||
display: flex;
|
||||
position: relative;
|
||||
background: linear-gradient(-135deg, rgb(1, 169, 46), rgb(134, 179, 0) 35%);
|
||||
color: var(--white);
|
||||
padding: ${f => f.isMobile ? '16px' : '60px 90px'};
|
||||
overflow: hidden;
|
||||
gap: var(--margin);
|
||||
> .hero {
|
||||
flex: 2;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
p {
|
||||
${f => f.isMobile ? 'font-size: 1rem;' : ''}
|
||||
}
|
||||
}
|
||||
> .announcements {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-height: 512px;
|
||||
overflow: auto;
|
||||
padding: var(--margin);
|
||||
border-radius: var(--radius);
|
||||
background: var(--black-50);
|
||||
backdrop-filter: blur(4px) brightness(120%);
|
||||
z-index: 1000;
|
||||
@media screen and (max-width: 800px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
> .rects {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
right: 160px;
|
||||
bottom: -120px;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
gap: 8px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
transform-origin: center center;
|
||||
transform: rotate(45deg);
|
||||
opacity: 0.5;
|
||||
> .rect {
|
||||
border: 2px solid var(--white);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 2px 4px var(--shadow-color);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
max-width: 500px;
|
||||
color: var(--fg);
|
||||
max-width: 500px;
|
||||
color: var(--fg);
|
||||
`;
|
||||
|
||||
export const IndexWelcomePage: React.VFC = () => {
|
||||
@ -102,7 +102,7 @@ export const IndexWelcomePage: React.VFC = () => {
|
||||
</Hero>
|
||||
<Twemoji options={{className: 'twemoji'}}>
|
||||
<div className="py-4 text-125 text-center">
|
||||
👍 ❤ 😆 🎉 🍮
|
||||
👍 ❤ 😆 🎉 🍮
|
||||
</div>
|
||||
</Twemoji>
|
||||
<article className="xarticle vstack pa-2">
|
||||
|
@ -14,23 +14,23 @@ import { designSystemColors } from '../../common/types/design-system-color';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const ColorInput = styled.input<{color: string}>`
|
||||
display: block;
|
||||
appearance: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 999px;
|
||||
background-color: var(--panel);
|
||||
border: 4px solid var(--${p => p.color});
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
&:checked {
|
||||
background: var(--${p => p.color});
|
||||
cursor: default;
|
||||
}
|
||||
&:hover, &:focus {
|
||||
box-shadow: 0 0 16px var(--${p => p.color});
|
||||
outline: none;
|
||||
}
|
||||
display: block;
|
||||
appearance: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 999px;
|
||||
background-color: var(--panel);
|
||||
border: 4px solid var(--${p => p.color});
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
&:checked {
|
||||
background: var(--${p => p.color});
|
||||
cursor: default;
|
||||
}
|
||||
&:hover, &:focus {
|
||||
box-shadow: 0 0 16px var(--${p => p.color});
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingPage: React.VFC = () => {
|
||||
|
@ -1,13 +1,13 @@
|
||||
@font-face {
|
||||
font-family: 'Pretendard JP Variable';
|
||||
font-weight: 45 920;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("/assets/PretendardJPVariable.woff2") format('woff2-variations');
|
||||
font-family: 'Pretendard JP Variable';
|
||||
font-weight: 45 920;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("/assets/PretendardJPVariable.woff2") format('woff2-variations');
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -1,9 +1,9 @@
|
||||
@font-face {
|
||||
font-family: 'Pretendard JP Variable';
|
||||
font-weight: 45 920;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("/assets/PretendardJPVariable.woff2") format('woff2-variations');
|
||||
font-family: 'Pretendard JP Variable';
|
||||
font-weight: 45 920;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url("/assets/PretendardJPVariable.woff2") format('woff2-variations');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -8,74 +8,74 @@ const isProduction = process.env.NODE_ENV === 'production';
|
||||
const meta = require('./package.json');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
fe: './src/frontend/init.tsx',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.(eot|woff|woff2|svg|ttf)([?]?.*)$/,
|
||||
type: 'asset/resource'
|
||||
}, {
|
||||
test: /\.json5$/,
|
||||
loader: 'json5-loader',
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
type: 'javascript/auto'
|
||||
}, {
|
||||
test: /\.tsx?$/,
|
||||
use: [
|
||||
{ loader: 'ts-loader' }
|
||||
]
|
||||
}, {
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
url: false,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
implementation: require('sass'),
|
||||
sassOptions: {
|
||||
fiber: false
|
||||
},
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}, {
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
}]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProgressPlugin({}),
|
||||
],
|
||||
output: {
|
||||
path: __dirname + '/built/assets',
|
||||
filename: `[name].${meta.version}.js`,
|
||||
publicPath: '/assets/',
|
||||
pathinfo: false,
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.js', '.ts', '.json', '.tsx'
|
||||
],
|
||||
alias: {
|
||||
}
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: ['node_modules']
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true
|
||||
},
|
||||
devtool: false, //'source-map',
|
||||
mode: isProduction ? 'production' : 'development'
|
||||
entry: {
|
||||
fe: './src/frontend/init.tsx',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.(eot|woff|woff2|svg|ttf)([?]?.*)$/,
|
||||
type: 'asset/resource'
|
||||
}, {
|
||||
test: /\.json5$/,
|
||||
loader: 'json5-loader',
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
type: 'javascript/auto'
|
||||
}, {
|
||||
test: /\.tsx?$/,
|
||||
use: [
|
||||
{ loader: 'ts-loader' }
|
||||
]
|
||||
}, {
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
url: false,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
implementation: require('sass'),
|
||||
sassOptions: {
|
||||
fiber: false
|
||||
},
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}, {
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
}]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProgressPlugin({}),
|
||||
],
|
||||
output: {
|
||||
path: __dirname + '/built/assets',
|
||||
filename: `[name].${meta.version}.js`,
|
||||
publicPath: '/assets/',
|
||||
pathinfo: false,
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.js', '.ts', '.json', '.tsx'
|
||||
],
|
||||
alias: {
|
||||
}
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: ['node_modules']
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true
|
||||
},
|
||||
devtool: false, //'source-map',
|
||||
mode: isProduction ? 'production' : 'development'
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user