From 52685eb3a4510d041ba3bde2e7e8a5a5b5393f47 Mon Sep 17 00:00:00 2001 From: Xeltica Date: Wed, 6 Jan 2021 22:48:04 +0900 Subject: [PATCH] resolve #11 --- migration/1609938844427-visibility.ts | 22 +++++++ src/models/entities/user.ts | 20 ++++++ src/server/router.ts | 22 ++++++- src/services/send.ts | 8 ++- src/types/Visibility.ts | 8 +++ src/views/mypage.pug | 87 ++++++++++++++++++++++++--- 6 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 migration/1609938844427-visibility.ts create mode 100644 src/types/Visibility.ts diff --git a/migration/1609938844427-visibility.ts b/migration/1609938844427-visibility.ts new file mode 100644 index 0000000..86b5de2 --- /dev/null +++ b/migration/1609938844427-visibility.ts @@ -0,0 +1,22 @@ +import {MigrationInterface, QueryRunner} from 'typeorm'; + +export class visibility1609938844427 implements MigrationInterface { + name = 'visibility1609938844427' + + public async up(queryRunner: QueryRunner): Promise { + 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 { + 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"'); + } + +} diff --git a/src/models/entities/user.ts b/src/models/entities/user.ts index 2f3be42..3a5aa6f 100644 --- a/src/models/entities/user.ts +++ b/src/models/entities/user.ts @@ -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; } \ No newline at end of file diff --git a/src/server/router.ts b/src/server/router.ts index 64f137e..359a14c 100644 --- a/src/server/router.ts +++ b/src/server/router.ts @@ -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(); @@ -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'); }); diff --git a/src/services/send.ts b/src/services/send.ts index d28a84d..f1890a6 100644 --- a/src/services/send.ts +++ b/src/services/send.ts @@ -8,9 +8,13 @@ export const send = async (user: User): Promise => { if (user.alertMode === 'note') { console.info(`send ${user.username}@${user.host}'s misshaialert as a note`); - const res = await api>(user.host, 'notes/create', { + const opts = { text, - }, user.token); + visibility: user.visibility, + } as Record; + if (user.localOnly) opts.localOnly = user.localOnly; + if (user.remoteFollowersOnly) opts.remoteFollowersOnly = user.remoteFollowersOnly; + const res = await api>(user.host, 'notes/create', opts, user.token); if (res.error) { throw res.error || res; } diff --git a/src/types/Visibility.ts b/src/types/Visibility.ts new file mode 100644 index 0000000..7164daa --- /dev/null +++ b/src/types/Visibility.ts @@ -0,0 +1,8 @@ +export const visibilities = [ + 'public', // パブリック + 'home', // ホーム + 'followers', // フォロワー + 'users' // ログインユーザー (Groundpolis 限定) +] as const; + +export type Visibility = typeof visibilities[number]; \ No newline at end of file diff --git a/src/views/mypage.pug b/src/views/mypage.pug index 9328e47..0b774db 100644 --- a/src/views/mypage.pug +++ b/src/views/mypage.pug @@ -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 + | フラグ
+ label + input(type="radio", name="flag", value="none", checked=!user.localOnly && !user.remoteFollowersOnly, tabindex=3) + | なし(標準)
+ label + input(type="radio", name="flag", value="localOnly", checked=user.localOnly, tabindex=4) + | ローカルのみ
+ if isGroundpolis + label + input(type="radio", name="flag", value="remoteFollowersOnly", checked=user.remoteFollowersOnly, tabindex=5) + | リモートフォロワーとローカル
+ 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.