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 { AlertMode, alertModes } from '../../types/AlertMode';
|
||||
import { visibilities, Visibility } from '../../types/Visibility';
|
||||
|
||||
@Entity()
|
||||
@Index([ 'username', 'host' ], { unique: true })
|
||||
|
@ -52,4 +53,23 @@ export class User {
|
|||
default: 'notification'
|
||||
})
|
||||
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 { Users } from '../models';
|
||||
import { send } from '../services/send';
|
||||
import { visibilities, Visibility } from '../types/Visibility';
|
||||
|
||||
export const router = new Router<DefaultState, Context>();
|
||||
|
||||
|
@ -59,9 +60,13 @@ router.get('/', async ctx => {
|
|||
const user = token ? await getUserByMisshaiToken(token) : undefined;
|
||||
|
||||
const isAvailable = user && await apiAvailable(user.host, user.token);
|
||||
|
||||
if (user && isAvailable) {
|
||||
const meta = await api<{ version: string }>(user?.host, 'meta', {});
|
||||
await ctx.render('mypage', {
|
||||
user,
|
||||
// To Activate Groundpolis Mode
|
||||
isGroundpolis: meta.version.includes('gp'),
|
||||
usersCount: await getUserCount(),
|
||||
score: await getScores(user),
|
||||
from: ctx.query.from,
|
||||
|
@ -195,13 +200,21 @@ router.post('/update-settings', async ctx => {
|
|||
await die(ctx, `${mode} is an invalid value`);
|
||||
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');
|
||||
if (!token) {
|
||||
await die(ctx, 'ログインしていません');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const u = await getUserByMisshaiToken(token);
|
||||
|
||||
if (!u) {
|
||||
|
@ -209,7 +222,12 @@ router.post('/update-settings', async ctx => {
|
|||
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');
|
||||
});
|
||||
|
|
|
@ -8,9 +8,13 @@ export const send = async (user: User): Promise<void> => {
|
|||
|
||||
if (user.alertMode === '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,
|
||||
}, 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) {
|
||||
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];
|
|
@ -15,7 +15,7 @@ block content
|
|||
.xd-alert.my-2
|
||||
i.icon.fas.fa-thumbs-up
|
||||
strong テスト送信しました。
|
||||
|
||||
|
||||
if isGroundpolis
|
||||
.xd-alert.my-2
|
||||
i.icon.fas.fa-meteor
|
||||
|
@ -49,6 +49,19 @@ block content
|
|||
td !{score.followersDelta}
|
||||
|
||||
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
|
||||
h1.title 設定
|
||||
.body
|
||||
|
@ -57,21 +70,75 @@ block content
|
|||
| スコア通知方法に「Misskey に通知」を選んでいる場合、Groundpolis v3 および Misskey v12 の最新版以外では動作しません。めいすきーや古いバージョンをお使いの方は、「自動的にノートを投稿」をお使いください。
|
||||
form(method="post", action="/update-settings")
|
||||
p: label スコア通知方法:
|
||||
select(name="alertMode")
|
||||
option(value="note", selected=user.alertMode === 'note') 自動的にノートを投稿 (標準)
|
||||
option(value="notification", selected=user.alertMode === 'notification') Misskey に通知
|
||||
option(value="nothing", selected=user.alertMode === 'nothing') 通知しない
|
||||
p: label タイムゾーン:(coming soon)
|
||||
button.primary(type="submit") 保存
|
||||
select(name="alertMode", tabindex=1)
|
||||
each set in alertModes
|
||||
option(value=set[0], selected=(user.alertMode === set[0]))= set[1]
|
||||
p: label 公開範囲:
|
||||
select(name="visibility", tabindex=2)
|
||||
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
|
||||
.header
|
||||
h1.title 操作
|
||||
.body
|
||||
form.mb-2(action="/send", method="post"): button#send(style="display: inline-block") アラートをテスト送信
|
||||
form.mb-2(action="/logout", method="post"): button#logout(style="display: inline-block") ログアウト
|
||||
form.mb-2(action="/optout", method="post"): button.danger#optout(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", tabindex=10): button#logout(style="display: inline-block") ログアウト
|
||||
form.mb-2(action="/optout", method="post", tabindex=11): button.danger#optout(style="display: inline-block") アカウント連携を解除する
|
||||
|
||||
block script
|
||||
script.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue