resolve #11
This commit is contained in:
parent
033c4bb12c
commit
52685eb3a4
6 changed files with 153 additions and 14 deletions
22
migration/1609938844427-visibility.ts
Normal file
22
migration/1609938844427-visibility.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import {MigrationInterface, QueryRunner} from 'typeorm';
|
||||||
|
|
||||||
|
export class visibility1609938844427 implements MigrationInterface {
|
||||||
|
name = 'visibility1609938844427'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query('CREATE TYPE "user_visibility_enum" AS ENUM(\'public\', \'home\', \'followers\', \'users\')');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" ADD "visibility" "user_visibility_enum" NOT NULL DEFAULT \'home\'');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" ADD "localOnly" boolean NOT NULL DEFAULT false');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" ADD "remoteFollowersOnly" boolean NOT NULL DEFAULT false');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" ALTER COLUMN "alertMode" SET DEFAULT \'notification\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query('ALTER TABLE "user" ALTER COLUMN "alertMode" SET DEFAULT \'note\'');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" DROP COLUMN "remoteFollowersOnly"');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" DROP COLUMN "localOnly"');
|
||||||
|
await queryRunner.query('ALTER TABLE "user" DROP COLUMN "visibility"');
|
||||||
|
await queryRunner.query('DROP TYPE "user_visibility_enum"');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
|
import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
|
||||||
import { AlertMode, alertModes } from '../../types/AlertMode';
|
import { AlertMode, alertModes } from '../../types/AlertMode';
|
||||||
|
import { visibilities, Visibility } from '../../types/Visibility';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index([ 'username', 'host' ], { unique: true })
|
@Index([ 'username', 'host' ], { unique: true })
|
||||||
|
@ -52,4 +53,23 @@ export class User {
|
||||||
default: 'notification'
|
default: 'notification'
|
||||||
})
|
})
|
||||||
public alertMode: AlertMode;
|
public alertMode: AlertMode;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: visibilities,
|
||||||
|
default: 'home',
|
||||||
|
})
|
||||||
|
public visibility: Visibility;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public localOnly: boolean;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public remoteFollowersOnly: boolean;
|
||||||
}
|
}
|
|
@ -12,6 +12,7 @@ import { getScores } from '../functions/get-scores';
|
||||||
import { AlertMode, alertModes } from '../types/AlertMode';
|
import { AlertMode, alertModes } from '../types/AlertMode';
|
||||||
import { Users } from '../models';
|
import { Users } from '../models';
|
||||||
import { send } from '../services/send';
|
import { send } from '../services/send';
|
||||||
|
import { visibilities, Visibility } from '../types/Visibility';
|
||||||
|
|
||||||
export const router = new Router<DefaultState, Context>();
|
export const router = new Router<DefaultState, Context>();
|
||||||
|
|
||||||
|
@ -59,9 +60,13 @@ router.get('/', async ctx => {
|
||||||
const user = token ? await getUserByMisshaiToken(token) : undefined;
|
const user = token ? await getUserByMisshaiToken(token) : undefined;
|
||||||
|
|
||||||
const isAvailable = user && await apiAvailable(user.host, user.token);
|
const isAvailable = user && await apiAvailable(user.host, user.token);
|
||||||
|
|
||||||
if (user && isAvailable) {
|
if (user && isAvailable) {
|
||||||
|
const meta = await api<{ version: string }>(user?.host, 'meta', {});
|
||||||
await ctx.render('mypage', {
|
await ctx.render('mypage', {
|
||||||
user,
|
user,
|
||||||
|
// To Activate Groundpolis Mode
|
||||||
|
isGroundpolis: meta.version.includes('gp'),
|
||||||
usersCount: await getUserCount(),
|
usersCount: await getUserCount(),
|
||||||
score: await getScores(user),
|
score: await getScores(user),
|
||||||
from: ctx.query.from,
|
from: ctx.query.from,
|
||||||
|
@ -195,6 +200,14 @@ router.post('/update-settings', async ctx => {
|
||||||
await die(ctx, `${mode} is an invalid value`);
|
await die(ctx, `${mode} is an invalid value`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const visibility = ctx.request.body.visibility as Visibility;
|
||||||
|
// 一応型チェック
|
||||||
|
if (!visibilities.includes(visibility)) {
|
||||||
|
await die(ctx, `${mode} is an invalid value`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const flag = ctx.request.body.flag;
|
||||||
|
|
||||||
const token = ctx.cookies.get('token');
|
const token = ctx.cookies.get('token');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
@ -209,7 +222,12 @@ router.post('/update-settings', async ctx => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(u.id, { alertMode: mode });
|
await Users.update(u.id, {
|
||||||
|
alertMode: mode,
|
||||||
|
localOnly: flag === 'localOnly',
|
||||||
|
remoteFollowersOnly: flag === 'remoteFollowersOnly',
|
||||||
|
visibility,
|
||||||
|
});
|
||||||
|
|
||||||
ctx.redirect('/?from=updateSettings');
|
ctx.redirect('/?from=updateSettings');
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,9 +8,13 @@ export const send = async (user: User): Promise<void> => {
|
||||||
|
|
||||||
if (user.alertMode === 'note') {
|
if (user.alertMode === 'note') {
|
||||||
console.info(`send ${user.username}@${user.host}'s misshaialert as a note`);
|
console.info(`send ${user.username}@${user.host}'s misshaialert as a note`);
|
||||||
const res = await api<Record<string, unknown>>(user.host, 'notes/create', {
|
const opts = {
|
||||||
text,
|
text,
|
||||||
}, user.token);
|
visibility: user.visibility,
|
||||||
|
} as Record<string, unknown>;
|
||||||
|
if (user.localOnly) opts.localOnly = user.localOnly;
|
||||||
|
if (user.remoteFollowersOnly) opts.remoteFollowersOnly = user.remoteFollowersOnly;
|
||||||
|
const res = await api<Record<string, unknown>>(user.host, 'notes/create', opts, user.token);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
throw res.error || res;
|
throw res.error || res;
|
||||||
}
|
}
|
||||||
|
|
8
src/types/Visibility.ts
Normal file
8
src/types/Visibility.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export const visibilities = [
|
||||||
|
'public', // パブリック
|
||||||
|
'home', // ホーム
|
||||||
|
'followers', // フォロワー
|
||||||
|
'users' // ログインユーザー (Groundpolis 限定)
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type Visibility = typeof visibilities[number];
|
|
@ -49,6 +49,19 @@ block content
|
||||||
td !{score.followersDelta}
|
td !{score.followersDelta}
|
||||||
|
|
||||||
section.xd-card#settings
|
section.xd-card#settings
|
||||||
|
-
|
||||||
|
const visibilities = [
|
||||||
|
[ 'public', 'パブリック'],
|
||||||
|
[ 'home', isGroundpolis ? '未収載' : 'ホーム'],
|
||||||
|
[ 'followers', 'フォロワー'],
|
||||||
|
];
|
||||||
|
if (isGroundpolis) visibilities.push(['users', 'ログインユーザー']);
|
||||||
|
const alertModes = [
|
||||||
|
[ 'note', '自動的にノートを投稿' ],
|
||||||
|
[ 'notification', 'Misskeyに通知(標準)' ],
|
||||||
|
[ 'nothing', '通知しない' ],
|
||||||
|
];
|
||||||
|
const currentAlertModeLabel = alertModes.find(a => a[0] === user.alertMode)[1];
|
||||||
.header
|
.header
|
||||||
h1.title 設定
|
h1.title 設定
|
||||||
.body
|
.body
|
||||||
|
@ -57,21 +70,75 @@ block content
|
||||||
| スコア通知方法に「Misskey に通知」を選んでいる場合、Groundpolis v3 および Misskey v12 の最新版以外では動作しません。めいすきーや古いバージョンをお使いの方は、「自動的にノートを投稿」をお使いください。
|
| スコア通知方法に「Misskey に通知」を選んでいる場合、Groundpolis v3 および Misskey v12 の最新版以外では動作しません。めいすきーや古いバージョンをお使いの方は、「自動的にノートを投稿」をお使いください。
|
||||||
form(method="post", action="/update-settings")
|
form(method="post", action="/update-settings")
|
||||||
p: label スコア通知方法:
|
p: label スコア通知方法:
|
||||||
select(name="alertMode")
|
select(name="alertMode", tabindex=1)
|
||||||
option(value="note", selected=user.alertMode === 'note') 自動的にノートを投稿 (標準)
|
each set in alertModes
|
||||||
option(value="notification", selected=user.alertMode === 'notification') Misskey に通知
|
option(value=set[0], selected=(user.alertMode === set[0]))= set[1]
|
||||||
option(value="nothing", selected=user.alertMode === 'nothing') 通知しない
|
p: label 公開範囲:
|
||||||
p: label タイムゾーン:(coming soon)
|
select(name="visibility", tabindex=2)
|
||||||
button.primary(type="submit") 保存
|
each set in visibilities
|
||||||
|
option(value=set[0], selected=(user.visibility === set[0]))= set[1]
|
||||||
|
p
|
||||||
|
| フラグ <br />
|
||||||
|
label
|
||||||
|
input(type="radio", name="flag", value="none", checked=!user.localOnly && !user.remoteFollowersOnly, tabindex=3)
|
||||||
|
| なし(標準)<br />
|
||||||
|
label
|
||||||
|
input(type="radio", name="flag", value="localOnly", checked=user.localOnly, tabindex=4)
|
||||||
|
| ローカルのみ<br />
|
||||||
|
if isGroundpolis
|
||||||
|
label
|
||||||
|
input(type="radio", name="flag", value="remoteFollowersOnly", checked=user.remoteFollowersOnly, tabindex=5)
|
||||||
|
| リモートフォロワーとローカル<br />
|
||||||
|
div
|
||||||
|
label 投稿テンプレート
|
||||||
|
div: textarea(name="template", disabled, tabindex=6)
|
||||||
|
details(tabindex=7)
|
||||||
|
summary ヘルプ
|
||||||
|
p
|
||||||
|
code {notesCount}
|
||||||
|
| などのテキストを挿入することで、投稿時に自動的に値が埋め込まれます。これを変数といいます。変数の表を次に示します。
|
||||||
|
table
|
||||||
|
thead: tr
|
||||||
|
th 変数
|
||||||
|
th 説明
|
||||||
|
tbody
|
||||||
|
tr
|
||||||
|
td {notesCount}
|
||||||
|
td ノート数
|
||||||
|
tr
|
||||||
|
td {followingCount}
|
||||||
|
td フォロー数
|
||||||
|
tr
|
||||||
|
td {followersCount}
|
||||||
|
td フォロワー数
|
||||||
|
tr
|
||||||
|
td {notesDelta}
|
||||||
|
td 昨日とのノート数の差
|
||||||
|
tr
|
||||||
|
td {followingDelta}
|
||||||
|
td 昨日とのフォロー数の差
|
||||||
|
tr
|
||||||
|
td {followersDelta}
|
||||||
|
td 昨日とのフォロワー数の差
|
||||||
|
tr
|
||||||
|
td {url}
|
||||||
|
td みす廃アラートのURL
|
||||||
|
tr
|
||||||
|
td {ranking}
|
||||||
|
td みす廃ランキングの順位
|
||||||
|
tr
|
||||||
|
td {rating}
|
||||||
|
td みす廃レート
|
||||||
|
button.primary(type="submit", tabindex=8) 保存
|
||||||
|
|
||||||
|
|
||||||
section.xd-card#settings
|
section.xd-card#settings
|
||||||
.header
|
.header
|
||||||
h1.title 操作
|
h1.title 操作
|
||||||
.body
|
.body
|
||||||
form.mb-2(action="/send", method="post"): button#send(style="display: inline-block") アラートをテスト送信
|
form.mb-2(action="/send", method="post", tabindex=9): button#send(style="display: inline-block") アラートをテスト送信
|
||||||
form.mb-2(action="/logout", method="post"): button#logout(style="display: inline-block") ログアウト
|
form.mb-2(action="/logout", method="post", tabindex=10): button#logout(style="display: inline-block") ログアウト
|
||||||
form.mb-2(action="/optout", method="post"): button.danger#optout(style="display: inline-block") アカウント連携を解除する
|
form.mb-2(action="/optout", method="post", tabindex=11): button.danger#optout(style="display: inline-block") アカウント連携を解除する
|
||||||
|
|
||||||
block script
|
block script
|
||||||
script.
|
script.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue