From b50587461376b242d8c44ab1e4f350f68a041c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Tue, 14 Jul 2020 18:00:09 +0900 Subject: [PATCH 01/39] Update README.md [AUTOGEN] --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index aacdfee96c..263e7b44ca 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). Roujo Oliver Maximilian Seidel weepjp -Jon Leibowitz kiritan みなしま Eduardo Quiros @@ -120,7 +119,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). Roujo Oliver Maximilian Seidel weepjp -Jon Leibowitz kiritan みなしま Eduardo Quiros @@ -135,7 +133,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). Nie(sha) osapon 見当かなみ -YuzuRyo61 +Wataru Manji (manji0) Nesakko Demogrognard @@ -146,9 +144,10 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). Nie(sha) osapon 見当かなみ -YuzuRyo61 +Wataru Manji (manji0) + @@ -156,8 +155,8 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). - + @@ -165,50 +164,51 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). -
YuzuRyo61 mewl hayabusa S Y Takumi SugitaYUKIMOCHI totokoro sheeta.s motcha
YuzuRyo61 mewl hayabusa S Y Takumi SugitaYUKIMOCHI totokoro sheeta.s motcha
+ - - + - + - - + - + -
motcha axtuki1 Satsuki Yanagi takimura aqz tamaina012 nafuchoco Atsuko Tominaganatalie natalie EBISUME noellabo CG Hekovic
motcha axtuki1 Satsuki Yanagi takimura aqz tamaina012 nafuchoco nafuchoco Atsuko Tominaganatalie natalie EBISUME noellabo CG Hekovic
+ + + +
Hekovic uroco @99Chandler Nokotaro Takeda nenohi Efertone Takashi Shibuya
Hekovic uroco @99Chandler Nokotaro Takeda nenohi Efertone Takashi Shibuya
-**Last updated:** Tue, 02 Jun 2020 00:00:08 UTC +**Last updated:** Tue, 14 Jul 2020 09:00:09 UTC [backer-url]: #backers From e1f2e364a4347a8da78a32ed741c789a288d3957 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 25 Jul 2020 12:23:49 +0900 Subject: [PATCH 02/39] fix(client): Fix federation widget --- src/client/widgets/federation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/widgets/federation.vue b/src/client/widgets/federation.vue index b99ef1b0aa..0f75da5895 100644 --- a/src/client/widgets/federation.vue +++ b/src/client/widgets/federation.vue @@ -10,7 +10,7 @@ #{{ instance.host }}

{{ instance.softwareName }} {{ instance.softwareVersion }}

- + From bd54e44b35f7aeae8766054322e2908881323041 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 25 Jul 2020 16:31:21 +0900 Subject: [PATCH 03/39] feat(client): Implement federation widget chart --- .../mini-chart.vue} | 0 src/client/widgets/federation.vue | 20 ++++++++++--------- src/client/widgets/trends.vue | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) rename src/client/{widgets/trends.chart.vue => components/mini-chart.vue} (100%) diff --git a/src/client/widgets/trends.chart.vue b/src/client/components/mini-chart.vue similarity index 100% rename from src/client/widgets/trends.chart.vue rename to src/client/components/mini-chart.vue diff --git a/src/client/widgets/federation.vue b/src/client/widgets/federation.vue index 0f75da5895..02381116e3 100644 --- a/src/client/widgets/federation.vue +++ b/src/client/widgets/federation.vue @@ -5,12 +5,12 @@
-
+
#{{ instance.host }}

{{ instance.softwareName }} {{ instance.softwareVersion }}

- +
@@ -21,7 +21,7 @@ import { faGlobe } from '@fortawesome/free-solid-svg-icons'; import MkContainer from '../components/ui/container.vue'; import define from './define'; -import XChart from './trends.chart.vue'; +import MkMiniChart from '../components/mini-chart.vue'; export default define({ name: 'federation', @@ -33,11 +33,12 @@ export default define({ }) }).extend({ components: { - MkContainer, XChart + MkContainer, MkMiniChart }, data() { return { instances: [], + charts: [], fetching: true, faGlobe }; @@ -50,14 +51,15 @@ export default define({ clearInterval(this.clock); }, methods: { - fetch() { - this.$root.api('federation/instances', { + async fetch() { + const instances = await this.$root.api('federation/instances', { sort: '+lastCommunicatedAt', limit: 5 - }).then(instances => { - this.instances = instances; - this.fetching = false; }); + const charts = await Promise.all(instances.map(i => this.$root.api('charts/instance', { host: i.host, limit: 16, span: 'hour' }))); + this.instances = instances; + this.charts = charts; + this.fetching = false; } } }); diff --git a/src/client/widgets/trends.vue b/src/client/widgets/trends.vue index d4a4b2d289..b439f91d54 100644 --- a/src/client/widgets/trends.vue +++ b/src/client/widgets/trends.vue @@ -10,7 +10,7 @@ #{{ stat.tag }}

{{ $t('nUsersMentioned', { n: stat.usersCount }) }}

- + @@ -21,7 +21,7 @@ import { faHashtag } from '@fortawesome/free-solid-svg-icons'; import MkContainer from '../components/ui/container.vue'; import define from './define'; -import XChart from './trends.chart.vue'; +import MkMiniChart from '../components/mini-chart.vue'; export default define({ name: 'hashtags', @@ -33,7 +33,7 @@ export default define({ }) }).extend({ components: { - MkContainer, XChart + MkContainer, MkMiniChart }, data() { return { From 58211fc6a72536b066bd8a78fb4bb083cfc1051a Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 25 Jul 2020 16:37:08 +0900 Subject: [PATCH 04/39] fix(client): Remove unncessary # --- src/client/widgets/federation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/widgets/federation.vue b/src/client/widgets/federation.vue index 02381116e3..8787050774 100644 --- a/src/client/widgets/federation.vue +++ b/src/client/widgets/federation.vue @@ -7,7 +7,7 @@
- #{{ instance.host }} + {{ instance.host }}

{{ instance.softwareName }} {{ instance.softwareVersion }}

From e5863c2867c1ee8d0d6f2257de7f7fc7791cf8a6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 25 Jul 2020 21:01:14 +0900 Subject: [PATCH 05/39] chore(client): Show ? when softwareName is unknown --- src/client/widgets/federation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/widgets/federation.vue b/src/client/widgets/federation.vue index 8787050774..585efb7781 100644 --- a/src/client/widgets/federation.vue +++ b/src/client/widgets/federation.vue @@ -8,7 +8,7 @@
{{ instance.host }} -

{{ instance.softwareName }} {{ instance.softwareVersion }}

+

{{ instance.softwareName || '?' }} {{ instance.softwareVersion }}

From 4a1552fb3cc4e4ab522ba2d745ce3b790cb187e6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Jul 2020 00:16:00 +0900 Subject: [PATCH 06/39] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9612d6e2bd..712491a175 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,10 @@ # Contribution guide :v: Thanks for your contributions :v: +## When you contribute... +- 任意のIssueについて、せっかく実装してくださっても、実装方法や設計の認識が揃ってないとマージできない/しないことになりかねないので、初めにそのIssue上で着手することを宣言し、必要であれば他メンバーに実装方法や設計の判断を仰いでください。宣言することは作業が他の人と被るのを防止する効果もあります。 +- 時間や優先度の都合上、提出してくださったPRが長期間放置されることもありますがご理解ください。 + ## Issues Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues . From cf9266eab9a987ccfa2308090d06a96314fca2a4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Jul 2020 01:58:44 +0900 Subject: [PATCH 07/39] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 712491a175..2ffe6dc2e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,10 @@ :v: Thanks for your contributions :v: ## When you contribute... -- 任意のIssueについて、せっかく実装してくださっても、実装方法や設計の認識が揃ってないとマージできない/しないことになりかねないので、初めにそのIssue上で着手することを宣言し、必要であれば他メンバーに実装方法や設計の判断を仰いでください。宣言することは作業が他の人と被るのを防止する効果もあります。 +- 任意のIssueについて、せっかく実装してくださっても、実装方法や設計の認識が揃ってないとマージできない/しないことになりかねないので、初めにそのIssue上で着手することを宣言し、必要に応じて他メンバーと実装方法や設計のすり合わせを行ってください。宣言することは作業が他の人と被るのを防止する効果もあります。 + - 設計に迷った時はプロジェクトリーダーの判断を仰いでください。 - 時間や優先度の都合上、提出してくださったPRが長期間放置されることもありますがご理解ください。 + - 温度感高めで見てほしいものは責付いてください。 ## Issues Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues . From f1ef85b63625edccd717dda8324078c232c7750e Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Jul 2020 11:04:07 +0900 Subject: [PATCH 08/39] feat(server): Fetch icon url of an instance (#6591) * feat(server): Fetch icon url of an instance Resolve #6589 * chore: Rename the function --- migration/1595676934834-instance-icon-url.ts | 14 ++ src/misc/app-lock.ts | 4 +- src/misc/fetch.ts | 21 +++ src/models/entities/instance.ts | 5 + src/queue/processors/deliver.ts | 4 +- src/queue/processors/inbox.ts | 4 +- src/remote/activitypub/models/person.ts | 4 +- src/services/fetch-instance-metadata.ts | 135 +++++++++++++++++++ src/services/fetch-nodeinfo.ts | 72 ---------- 9 files changed, 183 insertions(+), 80 deletions(-) create mode 100644 migration/1595676934834-instance-icon-url.ts create mode 100644 src/services/fetch-instance-metadata.ts delete mode 100644 src/services/fetch-nodeinfo.ts diff --git a/migration/1595676934834-instance-icon-url.ts b/migration/1595676934834-instance-icon-url.ts new file mode 100644 index 0000000000..c75370f174 --- /dev/null +++ b/migration/1595676934834-instance-icon-url.ts @@ -0,0 +1,14 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class instanceIconUrl1595676934834 implements MigrationInterface { + name = 'instanceIconUrl1595676934834' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "instance" ADD "iconUrl" character varying(256) DEFAULT null`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "iconUrl"`); + } + +} diff --git a/src/misc/app-lock.ts b/src/misc/app-lock.ts index ca2181f879..847299b46d 100644 --- a/src/misc/app-lock.ts +++ b/src/misc/app-lock.ts @@ -21,8 +21,8 @@ export function getApLock(uri: string, timeout = 30 * 1000) { return lock(`ap-object:${uri}`, timeout); } -export function getNodeinfoLock(host: string, timeout = 30 * 1000) { - return lock(`nodeinfo:${host}`, timeout); +export function getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000) { + return lock(`instance:${host}`, timeout); } export function getChartInsertLock(lockKey: string, timeout = 30 * 1000) { diff --git a/src/misc/fetch.ts b/src/misc/fetch.ts index 358bc25030..7be0e53fd4 100644 --- a/src/misc/fetch.ts +++ b/src/misc/fetch.ts @@ -27,6 +27,27 @@ export async function getJson(url: string, accept = 'application/json, */*', tim return await res.json(); } +export async function getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: HeadersInit) { + const res = await fetch(url, { + headers: Object.assign({ + 'User-Agent': config.userAgent, + Accept: accept + }, headers || {}), + timeout, + agent: getAgentByUrl, + }); + + if (!res.ok) { + throw { + name: `StatusError`, + statusCode: res.status, + message: `${res.status} ${res.statusText}`, + }; + } + + return await res.text(); +} + /** * Get http non-proxy agent */ diff --git a/src/models/entities/instance.ts b/src/models/entities/instance.ts index fe620887d2..5fedfc0956 100644 --- a/src/models/entities/instance.ts +++ b/src/models/entities/instance.ts @@ -158,6 +158,11 @@ export class Instance { }) public maintainerEmail: string | null; + @Column('varchar', { + length: 256, nullable: true, default: null, + }) + public iconUrl: string | null; + @Column('timestamp with time zone', { nullable: true, }) diff --git a/src/queue/processors/deliver.ts b/src/queue/processors/deliver.ts index 16b2f6e29a..cb7587ef81 100644 --- a/src/queue/processors/deliver.ts +++ b/src/queue/processors/deliver.ts @@ -4,7 +4,7 @@ import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-ins import Logger from '../../services/logger'; import { Instances } from '../../models'; import { instanceChart } from '../../services/chart'; -import { fetchNodeinfo } from '../../services/fetch-nodeinfo'; +import { fetchInstanceMetadata } from '../../services/fetch-instance-metadata'; import { fetchMeta } from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; @@ -48,7 +48,7 @@ export default async (job: Bull.Job) => { isNotResponding: false }); - fetchNodeinfo(i); + fetchInstanceMetadata(i); instanceChart.requestSent(i.host, true); }); diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts index 1d35079e9d..b4e8b85a46 100644 --- a/src/queue/processors/inbox.ts +++ b/src/queue/processors/inbox.ts @@ -8,7 +8,7 @@ import { instanceChart } from '../../services/chart'; import { fetchMeta } from '../../misc/fetch-meta'; import { toPuny, extractDbHost } from '../../misc/convert-host'; import { getApId } from '../../remote/activitypub/type'; -import { fetchNodeinfo } from '../../services/fetch-nodeinfo'; +import { fetchInstanceMetadata } from '../../services/fetch-instance-metadata'; import { InboxJobData } from '..'; import DbResolver from '../../remote/activitypub/db-resolver'; import { resolvePerson } from '../../remote/activitypub/models/person'; @@ -126,7 +126,7 @@ export default async (job: Bull.Job): Promise => { isNotResponding: false }); - fetchNodeinfo(i); + fetchInstanceMetadata(i); instanceChart.requestReceived(i.host); }); diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index a213abf474..5213f872ec 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -26,7 +26,7 @@ import { validActor } from '../../../remote/activitypub/type'; import { getConnection } from 'typeorm'; import { ensure } from '../../../prelude/ensure'; import { toArray } from '../../../prelude/array'; -import { fetchNodeinfo } from '../../../services/fetch-nodeinfo'; +import { fetchInstanceMetadata } from '../../../services/fetch-instance-metadata'; const logger = apLogger; @@ -204,7 +204,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { Instances.increment({ id: i.id }, 'usersCount', 1); instanceChart.newUser(i.host); - fetchNodeinfo(i); + fetchInstanceMetadata(i); }); usersChart.update(user!, true); diff --git a/src/services/fetch-instance-metadata.ts b/src/services/fetch-instance-metadata.ts new file mode 100644 index 0000000000..41fef859c9 --- /dev/null +++ b/src/services/fetch-instance-metadata.ts @@ -0,0 +1,135 @@ +import { JSDOM } from 'jsdom'; +import fetch from 'node-fetch'; +import { getJson, getHtml, getAgentByUrl } from '../misc/fetch'; +import { Instance } from '../models/entities/instance'; +import { Instances } from '../models'; +import { getFetchInstanceMetadataLock } from '../misc/app-lock'; +import Logger from './logger'; +import { URL } from 'url'; + +const logger = new Logger('metadata', 'cyan'); + +export async function fetchInstanceMetadata(instance: Instance): Promise { + const unlock = await getFetchInstanceMetadataLock(instance.host); + + const _instance = await Instances.findOne({ host: instance.host }); + const now = Date.now(); + if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) { + unlock(); + return; + } + + logger.info(`Fetching metadata of ${instance.host} ...`); + + try { + const [info, icon] = await Promise.all([ + fetchNodeinfo(instance).catch(() => null), + fetchIconUrl(instance).catch(() => null), + ]); + + logger.succ(`Successfuly fetched metadata of ${instance.host}`); + + const updates = { + infoUpdatedAt: new Date(), + } as Record; + + if (info) { + updates.softwareName = info.software.name.toLowerCase(); + updates.softwareVersion = info.software.version; + updates.openRegistrations = info.openRegistrations; + updates.name = info.metadata ? (info.metadata.nodeName || info.metadata.name || null) : null; + updates.description = info.metadata ? (info.metadata.nodeDescription || info.metadata.description || null) : null; + updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name || null) : null : null; + updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email || null) : null : null; + } + + if (icon) { + updates.iconUrl = icon; + } + + await Instances.update(instance.id, updates); + + logger.succ(`Successfuly updated metadata of ${instance.host}`); + } catch (e) { + logger.error(`Failed to update metadata of ${instance.host}: ${e}`); + } finally { + unlock(); + } +} + +async function fetchNodeinfo(instance: Instance): Promise> { + logger.info(`Fetching nodeinfo of ${instance.host} ...`); + + try { + const wellknown = await getJson('https://' + instance.host + '/.well-known/nodeinfo') + .catch(e => { + if (e.statusCode === 404) { + throw 'No nodeinfo provided'; + } else { + throw e.statusCode || e.message; + } + }); + + if (wellknown.links == null || !Array.isArray(wellknown.links)) { + throw 'No wellknown links'; + } + + const links = wellknown.links as any[]; + + const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); + const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); + const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); + const link = lnik2_1 || lnik2_0 || lnik1_0; + + if (link == null) { + throw 'No nodeinfo link provided'; + } + + const info = await getJson(link.href) + .catch(e => { + throw e.statusCode || e.message; + }); + + logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); + + return info; + } catch (e) { + logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`); + + throw e; + } +} + +async function fetchIconUrl(instance: Instance): Promise { + logger.info(`Fetching icon URL of ${instance.host} ...`); + + const url = 'https://' + instance.host; + + const html = await getHtml(url); + + const { window } = new JSDOM(html); + const doc = window.document; + + const hrefAppleTouchIconPrecomposed = doc.querySelector('link[rel="apple-touch-icon-precomposed"]')?.getAttribute('href'); + const hrefAppleTouchIcon = doc.querySelector('link[rel="apple-touch-icon"]')?.getAttribute('href'); + const hrefIcon = doc.querySelector('link[rel="icon"]')?.getAttribute('href'); + + const href = hrefAppleTouchIconPrecomposed || hrefAppleTouchIcon || hrefIcon; + + if (href) { + return (new URL(href, url)).href; + } + + const faviconUrl = url + '/favicon.ico'; + + const favicon = await fetch(faviconUrl, { + timeout: 10000, + agent: getAgentByUrl, + }); + + if (favicon.ok) { + return faviconUrl; + } + + return null; +} diff --git a/src/services/fetch-nodeinfo.ts b/src/services/fetch-nodeinfo.ts deleted file mode 100644 index 0cf51e3377..0000000000 --- a/src/services/fetch-nodeinfo.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { getJson } from '../misc/fetch'; -import { Instance } from '../models/entities/instance'; -import { Instances } from '../models'; -import { getNodeinfoLock } from '../misc/app-lock'; -import Logger from '../services/logger'; - -export const logger = new Logger('nodeinfo', 'cyan'); - -export async function fetchNodeinfo(instance: Instance) { - const unlock = await getNodeinfoLock(instance.host); - - const _instance = await Instances.findOne({ host: instance.host }); - const now = Date.now(); - if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) { - unlock(); - return; - } - - logger.info(`Fetching nodeinfo of ${instance.host} ...`); - - try { - const wellknown = await getJson('https://' + instance.host + '/.well-known/nodeinfo') - .catch(e => { - if (e.statusCode === 404) { - throw 'No nodeinfo provided'; - } else { - throw e.statusCode || e.message; - } - }); - - if (wellknown.links == null || !Array.isArray(wellknown.links)) { - throw 'No wellknown links'; - } - - const links = wellknown.links as any[]; - - const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); - const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); - const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); - const link = lnik2_1 || lnik2_0 || lnik1_0; - - if (link == null) { - throw 'No nodeinfo link provided'; - } - - const info = await getJson(link.href) - .catch(e => { - throw e.statusCode || e.message; - }); - - await Instances.update(instance.id, { - infoUpdatedAt: new Date(), - softwareName: info.software.name.toLowerCase(), - softwareVersion: info.software.version, - openRegistrations: info.openRegistrations, - name: info.metadata ? (info.metadata.nodeName || info.metadata.name || null) : null, - description: info.metadata ? (info.metadata.nodeDescription || info.metadata.description || null) : null, - maintainerName: info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name || null) : null : null, - maintainerEmail: info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email || null) : null : null, - }); - - logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); - } catch (e) { - logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`); - - await Instances.update(instance.id, { - infoUpdatedAt: new Date(), - }); - } finally { - unlock(); - } -} From 4feccdfd92e6191a6d04320c4ea5f0632f69ba97 Mon Sep 17 00:00:00 2001 From: Xeltica <7106976+Xeltica@users.noreply.github.com> Date: Sun, 26 Jul 2020 11:05:26 +0900 Subject: [PATCH 09/39] =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E8=A8=AD=E5=AE=9A=E3=81=AE=E4=B8=8D=E8=B6=B3?= =?UTF-8?q?=E5=88=86=E3=82=92=E8=BF=BD=E5=8A=A0=20(#6576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * インスタンス設定の不足分を追加 * fix bug * Update ja-JP.yml * Update settings.vue * Update settings.vue Co-authored-by: syuilo --- locales/ja-JP.yml | 14 +++++ src/client/pages/instance/settings.vue | 84 +++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ec767aafa9..478fe1311e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -535,6 +535,20 @@ enableAll: "全て有効にする" disableAll: "全て無効にする" tokenRequested: "アカウントへのアクセス許可" pluginTokenRequestedDescription: "このプラグインはここで設定した権限を行使できるようになります。" +useStarForReactionFallback: "リアクション絵文字が不明な場合、代わりに★を使う" +emailConfig: "メールサーバー設定" +enableEmail: "メール配信機能を有効化する" +emailConfigInfo: "メールアドレスの確認やパスワードリセットの際に使います" +email: "メールアドレス" +smtpConfig: "SMTP サーバーの設定" +smtpHost: "ホスト" +smtpPort: "ポート" +smtpUser: "ユーザー名" +smtpPass: "パスワード" +emptyToDisableSmtpAuth: "ユーザー名とパスワードを空欄にすることで、SMTP認証を無効化出来ます" +smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する" +smtpSecureInfo: "STARTTLS使用時はオフにします。" +testEmail: "配信テスト" _theme: explore: "テーマを探す" diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue index 0436e87804..dfd6cc6d4f 100644 --- a/src/client/pages/instance/settings.vue +++ b/src/client/pages/instance/settings.vue @@ -28,6 +28,9 @@ {{ $t('enableGlobalTimeline') }} {{ $t('disablingTimelinesInfo') }}
+
+ {{ $t('useStarForReactionFallback') }} +
@@ -74,6 +77,29 @@
+
+
{{ $t('emailConfig') }}
+
+ {{ $t('enableEmail') }} + {{ $t('email') }} +
{{ $t('smtpConfig') }}
+
+ {{ $t('smtpHost') }} + {{ $t('smtpPort') }} +
+
+ {{ $t('smtpUser') }} + {{ $t('smtpPass') }} +
+ {{ $t('emptyToDisableSmtpAuth') }} + {{ $t('smtpSecure') }} +
+ {{ $t('testEmail') }} + {{ $t('save') }} +
+
+
+
{{ $t('serviceworker') }}
@@ -195,12 +221,19 @@ {{ $t('save') }}
+
+
Summaly Proxy
+
+ URL + {{ $t('save') }} +
+
+ + diff --git a/src/client/pages/my-settings/index.vue b/src/client/pages/my-settings/index.vue index 3af896d78e..16e786bfc8 100644 --- a/src/client/pages/my-settings/index.vue +++ b/src/client/pages/my-settings/index.vue @@ -27,6 +27,7 @@ + @@ -47,6 +48,7 @@ import XImportExport from './import-export.vue'; import XDrive from './drive.vue'; import XReactionSetting from './reaction.vue'; import XMuteBlock from './mute-block.vue'; +import XWordMute from './word-mute.vue'; import XSecurity from './security.vue'; import X2fa from './2fa.vue'; import XIntegration from './integration.vue'; @@ -68,6 +70,7 @@ export default Vue.extend({ XDrive, XReactionSetting, XMuteBlock, + XWordMute, XSecurity, X2fa, XIntegration, diff --git a/src/client/pages/my-settings/word-mute.vue b/src/client/pages/my-settings/word-mute.vue new file mode 100644 index 0000000000..6b2a372f0b --- /dev/null +++ b/src/client/pages/my-settings/word-mute.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/client/scripts/check-word-mute.ts b/src/client/scripts/check-word-mute.ts new file mode 100644 index 0000000000..3b1fa75b1e --- /dev/null +++ b/src/client/scripts/check-word-mute.ts @@ -0,0 +1,26 @@ +export async function checkWordMute(note: Record, me: Record | null | undefined, mutedWords: string[][]): Promise { + // 自分自身 + if (me && (note.userId === me.id)) return false; + + const words = mutedWords + // Clean up + .map(xs => xs.filter(x => x !== '')) + .filter(xs => xs.length > 0); + + if (words.length > 0) { + if (note.text == null) return false; + + const matched = words.some(and => + and.every(keyword => { + const regexp = keyword.match(/^\/(.+)\/(.*)$/); + if (regexp) { + return new RegExp(regexp[1], regexp[2]).test(note.text!); + } + return note.text!.includes(keyword); + })); + + if (matched) return true; + } + + return false; +} diff --git a/src/client/store.ts b/src/client/store.ts index 2cd2c8cf3c..2bf44088af 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -18,6 +18,7 @@ export const defaultSettings = { pastedFileName: 'yyyy-MM-dd HH-mm-ss [{{number}}]', memo: null, reactions: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], + mutedWords: [], }; export const defaultDeviceUserSettings = { diff --git a/src/client/style.scss b/src/client/style.scss index c3d3cf2233..ab0dcf6220 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -355,6 +355,10 @@ hr { padding: 16px; } + &._noPad { + padding: 0 !important; + } + & + ._content { border-top: solid 1px var(--divider); } diff --git a/src/db/postgre.ts b/src/db/postgre.ts index 81fb92f684..6ffc56ee08 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -59,6 +59,7 @@ import { PromoNote } from '../models/entities/promo-note'; import { PromoRead } from '../models/entities/promo-read'; import { program } from '../argv'; import { Relay } from '../models/entities/relay'; +import { MutedNote } from '../models/entities/muted-note'; const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); @@ -151,6 +152,7 @@ export const entities = [ ReversiGame, ReversiMatching, Relay, + MutedNote, ...charts as any ]; diff --git a/src/misc/check-word-mute.ts b/src/misc/check-word-mute.ts new file mode 100644 index 0000000000..5af267d75d --- /dev/null +++ b/src/misc/check-word-mute.ts @@ -0,0 +1,39 @@ +const RE2 = require('re2'); +import { Note } from '../models/entities/note'; +import { User } from '../models/entities/user'; + +type NoteLike = { + userId: Note['userId']; + text: Note['text']; +}; + +type UserLike = { + id: User['id']; +}; + +export async function checkWordMute(note: NoteLike, me: UserLike | null | undefined, mutedWords: string[][]): Promise { + // 自分自身 + if (me && (note.userId === me.id)) return false; + + const words = mutedWords + // Clean up + .map(xs => xs.filter(x => x !== '')) + .filter(xs => xs.length > 0); + + if (words.length > 0) { + if (note.text == null) return false; + + const matched = words.some(and => + and.every(keyword => { + const regexp = keyword.match(/^\/(.+)\/(.*)$/); + if (regexp) { + return new RE2(regexp[1], regexp[2]).test(note.text!); + } + return note.text!.includes(keyword); + })); + + if (matched) return true; + } + + return false; +} diff --git a/src/models/entities/muted-note.ts b/src/models/entities/muted-note.ts new file mode 100644 index 0000000000..521876688c --- /dev/null +++ b/src/models/entities/muted-note.ts @@ -0,0 +1,48 @@ +import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Note } from './note'; +import { User } from './user'; +import { id } from '../id'; +import { mutedNoteReasons } from '../../types'; + +@Entity() +@Index(['noteId', 'userId'], { unique: true }) +export class MutedNote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + comment: 'The note ID.' + }) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; + + @Index() + @Column({ + ...id(), + comment: 'The user ID.' + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + /** + * ミュートされた理由。 + */ + @Index() + @Column('enum', { + enum: mutedNoteReasons, + comment: 'The reason of the MutedNote.' + }) + public reason: typeof mutedNoteReasons[number]; +} diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts index a89d7364f3..0a6722aace 100644 --- a/src/models/entities/user-profile.ts +++ b/src/models/entities/user-profile.ts @@ -147,6 +147,17 @@ export class UserProfile { }) public integrations: Record; + @Index() + @Column('boolean', { + default: false, + }) + public enableWordMute: boolean; + + @Column('jsonb', { + default: [] + }) + public mutedWords: string[][]; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/src/models/index.ts b/src/models/index.ts index e1389e7353..e58d8b551d 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -53,6 +53,7 @@ import { PromoNote } from './entities/promo-note'; import { PromoRead } from './entities/promo-read'; import { EmojiRepository } from './repositories/emoji'; import { RelayRepository } from './repositories/relay'; +import { MutedNote } from './entities/muted-note'; export const Announcements = getRepository(Announcement); export const AnnouncementReads = getRepository(AnnouncementRead); @@ -108,3 +109,4 @@ export const AntennaNotes = getRepository(AntennaNote); export const PromoNotes = getRepository(PromoNote); export const PromoReads = getRepository(PromoRead); export const Relays = getCustomRepository(RelayRepository); +export const MutedNotes = getRepository(MutedNote); diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index bbaafc9050..955a70ee60 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -239,6 +239,7 @@ export class UserRepository extends Repository { hasUnreadNotification: this.getHasUnreadNotification(user.id), hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), integrations: profile!.integrations, + mutedWords: profile!.mutedWords, } : {}), ...(opts.includeSecrets ? { diff --git a/src/server/api/common/generate-muted-note-query.ts b/src/server/api/common/generate-muted-note-query.ts new file mode 100644 index 0000000000..498930476c --- /dev/null +++ b/src/server/api/common/generate-muted-note-query.ts @@ -0,0 +1,13 @@ +import { User } from '../../../models/entities/user'; +import { MutedNotes } from '../../../models'; +import { SelectQueryBuilder } from 'typeorm'; + +export function generateMutedNoteQuery(q: SelectQueryBuilder, me: User) { + const mutedQuery = MutedNotes.createQueryBuilder('muted') + .select('muted.noteId') + .where('muted.userId = :userId', { userId: me.id }); + + q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); + + q.setParameters(mutedQuery.getParameters()); +} diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 48b5e48fc2..e1889df22d 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -142,7 +142,11 @@ export const meta = { desc: { 'ja-JP': 'ピン留めするページID' } - } + }, + + mutedWords: { + validator: $.optional.arr($.arr($.str)) + }, }, errors: { @@ -193,6 +197,10 @@ export default define(meta, async (ps, user, token) => { if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; + if (ps.mutedWords !== undefined) { + profileUpdates.mutedWords = ps.mutedWords; + profileUpdates.enableWordMute = ps.mutedWords.length > 0; + } if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 26b0cb0f5a..4361b8a299 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -10,6 +10,7 @@ import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; import { injectPromo } from '../../common/inject-promo'; import { injectFeatured } from '../../common/inject-featured'; +import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; export const meta = { desc: { @@ -83,6 +84,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); if (user) generateMuteQuery(query, user); + if (user) generateMutedNoteQuery(query, user); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index b0a73d1d7d..82199e607e 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -12,6 +12,7 @@ import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; import { injectPromo } from '../../common/inject-promo'; import { injectFeatured } from '../../common/inject-featured'; +import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; export const meta = { desc: { @@ -133,6 +134,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); generateMuteQuery(query, user); + generateMutedNoteQuery(query, user); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index a74dc3b15c..9d51b3b48b 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -12,6 +12,7 @@ import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; import { injectPromo } from '../../common/inject-promo'; import { injectFeatured } from '../../common/inject-featured'; +import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; export const meta = { desc: { @@ -101,6 +102,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); if (user) generateMuteQuery(query, user); + if (user) generateMutedNoteQuery(query, user); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index d60136a9ca..c6929f4a51 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -10,6 +10,7 @@ import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; import { injectPromo } from '../../common/inject-promo'; import { injectFeatured } from '../../common/inject-featured'; +import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; export const meta = { desc: { @@ -126,6 +127,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); generateMuteQuery(query, user); + generateMutedNoteQuery(query, user); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { diff --git a/src/server/api/stream/channel.ts b/src/server/api/stream/channel.ts index 18fa651820..82a95ad3d7 100644 --- a/src/server/api/stream/channel.ts +++ b/src/server/api/stream/channel.ts @@ -15,6 +15,10 @@ export default abstract class Channel { return this.connection.user; } + protected get userProfile() { + return this.connection.userProfile; + } + protected get following() { return this.connection.following; } diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts index a3ecf8e706..39800fa775 100644 --- a/src/server/api/stream/channels/global-timeline.ts +++ b/src/server/api/stream/channels/global-timeline.ts @@ -4,6 +4,7 @@ import Channel from '../channel'; import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; import { PackedNote } from '../../../../models/repositories/note'; +import { checkWordMute } from '../../../../misc/check-word-mute'; export default class extends Channel { public readonly chName = 'globalTimeline'; @@ -47,6 +48,13 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (shouldMuteThisNote(note, this.muting)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する + // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) + // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 + // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 + // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.send('note', note); } diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts index 3cf57c294c..8504d4547b 100644 --- a/src/server/api/stream/channels/home-timeline.ts +++ b/src/server/api/stream/channels/home-timeline.ts @@ -3,6 +3,7 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import Channel from '../channel'; import { Notes } from '../../../../models'; import { PackedNote } from '../../../../models/repositories/note'; +import { checkWordMute } from '../../../../misc/check-word-mute'; export default class extends Channel { public readonly chName = 'homeTimeline'; @@ -52,6 +53,13 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (shouldMuteThisNote(note, this.muting)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する + // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) + // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 + // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 + // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.send('note', note); } diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts index 40686f4b28..bc491934ea 100644 --- a/src/server/api/stream/channels/hybrid-timeline.ts +++ b/src/server/api/stream/channels/hybrid-timeline.ts @@ -5,6 +5,7 @@ import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; import { PackedNote } from '../../../../models/repositories/note'; import { PackedUser } from '../../../../models/repositories/user'; +import { checkWordMute } from '../../../../misc/check-word-mute'; export default class extends Channel { public readonly chName = 'hybridTimeline'; @@ -61,6 +62,13 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (shouldMuteThisNote(note, this.muting)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する + // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) + // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 + // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 + // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.send('note', note); } diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts index 4b7f74e4f7..3279912f87 100644 --- a/src/server/api/stream/channels/local-timeline.ts +++ b/src/server/api/stream/channels/local-timeline.ts @@ -5,6 +5,7 @@ import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; import { PackedNote } from '../../../../models/repositories/note'; import { PackedUser } from '../../../../models/repositories/user'; +import { checkWordMute } from '../../../../misc/check-word-mute'; export default class extends Channel { public readonly chName = 'localTimeline'; @@ -49,6 +50,13 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (shouldMuteThisNote(note, this.muting)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する + // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) + // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 + // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 + // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.send('note', note); } diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index b7cefcf5ab..bebf88a7cd 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -7,15 +7,17 @@ import Channel from './channel'; import channels from './channels'; import { EventEmitter } from 'events'; import { User } from '../../../models/entities/user'; -import { Users, Followings, Mutings } from '../../../models'; +import { Users, Followings, Mutings, UserProfiles } from '../../../models'; import { ApiError } from '../error'; import { AccessToken } from '../../../models/entities/access-token'; +import { UserProfile } from '../../../models/entities/user-profile'; /** * Main stream connection */ export default class Connection { public user?: User; + public userProfile?: UserProfile; public following: User['id'][] = []; public muting: User['id'][] = []; public token?: AccessToken; @@ -25,6 +27,7 @@ export default class Connection { private subscribingNotes: any = {}; private followingClock: NodeJS.Timer; private mutingClock: NodeJS.Timer; + private userProfileClock: NodeJS.Timer; constructor( wsConnection: websocket.connection, @@ -49,6 +52,9 @@ export default class Connection { this.updateMuting(); this.mutingClock = setInterval(this.updateMuting, 5000); + + this.updateUserProfile(); + this.userProfileClock = setInterval(this.updateUserProfile, 5000); } } @@ -262,6 +268,13 @@ export default class Connection { this.muting = mutings.map(x => x.muteeId); } + @autobind + private async updateUserProfile() { + this.userProfile = await UserProfiles.findOne({ + userId: this.user!.id + }); + } + /** * ストリームが切れたとき */ @@ -273,5 +286,6 @@ export default class Connection { if (this.followingClock) clearInterval(this.followingClock); if (this.mutingClock) clearInterval(this.mutingClock); + if (this.userProfileClock) clearInterval(this.userProfileClock); } } diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 7b5e6a92ba..44ec5fda6f 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -17,7 +17,7 @@ import extractMentions from '../../misc/extract-mentions'; import extractEmojis from '../../misc/extract-emojis'; import extractHashtags from '../../misc/extract-hashtags'; import { Note, IMentionedRemoteUsers } from '../../models/entities/note'; -import { Mutings, Users, NoteWatchings, Notes, Instances, UserProfiles, Antennas, Followings } from '../../models'; +import { Mutings, Users, NoteWatchings, Notes, Instances, UserProfiles, Antennas, Followings, MutedNotes } from '../../models'; import { DriveFile } from '../../models/entities/drive-file'; import { App } from '../../models/entities/app'; import { Not, getConnection, In } from 'typeorm'; @@ -29,6 +29,7 @@ import { createNotification } from '../create-notification'; import { isDuplicateKeyValueError } from '../../misc/is-duplicate-key-value-error'; import { ensure } from '../../prelude/ensure'; import { checkHitAntenna } from '../../misc/check-hit-antenna'; +import { checkWordMute } from '../../misc/check-word-mute'; import { addNoteToAntenna } from '../add-note-to-antenna'; import { countSameRenotes } from '../../misc/count-same-renotes'; import { deliverToRelays } from '../relay'; @@ -219,6 +220,24 @@ export default async (user: User, data: Option, silent = false) => new Promise { + for (const u of us) { + checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { + if (shouldMute) { + MutedNotes.save({ + id: genId(), + userId: u.userId, + noteId: note.id, + reason: 'word', + }); + } + }); + } + }); + // Antenna Antennas.find().then(async antennas => { const followings = await Followings.createQueryBuilder('following') diff --git a/src/types.ts b/src/types.ts index 30a62412a8..d8eb442810 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; + +export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const; diff --git a/yarn.lock b/yarn.lock index dd1d55b91b..082f8b4dd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3245,6 +3245,11 @@ entities@^2.0.0, entities@~2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== + errno@^0.1.3: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -4129,6 +4134,11 @@ graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, g resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" @@ -4658,6 +4668,11 @@ insert-text-at-cursor@0.3.0: resolved "https://registry.yarnpkg.com/insert-text-at-cursor/-/insert-text-at-cursor-0.3.0.tgz#1819607680ec1570618347c4cd475e791faa25da" integrity sha512-/nPtyeX9xPUvxZf+r0518B7uqNKlP+LqNJqSiXFEaa2T71rWIwTVXGH7hB9xO/EVdwa5/pWlFCPwShOW81XIxQ== +install-artifact-from-github@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.0.2.tgz#e1e478dd29880b9112ecd684a84029603e234a9d" + integrity sha512-yuMFBSVIP3vD0SDBGUqeIpgOAIlFx8eQFknQObpkYEM5gsl9hy6R9Ms3aV+Vw9MMyYsoPMeex0XDnfgY7uzc+Q== + interpret@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" @@ -6187,7 +6202,7 @@ mz@^2.4.0, mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.14.0: +nan@^2.14.0, nan@^2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== @@ -6283,6 +6298,22 @@ node-forge@^0.9.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5" integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ== +node-gyp@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.0.0.tgz#2e88425ce84e9b1a4433958ed55d74c70fffb6be" + integrity sha512-ZW34qA3CJSPKDz2SJBHKRvyNQN0yWO5EGKKksJc+jElu9VA468gwJTyTArC1iOXU7rN3Wtfg/CMt/dBAOFIjvg== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^4.0.3" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^2.6.3" + semver "^7.3.2" + tar "^6.0.1" + which "^2.0.2" + node-object-hash@^1.2.0: version "1.4.2" resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94" @@ -7775,6 +7806,15 @@ rdf-canonize@^1.0.2: node-forge "^0.9.1" semver "^6.3.0" +re2@1.15.4: + version "1.15.4" + resolved "https://registry.yarnpkg.com/re2/-/re2-1.15.4.tgz#2ffc3e4894fb60430393459978197648be01a0a9" + integrity sha512-7w3K+Daq/JjbX/dz5voMt7B9wlprVBQnMiypyCojAZ99kcAL+3LiJ5uBoX/u47l8eFTVq3Wj+V0pmvU+CT8tOg== + dependencies: + install-artifact-from-github "^1.0.2" + nan "^2.14.1" + node-gyp "^7.0.0" + read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" @@ -8183,7 +8223,7 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^2.6.2: +rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -9088,7 +9128,7 @@ tar-stream@^2.0.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.2: +tar@^6.0.1, tar@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== @@ -10138,7 +10178,7 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -which@2.0.2, which@^2.0.1: +which@2.0.2, which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== From 14b7f05af40ede154a767334dbbefc3458584290 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 27 Jul 2020 23:25:37 +0900 Subject: [PATCH 18/39] refactor(client): Use v-model for note component, freeze object Related: #6595 --- src/client/components/note.vue | 114 +++++++++++++----- src/client/components/notes.vue | 16 ++- .../components/reactions-viewer.reaction.vue | 14 +-- src/client/components/reactions-viewer.vue | 24 +--- src/client/components/timeline.vue | 1 - src/client/pages/favorites.vue | 2 +- src/client/pages/instance/index.vue | 2 +- src/client/pages/instance/queue.queue.vue | 2 +- src/client/pages/note.vue | 2 +- src/client/scripts/stream.ts | 4 +- 10 files changed, 111 insertions(+), 70 deletions(-) diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 9bbf763494..fba812fc71 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -40,14 +40,14 @@

- +

({{ $t('private') }}) - + RN:
@@ -59,7 +59,7 @@
- + - -
- +
@@ -62,14 +62,15 @@ export default Vue.extend({ default: false }, - extract: { + prop: { + type: String, required: false } }, computed: { notes(): any[] { - return this.extract ? this.extract(this.items) : this.items; + return this.prop ? this.items.map(item => item[this.prop]) : this.items; }, reversed(): boolean { @@ -78,6 +79,15 @@ export default Vue.extend({ }, methods: { + updated(oldValue, newValue) { + const i = this.notes.findIndex(n => n === oldValue); + if (this.prop) { + Vue.set(this.items[i], this.prop, newValue); + } else { + Vue.set(this.items, i, newValue); + } + }, + focus() { this.$refs.notes.focus(); } diff --git a/src/client/components/reactions-viewer.reaction.vue b/src/client/components/reactions-viewer.reaction.vue index 97d019d17f..639a1603ca 100644 --- a/src/client/components/reactions-viewer.reaction.vue +++ b/src/client/components/reactions-viewer.reaction.vue @@ -1,7 +1,7 @@ @@ -30,14 +30,6 @@ export default Vue.extend({ type: String, required: true, }, - myReaction: { - type: String, - required: false, - }, - emojis: { - type: Array, - required: true, - }, count: { type: Number, required: true, @@ -79,7 +71,7 @@ export default Vue.extend({ toggleReaction() { if (!this.canToggle) return; - const oldReaction = this.myReaction; + const oldReaction = this.note.myReaction; if (oldReaction) { this.$root.api('notes/reactions/delete', { noteId: this.note.id diff --git a/src/client/components/reactions-viewer.vue b/src/client/components/reactions-viewer.vue index 353e72ccfa..88e7df4646 100644 --- a/src/client/components/reactions-viewer.vue +++ b/src/client/components/reactions-viewer.vue @@ -1,6 +1,6 @@ @@ -12,28 +12,16 @@ export default Vue.extend({ components: { XReaction }, + data() { + return { + initialReactions: new Set(Object.keys(this.note.reactions)) + }; + }, props: { note: { type: Object, required: true }, - reactions: { - type: Object, - required: true - }, - myReaction: { - type: String, - required: false, - }, - emojis: { - type: Array, - required: true, - }, - }, - data() { - return { - initialReactions: new Set(Object.keys(this.note.reactions)) - }; }, computed: { isMe(): boolean { diff --git a/src/client/components/timeline.vue b/src/client/components/timeline.vue index 5fd55e8ca2..28ff6ab1b3 100644 --- a/src/client/components/timeline.vue +++ b/src/client/components/timeline.vue @@ -52,7 +52,6 @@ export default Vue.extend({ }); const prepend = note => { - Object.freeze(note); (this.$refs.tl as any).prepend(note); this.$emit('note'); diff --git a/src/client/pages/favorites.vue b/src/client/pages/favorites.vue index 59bef2ca91..0e625f84cf 100644 --- a/src/client/pages/favorites.vue +++ b/src/client/pages/favorites.vue @@ -2,7 +2,7 @@
{{ $t('favorites') }} - +
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index d21f8d455e..3aedcb65af 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -436,7 +436,7 @@ export default Vue.extend({ }, onStatsLog(statsLog) { - for (const stats of statsLog.reverse()) { + for (const stats of [...statsLog].reverse()) { this.onStats(stats); } } diff --git a/src/client/pages/instance/queue.queue.vue b/src/client/pages/instance/queue.queue.vue index 1649d1e172..c2aa545fc0 100644 --- a/src/client/pages/instance/queue.queue.vue +++ b/src/client/pages/instance/queue.queue.vue @@ -169,7 +169,7 @@ export default Vue.extend({ }, onStatsLog(statsLog) { - for (const stats of statsLog.reverse()) { + for (const stats of [...statsLog].reverse()) { this.onStats(stats); } }, diff --git a/src/client/pages/note.vue b/src/client/pages/note.vue index 5464875dfb..3f42516323 100644 --- a/src/client/pages/note.vue +++ b/src/client/pages/note.vue @@ -14,7 +14,7 @@
- +
diff --git a/src/client/scripts/stream.ts b/src/client/scripts/stream.ts index 4dcd3f1b2e..8a525ba002 100644 --- a/src/client/scripts/stream.ts +++ b/src/client/scripts/stream.ts @@ -112,10 +112,10 @@ export default class Stream extends EventEmitter { } for (const c of connections.filter(c => c != null)) { - c.emit(body.type, body.body); + c.emit(body.type, Object.freeze(body.body)); } } else { - this.emit(type, body); + this.emit(type, Object.freeze(body)); } } From 0efa969a153a060d232a0e31b10577ece87faeae Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 27 Jul 2020 23:26:32 +0900 Subject: [PATCH 19/39] chore: Remove debug code --- src/client/components/note.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/components/note.vue b/src/client/components/note.vue index fba812fc71..c3a199a805 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -246,8 +246,6 @@ export default Vue.extend({ this.connection = this.$root.stream; } - console.log(this.note); - this.muted = await checkWordMute(this.appearNote, this.$store.state.i, this.$store.state.settings.mutedWords); if (this.detail) { From a8adc46f3ba42e86c64a64f2633f5796aeca01f4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 28 Jul 2020 09:36:43 +0900 Subject: [PATCH 20/39] refactor: Rename function --- .../{should-mute-this-note.ts => is-muted-user-related.ts} | 2 +- src/server/api/stream/channels/antenna.ts | 4 ++-- src/server/api/stream/channels/global-timeline.ts | 4 ++-- src/server/api/stream/channels/hashtag.ts | 4 ++-- src/server/api/stream/channels/home-timeline.ts | 4 ++-- src/server/api/stream/channels/hybrid-timeline.ts | 4 ++-- src/server/api/stream/channels/local-timeline.ts | 4 ++-- src/server/api/stream/channels/user-list.ts | 4 ++-- src/services/add-note-to-antenna.ts | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) rename src/misc/{should-mute-this-note.ts => is-muted-user-related.ts} (76%) diff --git a/src/misc/should-mute-this-note.ts b/src/misc/is-muted-user-related.ts similarity index 76% rename from src/misc/should-mute-this-note.ts rename to src/misc/is-muted-user-related.ts index 8f606a2943..6f074bcb90 100644 --- a/src/misc/should-mute-this-note.ts +++ b/src/misc/is-muted-user-related.ts @@ -1,4 +1,4 @@ -export default function(note: any, mutedUserIds: string[]): boolean { +export function isMutedUserRelated(note: any, mutedUserIds: string[]): boolean { if (mutedUserIds.includes(note.userId)) { return true; } diff --git a/src/server/api/stream/channels/antenna.ts b/src/server/api/stream/channels/antenna.ts index 714edb502d..b5a792f814 100644 --- a/src/server/api/stream/channels/antenna.ts +++ b/src/server/api/stream/channels/antenna.ts @@ -1,7 +1,7 @@ import autobind from 'autobind-decorator'; import Channel from '../channel'; import { Notes } from '../../../../models'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; export default class extends Channel { public readonly chName = 'antenna'; @@ -25,7 +25,7 @@ export default class extends Channel { const note = await Notes.pack(body.id, this.user, { detail: true }); // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; this.send('note', note); } else { diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts index 39800fa775..d530907d8d 100644 --- a/src/server/api/stream/channels/global-timeline.ts +++ b/src/server/api/stream/channels/global-timeline.ts @@ -1,5 +1,5 @@ import autobind from 'autobind-decorator'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; import Channel from '../channel'; import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; @@ -46,7 +46,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts index e55a508328..32d8111f72 100644 --- a/src/server/api/stream/channels/hashtag.ts +++ b/src/server/api/stream/channels/hashtag.ts @@ -1,5 +1,5 @@ import autobind from 'autobind-decorator'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; import Channel from '../channel'; import { Notes } from '../../../../models'; import { PackedNote } from '../../../../models/repositories/note'; @@ -34,7 +34,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; this.send('note', note); } diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts index 8504d4547b..caf4ccf5e9 100644 --- a/src/server/api/stream/channels/home-timeline.ts +++ b/src/server/api/stream/channels/home-timeline.ts @@ -1,5 +1,5 @@ import autobind from 'autobind-decorator'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; import Channel from '../channel'; import { Notes } from '../../../../models'; import { PackedNote } from '../../../../models/repositories/note'; @@ -51,7 +51,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts index bc491934ea..1aec98aa72 100644 --- a/src/server/api/stream/channels/hybrid-timeline.ts +++ b/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,5 +1,5 @@ import autobind from 'autobind-decorator'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; import Channel from '../channel'; import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; @@ -60,7 +60,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts index 3279912f87..6426ccc23f 100644 --- a/src/server/api/stream/channels/local-timeline.ts +++ b/src/server/api/stream/channels/local-timeline.ts @@ -1,5 +1,5 @@ import autobind from 'autobind-decorator'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; import Channel from '../channel'; import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; @@ -48,7 +48,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) diff --git a/src/server/api/stream/channels/user-list.ts b/src/server/api/stream/channels/user-list.ts index e1b7a88830..4191a0de54 100644 --- a/src/server/api/stream/channels/user-list.ts +++ b/src/server/api/stream/channels/user-list.ts @@ -1,7 +1,7 @@ import autobind from 'autobind-decorator'; import Channel from '../channel'; import { Notes, UserListJoinings, UserLists } from '../../../../models'; -import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../../../../misc/is-muted-user-related'; import { User } from '../../../../models/entities/user'; import { PackedNote } from '../../../../models/repositories/note'; @@ -73,7 +73,7 @@ export default class extends Channel { } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (shouldMuteThisNote(note, this.muting)) return; + if (isMutedUserRelated(note, this.muting)) return; this.send('note', note); } diff --git a/src/services/add-note-to-antenna.ts b/src/services/add-note-to-antenna.ts index 88a6613c60..57a0df2752 100644 --- a/src/services/add-note-to-antenna.ts +++ b/src/services/add-note-to-antenna.ts @@ -2,7 +2,7 @@ import { Antenna } from '../models/entities/antenna'; import { Note } from '../models/entities/note'; import { AntennaNotes, Mutings, Notes } from '../models'; import { genId } from '../misc/gen-id'; -import shouldMuteThisNote from '../misc/should-mute-this-note'; +import { isMutedUserRelated } from '../misc/is-muted-user-related'; import { ensure } from '../prelude/ensure'; import { publishAntennaStream, publishMainStream } from './stream'; import { User } from '../models/entities/user'; @@ -39,7 +39,7 @@ export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: U _note.renote = await Notes.findOne(note.renoteId).then(ensure); } - if (shouldMuteThisNote(_note, mutings.map(x => x.muteeId))) { + if (isMutedUserRelated(_note, mutings.map(x => x.muteeId))) { return; } From 1b9d316e7c2446211f4b5b6ec27dce0d9b4f0968 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 28 Jul 2020 09:38:41 +0900 Subject: [PATCH 21/39] refactor: Rename function --- .../{generate-mute-query.ts => generate-muted-user-query.ts} | 4 ++-- src/server/api/common/inject-featured.ts | 4 ++-- src/server/api/endpoints/antennas/notes.ts | 4 ++-- src/server/api/endpoints/clips/notes.ts | 4 ++-- src/server/api/endpoints/notes/children.ts | 4 ++-- src/server/api/endpoints/notes/featured.ts | 4 ++-- src/server/api/endpoints/notes/global-timeline.ts | 4 ++-- src/server/api/endpoints/notes/hybrid-timeline.ts | 4 ++-- src/server/api/endpoints/notes/local-timeline.ts | 4 ++-- src/server/api/endpoints/notes/mentions.ts | 4 ++-- src/server/api/endpoints/notes/renotes.ts | 4 ++-- src/server/api/endpoints/notes/replies.ts | 4 ++-- src/server/api/endpoints/notes/search-by-tag.ts | 4 ++-- src/server/api/endpoints/notes/search.ts | 4 ++-- src/server/api/endpoints/notes/timeline.ts | 4 ++-- src/server/api/endpoints/users.ts | 4 ++-- src/server/api/endpoints/users/notes.ts | 4 ++-- src/server/api/endpoints/users/recommendation.ts | 4 ++-- 18 files changed, 36 insertions(+), 36 deletions(-) rename src/server/api/common/{generate-mute-query.ts => generate-muted-user-query.ts} (87%) diff --git a/src/server/api/common/generate-mute-query.ts b/src/server/api/common/generate-muted-user-query.ts similarity index 87% rename from src/server/api/common/generate-mute-query.ts rename to src/server/api/common/generate-muted-user-query.ts index 4504d23512..b346f2f0fb 100644 --- a/src/server/api/common/generate-mute-query.ts +++ b/src/server/api/common/generate-muted-user-query.ts @@ -2,7 +2,7 @@ import { User } from '../../../models/entities/user'; import { Mutings } from '../../../models'; import { SelectQueryBuilder, Brackets } from 'typeorm'; -export function generateMuteQuery(q: SelectQueryBuilder, me: User, exclude?: User) { +export function generateMutedUserQuery(q: SelectQueryBuilder, me: User, exclude?: User) { const mutingQuery = Mutings.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); @@ -28,7 +28,7 @@ export function generateMuteQuery(q: SelectQueryBuilder, me: User, exclude? q.setParameters(mutingQuery.getParameters()); } -export function generateMuteQueryForUsers(q: SelectQueryBuilder, me: User) { +export function generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: User) { const mutingQuery = Mutings.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts index 92e1e3b396..098d20e72d 100644 --- a/src/server/api/common/inject-featured.ts +++ b/src/server/api/common/inject-featured.ts @@ -2,7 +2,7 @@ import rndstr from 'rndstr'; import { Note } from '../../../models/entities/note'; import { User } from '../../../models/entities/user'; import { Notes, UserProfiles, NoteReactions } from '../../../models'; -import { generateMuteQuery } from './generate-mute-query'; +import { generateMutedUserQuery } from './generate-muted-user-query'; import { ensure } from '../../../prelude/ensure'; // TODO: リアクション、Renote、返信などをしたノートは除外する @@ -29,7 +29,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { if (user) { query.andWhere('note.userId != :userId', { userId: user.id }); - generateMuteQuery(query, user); + generateMutedUserQuery(query, user); const reactionQuery = NoteReactions.createQueryBuilder('reaction') .select('reaction.noteId') diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 54af6c7d3f..402a2758bb 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -4,7 +4,7 @@ import define from '../../define'; import { Antennas, Notes, AntennaNotes } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { ApiError } from '../../error'; export const meta = { @@ -62,7 +62,7 @@ export default define(meta, async (ps, user) => { .setParameters(antennaQuery.getParameters()); generateVisibilityQuery(query, user); - generateMuteQuery(query, user); + generateMutedUserQuery(query, user); const notes = await query .take(ps.limit!) diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index d4d994d55a..4cd7e8c621 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -4,7 +4,7 @@ import define from '../../define'; import { Clips, Notes } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; export const meta = { tags: ['account', 'notes', 'clips'], @@ -57,7 +57,7 @@ export default define(meta, async (ps, user) => { .setParameters(clipQuery.getParameters()); generateVisibilityQuery(query, user); - generateMuteQuery(query, user); + generateMutedUserQuery(query, user); const notes = await query .take(ps.limit!) diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index e1a5cc1c8f..0875e0f240 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -3,7 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { Brackets } from 'typeorm'; import { Notes } from '../../../../models'; @@ -67,7 +67,7 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, user); - if (user) generateMuteQuery(query, user); + if (user) generateMutedUserQuery(query, user); const notes = await query.take(ps.limit!).getMany(); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 0dc705de7a..4dda7d0edb 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -1,6 +1,6 @@ import $ from 'cafy'; import define from '../../define'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { Notes } from '../../../../models'; export const meta = { @@ -51,7 +51,7 @@ export default define(meta, async (ps, user) => { .andWhere(`note.visibility = 'public'`) .leftJoinAndSelect('note.user', 'user'); - if (user) generateMuteQuery(query, user); + if (user) generateMutedUserQuery(query, user); let notes = await query .orderBy('note.score', 'DESC') diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 4361b8a299..5e61c17841 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -5,7 +5,7 @@ import { fetchMeta } from '../../../../misc/fetch-meta'; import { ApiError } from '../../error'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes } from '../../../../models'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; import { injectPromo } from '../../common/inject-promo'; @@ -83,7 +83,7 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('note.user', 'user'); generateRepliesQuery(query, user); - if (user) generateMuteQuery(query, user); + if (user) generateMutedUserQuery(query, user); if (user) generateMutedNoteQuery(query, user); if (ps.withFiles) { diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 82199e607e..fab4e9f4e5 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -7,7 +7,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query'; import { Followings, Notes } from '../../../../models'; import { Brackets } from 'typeorm'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; import { injectPromo } from '../../common/inject-promo'; @@ -133,7 +133,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); - generateMuteQuery(query, user); + generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); if (ps.includeMyRenotes === false) { diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 9d51b3b48b..38ec1d4727 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -4,7 +4,7 @@ import define from '../../define'; import { fetchMeta } from '../../../../misc/fetch-meta'; import { ApiError } from '../../error'; import { Notes } from '../../../../models'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { activeUsersChart } from '../../../../services/chart'; @@ -101,7 +101,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); - if (user) generateMuteQuery(query, user); + if (user) generateMutedUserQuery(query, user); if (user) generateMutedNoteQuery(query, user); if (ps.withFiles) { diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index a478d89c07..8a9d295d38 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -4,7 +4,7 @@ import define from '../../define'; import read from '../../../../services/note/read'; import { Notes, Followings } from '../../../../models'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Brackets } from 'typeorm'; @@ -66,7 +66,7 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, user); - generateMuteQuery(query, user); + generateMutedUserQuery(query, user); if (ps.visibility) { query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 1a6f66b368..31c24f294a 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -4,7 +4,7 @@ import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes } from '../../../../models'; @@ -71,7 +71,7 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, user); - if (user) generateMuteQuery(query, user); + if (user) generateMutedUserQuery(query, user); const renotes = await query.take(ps.limit!).getMany(); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 3bf16804ef..9fad74c78e 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -4,7 +4,7 @@ import define from '../../define'; import { Notes } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; export const meta = { desc: { @@ -62,7 +62,7 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, user); - if (user) generateMuteQuery(query, user); + if (user) generateMutedUserQuery(query, user); const timeline = await query.take(ps.limit!).getMany(); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index b30fe732e2..446beb32dd 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -3,7 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes } from '../../../../models'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { Brackets } from 'typeorm'; import { safeForSql } from '../../../../misc/safe-for-sql'; @@ -97,7 +97,7 @@ export default define(meta, async (ps, me) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, me); - if (me) generateMuteQuery(query, me); + if (me) generateMutedUserQuery(query, me); if (ps.tag) { if (!safeForSql(ps.tag)) return; diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index 602c1a71f5..2c75d2a55a 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -7,7 +7,7 @@ import { ID } from '../../../../misc/cafy-id'; import config from '../../../../config'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; export const meta = { desc: { @@ -69,7 +69,7 @@ export default define(meta, async (ps, me) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, me); - if (me) generateMuteQuery(query, me); + if (me) generateMutedUserQuery(query, me); const notes = await query.take(ps.limit!).getMany(); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index c6929f4a51..657739820b 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -4,7 +4,7 @@ import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes, Followings } from '../../../../models'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; @@ -126,7 +126,7 @@ export default define(meta, async (ps, user) => { generateRepliesQuery(query, user); generateVisibilityQuery(query, user); - generateMuteQuery(query, user); + generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); if (ps.includeMyRenotes === false) { diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts index d21dceb27c..9d7991b406 100644 --- a/src/server/api/endpoints/users.ts +++ b/src/server/api/endpoints/users.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import define from '../define'; import { Users } from '../../../models'; -import { generateMuteQueryForUsers } from '../common/generate-mute-query'; +import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query'; export const meta = { tags: ['users'], @@ -87,7 +87,7 @@ export default define(meta, async (ps, me) => { default: query.orderBy('user.id', 'ASC'); break; } - if (me) generateMuteQueryForUsers(query, me); + if (me) generateMutedUserQueryForUsers(query, me); query.take(ps.limit!); query.skip(ps.offset); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index f5f08dc891..33e3ecb03f 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -6,7 +6,7 @@ import { getUser } from '../../common/getters'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { Notes } from '../../../../models'; -import { generateMuteQuery } from '../../common/generate-mute-query'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { Brackets } from 'typeorm'; export const meta = { @@ -134,7 +134,7 @@ export default define(meta, async (ps, me) => { .leftJoinAndSelect('note.user', 'user'); generateVisibilityQuery(query, me); - if (me) generateMuteQuery(query, me, user); + if (me) generateMutedUserQuery(query, me, user); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 73ac615d46..1b59624aa9 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -2,7 +2,7 @@ import * as ms from 'ms'; import $ from 'cafy'; import define from '../../define'; import { Users, Followings } from '../../../../models'; -import { generateMuteQueryForUsers } from '../../common/generate-mute-query'; +import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query'; import { generateBlockQueryForUsers } from '../../common/generate-block-query'; export const meta = { @@ -47,7 +47,7 @@ export default define(meta, async (ps, me) => { .andWhere('user.id != :meId', { meId: me.id }) .orderBy('user.followersCount', 'DESC'); - generateMuteQueryForUsers(query, me); + generateMutedUserQueryForUsers(query, me); generateBlockQueryForUsers(query, me); const followingQuery = Followings.createQueryBuilder('following') From 6b8354ccbfa1d96b4445013d2e93af8e06550516 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 28 Jul 2020 10:08:08 +0900 Subject: [PATCH 22/39] enhance(client): Use tab component for page list --- src/client/components/tab.vue | 6 +++++- src/client/pages/pages.vue | 35 ++++++++++++++++------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue index 3ea63fa59f..824f150840 100644 --- a/src/client/components/tab.vue +++ b/src/client/components/tab.vue @@ -1,6 +1,6 @@ @@ -33,6 +33,10 @@ export default Vue.extend({ color: var(--accent); border-bottom-color: var(--accent); } + + > .icon { + margin-right: 6px; + } } &.max-width_500px { diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue index a261785715..9f9c68ee28 100644 --- a/src/client/pages/pages.vue +++ b/src/client/pages/pages.vue @@ -3,24 +3,20 @@ {{ $t('pages') }} - - -
- - - - -
-
+ - - -
- - - -
-
+
+ + + + +
+ +
+ + + +
@@ -31,14 +27,15 @@ import { faStickyNote, faHeart } from '@fortawesome/free-regular-svg-icons'; import MkPagePreview from '../components/page-preview.vue'; import MkPagination from '../components/ui/pagination.vue'; import MkButton from '../components/ui/button.vue'; -import MkContainer from '../components/ui/container.vue'; +import MkTab from '../components/tab.vue'; export default Vue.extend({ components: { - MkPagePreview, MkPagination, MkButton, MkContainer + MkPagePreview, MkPagination, MkButton, MkTab }, data() { return { + tab: 'my', myPagesPagination: { endpoint: 'i/pages', limit: 5, From 595ad04ddbbf9ff9fc6842f345d4738a9f1cc150 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 28 Jul 2020 19:02:28 +0900 Subject: [PATCH 23/39] =?UTF-8?q?feat(client):=20=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=82=92=E7=84=A1=E5=8A=B9=E3=81=AB?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ja-JP.yml | 1 + src/client/init.ts | 2 +- src/client/pages/preferences/plugins.vue | 16 ++++++++++++++++ src/client/scripts/aiscript/api.ts | 17 ++++++++++++++--- src/client/store.ts | 14 +++++++++++++- 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c34d93dc87..06b53f26a0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -555,6 +555,7 @@ smtpSecureInfo: "STARTTLS使用時はオフにします。" testEmail: "配信テスト" wordMute: "ワードミュート" userSaysSomething: "{name}が何かを言いました" +makeActive: "アクティブにする" _wordMute: muteWords: "ミュートするワード" diff --git a/src/client/init.ts b/src/client/init.ts index b819a16e5a..d76e94c5a3 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -242,7 +242,7 @@ os.init(async () => { //store.commit('instance/set', ); }); - for (const plugin of store.state.deviceUser.plugins) { + for (const plugin of store.state.deviceUser.plugins.filter(p => p.active)) { console.info('Plugin installed:', plugin.name, 'v' + plugin.version); const aiscript = new AiScript(createPluginEnv(app, { diff --git a/src/client/pages/preferences/plugins.vue b/src/client/pages/preferences/plugins.vue index 8bd522ddc6..b61b2c8daf 100644 --- a/src/client/pages/preferences/plugins.vue +++ b/src/client/pages/preferences/plugins.vue @@ -18,6 +18,9 @@