From 48edfb5b2e14d511f79ea82851e9b00a6574ab14 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 11 Jun 2021 12:19:48 +0900 Subject: [PATCH 01/38] Update CONTRIBUTING.md (#7570) --- CONTRIBUTING.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6926ed918..6dc2e5e22 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,12 +28,7 @@ If your language is not listed in Crowdin, please open an issue. ![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg) -## Internationalization (i18n) -Misskey uses the Vue.js plugin [Vue I18n](https://github.com/kazupon/vue-i18n). -Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/introduction.html . - ## Documentation -* Documents for contributors are located in [`/docs`](/docs). * Documents for instance admins are located in [`/docs`](/docs). * Documents for end users are located in [`/src/docs`](/src/docs). @@ -41,8 +36,8 @@ Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/intr * Test codes are located in [`/test`](/test). ## Continuous integration -Misskey uses CircleCI for executing automated tests. -Configuration files are located in [`/.circleci`](/.circleci). +Misskey uses GitHub Actions for executing automated tests. +Configuration files are located in [`/.github/workflows`](/.github/workflows). ## Adding MisskeyRoom items * Use English for material, object and texture names. From 61fa6c0bff14f8ed7455e966de03518d97560897 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 12 Jun 2021 22:32:44 +0900 Subject: [PATCH 02/38] Update resolutions (#7572) --- package.json | 4 +--- yarn.lock | 13 ++++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 44cf2404b..3d92c47cd 100644 --- a/package.json +++ b/package.json @@ -32,9 +32,7 @@ "resolutions": { "mfm-js/twemoji-parser": "13.1.x", "chokidar": "^3.3.1", - "constantinople": "^4.0.1", - "jsonld/rdf-canonize/node-forge": "0.10.0", - "lodash": "^4.17.20" + "lodash": "^4.17.21" }, "dependencies": { "@babel/plugin-transform-runtime": "7.14.3", diff --git a/yarn.lock b/yarn.lock index 2e0a3e2d9..58ce75ada 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6682,10 +6682,10 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@4.0.0: version "4.0.0" @@ -7310,11 +7310,6 @@ node-fetch@2.6.1, node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-forge@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - node-gyp-build@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" From 80f207fdddb435d945e689c8fe64664b8fa552dd Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 12 Jun 2021 22:40:17 +0900 Subject: [PATCH 03/38] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=8C?= =?UTF-8?q?=E3=81=86=E3=81=94=E3=81=8B=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#7566)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * startServer * typeorm 0.2.32 * Fix: chartのテストがテストの並び順によっては正しく初期化されない * initTestDb --- .mocharc.json | 2 +- package.json | 2 +- test/api-visibility.ts | 6 +++-- test/chart.ts | 54 +++++++----------------------------------- test/fetch-resource.ts | 7 +++--- test/mute.ts | 7 +++--- test/note.ts | 10 ++++---- test/streaming.ts | 14 +++++------ test/user-notes.ts | 7 +++--- test/utils.ts | 49 +++++++++++++++++++++++++++++++++++++- yarn.lock | 8 +++---- 11 files changed, 91 insertions(+), 75 deletions(-) diff --git a/.mocharc.json b/.mocharc.json index fc7fee215..278a5b310 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -2,6 +2,6 @@ "extension": ["ts","js","cjs","mjs"], "require": ["ts-node/register", "tsconfig-paths/register"], "slow": 1000, - "timeout": 30000, + "timeout": 35000, "exit": true } diff --git a/package.json b/package.json index 3d92c47cd..a6b0e5e66 100644 --- a/package.json +++ b/package.json @@ -233,7 +233,7 @@ "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "twemoji-parser": "13.1.0", - "typeorm": "0.2.34", + "typeorm": "0.2.32", "typescript": "4.3.2", "ulid": "2.3.0", "uuid": "8.3.2", diff --git a/test/api-visibility.ts b/test/api-visibility.ts index c4ec32193..6548146c7 100644 --- a/test/api-visibility.ts +++ b/test/api-visibility.ts @@ -12,12 +12,14 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, signup, request, post, launchServer, shutdownServer } from './utils'; +import { async, signup, request, post, startServer, shutdownServer } from './utils'; describe('API visibility', () => { let p: childProcess.ChildProcess; - before(launchServer(g => p = g)); + before(async () => { + p = await startServer(); + }); after(async () => { await shutdownServer(p); diff --git a/test/chart.ts b/test/chart.ts index 55f6bd696..4a40b2553 100644 --- a/test/chart.ts +++ b/test/chart.ts @@ -12,61 +12,28 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as lolex from '@sinonjs/fake-timers'; -import { async } from './utils'; +import { async, initTestDb } from './utils'; import TestChart from '../src/services/chart/charts/classes/test'; import TestGroupedChart from '../src/services/chart/charts/classes/test-grouped'; import TestUniqueChart from '../src/services/chart/charts/classes/test-unique'; import * as _TestChart from '../src/services/chart/charts/schemas/test'; import * as _TestGroupedChart from '../src/services/chart/charts/schemas/test-grouped'; import * as _TestUniqueChart from '../src/services/chart/charts/schemas/test-unique'; -import { Connection, getConnection, createConnection } from 'typeorm'; -import config from '../src/config'; import Chart from '../src/services/chart/core'; -import { initDb } from '../src/db/postgre'; - -function initChartDb() { - try { - const conn = getConnection(); - return Promise.resolve(conn); - } catch (e) {} - - return createConnection({ - type: 'postgres', - host: config.db.host, - port: config.db.port, - username: config.db.user, - password: config.db.pass, - database: config.db.db, - synchronize: true, - dropSchema: true, - entities: [ - Chart.schemaToEntity(_TestChart.name, _TestChart.schema), - Chart.schemaToEntity(_TestGroupedChart.name, _TestGroupedChart.schema), - Chart.schemaToEntity(_TestUniqueChart.name, _TestUniqueChart.schema) - ] - }); -} describe('Chart', () => { let testChart: TestChart; let testGroupedChart: TestGroupedChart; let testUniqueChart: TestUniqueChart; - let clock: lolex.InstalledClock; - let connection: Connection; + let clock: lolex.Clock; - before(done => { - initChartDb().then(c => { - connection = c; - done(); - }); - }); + beforeEach(async(async () => { + await initTestDb(false, [ + Chart.schemaToEntity(_TestChart.name, _TestChart.schema), + Chart.schemaToEntity(_TestGroupedChart.name, _TestGroupedChart.schema), + Chart.schemaToEntity(_TestUniqueChart.name, _TestUniqueChart.schema) + ]); - after(async(async () => { - await connection.close(); - await initDb(true, undefined, true); - })); - - beforeEach(done => { testChart = new TestChart(); testGroupedChart = new TestGroupedChart(); testUniqueChart = new TestUniqueChart(); @@ -74,13 +41,10 @@ describe('Chart', () => { clock = lolex.install({ now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)) }); - done(); - }); + })); afterEach(async(async () => { clock.uninstall(); - await connection.dropDatabase(); - await connection.synchronize(); })); it('Can updates', async(async () => { diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts index e9d10d1ab..31308b08f 100644 --- a/test/fetch-resource.ts +++ b/test/fetch-resource.ts @@ -12,7 +12,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, launchServer, signup, post, request, simpleGet, port, shutdownServer } from './utils'; +import { async, startServer, signup, post, request, simpleGet, port, shutdownServer } from './utils'; import * as openapi from '@redocly/openapi-core'; // Request Accept @@ -32,12 +32,13 @@ describe('Fetch resource', () => { let alice: any; let alicesPost: any; - before(launchServer(g => p = g, async () => { + before(async () => { + p = await startServer(); alice = await signup({ username: 'alice' }); alicesPost = await post(alice, { text: 'test' }); - })); + }); after(async () => { await shutdownServer(p); diff --git a/test/mute.ts b/test/mute.ts index 38911b6e1..632f60fa4 100644 --- a/test/mute.ts +++ b/test/mute.ts @@ -12,7 +12,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, signup, request, post, react, connectStream, launchServer, shutdownServer } from './utils'; +import { async, signup, request, post, react, connectStream, startServer, shutdownServer } from './utils'; describe('Mute', () => { let p: childProcess.ChildProcess; @@ -22,11 +22,12 @@ describe('Mute', () => { let bob: any; let carol: any; - before(launchServer(g => p = g, async () => { + before(async () => { + p = await startServer(); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); - })); + }); after(async () => { await shutdownServer(p); diff --git a/test/note.ts b/test/note.ts index 3f1700577..31aaf00da 100644 --- a/test/note.ts +++ b/test/note.ts @@ -12,9 +12,8 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, signup, request, post, uploadFile, launchServer, shutdownServer } from './utils'; +import { async, signup, request, post, uploadFile, startServer, shutdownServer, initTestDb } from './utils'; import { Note } from '../src/models/entities/note'; -import { initDb } from '../src/db/postgre'; describe('Note', () => { let p: childProcess.ChildProcess; @@ -23,12 +22,13 @@ describe('Note', () => { let alice: any; let bob: any; - before(launchServer(g => p = g, async () => { - const connection = await initDb(true); + before(async () => { + p = await startServer(); + const connection = await initTestDb(true); Notes = connection.getRepository(Note); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); - })); + }); after(async () => { await shutdownServer(p); diff --git a/test/streaming.ts b/test/streaming.ts index 214fdeb1f..cc3168e98 100644 --- a/test/streaming.ts +++ b/test/streaming.ts @@ -12,21 +12,21 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { connectStream, signup, request, post, launchServer } from './utils'; +import { connectStream, signup, request, post, startServer, shutdownServer, initTestDb } from './utils'; import { Following } from '../src/models/entities/following'; -import { initDb } from '../src/db/postgre'; describe('Streaming', () => { let p: childProcess.ChildProcess; let Followings: any; - beforeEach(launchServer(g => p = g, async () => { - const connection = await initDb(true); + beforeEach(async () => { + p = await startServer(); + const connection = await initTestDb(true); Followings = connection.getRepository(Following); - })); + }); - afterEach(() => { - p.kill(); + afterEach(async () => { + await shutdownServer(p); }); const follow = async (follower: any, followee: any) => { diff --git a/test/user-notes.ts b/test/user-notes.ts index 4af8ce0cc..30589f814 100644 --- a/test/user-notes.ts +++ b/test/user-notes.ts @@ -12,7 +12,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, signup, request, post, uploadFile, launchServer, shutdownServer } from './utils'; +import { async, signup, request, post, uploadFile, startServer, shutdownServer } from './utils'; describe('users/notes', () => { let p: childProcess.ChildProcess; @@ -22,7 +22,8 @@ describe('users/notes', () => { let pngNote: any; let jpgPngNote: any; - before(launchServer(g => p = g, async () => { + before(async () => { + p = await startServer(); alice = await signup({ username: 'alice' }); const jpg = await uploadFile(alice, __dirname + '/resources/Lenna.jpg'); const png = await uploadFile(alice, __dirname + '/resources/Lenna.png'); @@ -35,7 +36,7 @@ describe('users/notes', () => { jpgPngNote = await post(alice, { fileIds: [jpg.id, png.id] }); - })); + }); after(async() => { await shutdownServer(p); diff --git a/test/utils.ts b/test/utils.ts index e4c96d0e1..1a0c54463 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,8 +6,11 @@ import * as childProcess from 'child_process'; import * as http from 'http'; import loadConfig from '../src/config/load'; import { SIGKILL } from 'constants'; +import { createConnection, getConnection } from 'typeorm'; +import { entities } from '../src/db/postgre'; -export const port = loadConfig().port; +const config = loadConfig(); +export const port = config.port; export const async = (fn: Function) => (done: Function) => { fn().then(() => { @@ -147,6 +150,50 @@ export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProce }; } +export async function initTestDb(justBorrow = false, initEntities?: any[]) { + if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; + + try { + const conn = await getConnection(); + await conn.close(); + } catch (e) {} + + return await createConnection({ + type: 'postgres', + host: config.db.host, + port: config.db.port, + username: config.db.user, + password: config.db.pass, + database: config.db.db, + synchronize: true && !justBorrow, + dropSchema: true && !justBorrow, + entities: initEntities || entities + }); +} + +export function startServer(timeout = 30 * 1000): Promise { + return new Promise((res, rej) => { + const t = setTimeout(() => { + p.kill(SIGKILL); + rej('timeout to start'); + }, timeout); + + const p = childProcess.spawn('node', [__dirname + '/../index.js'], { + stdio: ['inherit', 'inherit', 'inherit', 'ipc'], + env: { NODE_ENV: 'test', PATH: process.env.PATH } + }); + + p.on('error', e => rej(e)); + + p.on('message', message => { + if (message === 'ok') { + clearTimeout(t); + res(p); + } + }); + }); +} + export function shutdownServer(p: childProcess.ChildProcess, timeout = 20 * 1000) { return new Promise((res, rej) => { const t = setTimeout(() => { diff --git a/yarn.lock b/yarn.lock index 58ce75ada..3c471bd1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10973,10 +10973,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typeorm@0.2.34: - version "0.2.34" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.34.tgz#637b3cec2de54ee7f423012b813a2022c0aacc8b" - integrity sha512-FZAeEGGdSGq7uTH3FWRQq67JjKu0mgANsSZ04j3kvDYNgy9KwBl/6RFgMVgiSgjf7Rqd7NrhC2KxVT7I80qf7w== +typeorm@0.2.32: + version "0.2.32" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.32.tgz#544dbfdfe0cd0887548d9bcbd28527ea4f4b3c9b" + integrity sha512-LOBZKZ9As3f8KRMPCUT2H0JZbZfWfkcUnO3w/1BFAbL/X9+cADTF6bczDGGaKVENJ3P8SaKheKmBgpt5h1x+EQ== dependencies: "@sqltools/formatter" "^1.2.2" app-root-path "^3.0.0" From c3f9f6dfa5cc25fe0a4d43c3c57c22536dc06578 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 14 Jun 2021 22:58:43 +0900 Subject: [PATCH 04/38] Update theme.vue --- src/client/pages/settings/theme.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/settings/theme.vue b/src/client/pages/settings/theme.vue index 1eb0d68be..94eddb1b6 100644 --- a/src/client/pages/settings/theme.vue +++ b/src/client/pages/settings/theme.vue @@ -71,7 +71,7 @@ {{ $ts.removeWallpaper }} - {{ $ts._theme.explore }} + {{ $ts._theme.explore }} {{ $ts._theme.install }} From e4211ba40e121ddc5ef32f3c55fdc179fe40913f Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 23 Jun 2021 13:42:02 +0900 Subject: [PATCH 05/38] =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=88=E6=99=82=E3=82=82emojiAdded=E3=82=A4=E3=83=99?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E7=99=BA=E8=A1=8C=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/endpoints/admin/emoji/copy.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/server/api/endpoints/admin/emoji/copy.ts b/src/server/api/endpoints/admin/emoji/copy.ts index 1a784f406..72c2b014a 100644 --- a/src/server/api/endpoints/admin/emoji/copy.ts +++ b/src/server/api/endpoints/admin/emoji/copy.ts @@ -7,6 +7,7 @@ import { ApiError } from '../../../error'; import { DriveFile } from '../../../../../models/entities/drive-file'; import { ID } from '@/misc/cafy-id'; import uploadFromUrl from '../../../../../services/drive/upload-from-url'; +import { publishBroadcastStream } from '@/services/stream'; export const meta = { tags: ['admin'], @@ -57,7 +58,7 @@ export default define(meta, async (ps, me) => { throw new ApiError(); } - const copied = await Emojis.save({ + const copied = await Emojis.insert({ id: genId(), updatedAt: new Date(), name: emoji.name, @@ -66,10 +67,14 @@ export default define(meta, async (ps, me) => { url: driveFile.url, type: driveFile.type, fileId: driveFile.id, - }); + }).then(x => Emojis.findOneOrFail(x.identifiers[0])); await getConnection().queryResultCache!.remove(['meta_emojis']); + publishBroadcastStream('emojiAdded', { + emoji: await Emojis.pack(copied.id) + }); + return { id: copied.id }; From 45a58615bcea1f63ae27d5a80e63c20a5814a8d8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 30 Jun 2021 23:33:50 +0900 Subject: [PATCH 06/38] improve type --- src/server/api/stream/channels/messaging.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/api/stream/channels/messaging.ts b/src/server/api/stream/channels/messaging.ts index 4c41dc820..58427e277 100644 --- a/src/server/api/stream/channels/messaging.ts +++ b/src/server/api/stream/channels/messaging.ts @@ -10,7 +10,7 @@ export default class extends Channel { public static requireCredential = true; private otherpartyId: string | null; - private otherparty?: User; + private otherparty: User | null; private groupId: string | null; private subCh: string; private typers: Record = {}; @@ -18,9 +18,9 @@ export default class extends Channel { @autobind public async init(params: any) { - this.otherpartyId = params.otherparty as string; - this.otherparty = await Users.findOne({ id: this.otherpartyId }); - this.groupId = params.group as string; + this.otherpartyId = params.otherparty; + this.otherparty = this.otherpartyId ? await Users.findOneOrFail({ id: this.otherpartyId }) : null; + this.groupId = params.group; // Check joining if (this.groupId) { From ae674d25071bec621bf144ba89ff511f721bdbbb Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 1 Jul 2021 00:50:19 +0900 Subject: [PATCH 07/38] refactoring --- src/services/chart/core.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index 4a554daa7..14cbd9280 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -52,7 +52,7 @@ export default abstract class Chart> { private static readonly columnDot = '_'; private name: string; - private queue: { + private buffer: { diff: DeepPartial; group: string | null; }[] = []; @@ -330,28 +330,28 @@ export default abstract class Chart> { @autobind protected commit(diff: DeepPartial, group: string | null = null): void { - this.queue.push({ + this.buffer.push({ diff, group, }); } @autobind public async save() { - if (this.queue.length === 0) { + if (this.buffer.length === 0) { logger.info(`${this.name}: Write skipped`); return; } - // TODO: 前の時間のログがqueueにあった場合のハンドリング + // TODO: 前の時間のログがbufferにあった場合のハンドリング // 例えば、save が20分ごとに行われるとして、前回行われたのは 01:50 だったとする。 - // 次に save が行われるのは 02:10 ということになるが、もし 01:55 に新規ログが queue に追加されたとすると、 + // 次に save が行われるのは 02:10 ということになるが、もし 01:55 に新規ログが buffer に追加されたとすると、 // そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。 // これを回避するための実装は複雑になりそうなため、一旦保留。 const update = async (log: Log) => { const finalDiffs = {} as Record; - for (const diff of this.queue.filter(q => q.group === log.group).map(q => q.diff)) { + for (const diff of this.buffer.filter(q => q.group === log.group).map(q => q.diff)) { const columns = Chart.convertObjectToFlattenColumns(diff); for (const [k, v] of Object.entries(columns)) { @@ -378,11 +378,11 @@ export default abstract class Chart> { logger.info(`${this.name + (log.group ? `:${log.group}` : '')}: Updated`); - // TODO: この一連の処理が始まった後に新たにqueueに入ったものは消さないようにする - this.queue = this.queue.filter(q => q.group !== log.group); + // TODO: この一連の処理が始まった後に新たにbufferに入ったものは消さないようにする + this.buffer = this.buffer.filter(q => q.group !== log.group); }; - const groups = removeDuplicates(this.queue.map(log => log.group)); + const groups = removeDuplicates(this.buffer.map(log => log.group)); await Promise.all(groups.map(group => this.getCurrentLog(group).then(log => update(log)))); } From 2bd3bec2d5a80975e649a94dd65f51705e485b7b Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 9 Jul 2021 01:07:55 +0900 Subject: [PATCH 08/38] Resolve #7165 --- src/server/api/common/read-notification.ts | 23 +++++++++++++++++++--- src/services/note/read.ts | 5 +++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/server/api/common/read-notification.ts b/src/server/api/common/read-notification.ts index f686446c5..effa61e8b 100644 --- a/src/server/api/common/read-notification.ts +++ b/src/server/api/common/read-notification.ts @@ -4,9 +4,6 @@ import { Notification } from '../../../models/entities/notification'; import { Notifications, Users } from '../../../models'; import { In } from 'typeorm'; -/** - * Mark notifications as read - */ export async function readNotification( userId: User['id'], notificationIds: Notification['id'][] @@ -19,6 +16,26 @@ export async function readNotification( isRead: true }); + post(userId); +} + +export async function readNotificationByQuery( + userId: User['id'], + query: Record +) { + // Update documents + await Notifications.update({ + ...query, + notifieeId: userId, + isRead: false + }, { + isRead: true + }); + + post(userId); +} + +async function post(userId: User['id']) { if (!await Users.getHasUnreadNotification(userId)) { // 全ての(いままで未読だった)通知を(これで)読みましたよというイベントを発行 publishMainStream(userId, 'readAllNotifications'); diff --git a/src/services/note/read.ts b/src/services/note/read.ts index 2bdb85947..a661c0a9d 100644 --- a/src/services/note/read.ts +++ b/src/services/note/read.ts @@ -7,6 +7,7 @@ import { Channel } from '../../models/entities/channel'; import { checkHitAntenna } from '@/misc/check-hit-antenna'; import { getAntennas } from '@/misc/antenna-cache'; import { PackedNote } from '../../models/repositories/note'; +import { readNotificationByQuery } from '@/server/api/common/read-notification'; /** * Mark notes as read @@ -96,6 +97,10 @@ export default async function( publishMainStream(userId, 'readAllChannels'); } }); + + readNotificationByQuery(userId, { + noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]), + }); } if (readAntennaNotes.length > 0) { From 0e1ee5bae7c226ddf7f5f4add90b6282cbeb9413 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 9 Jul 2021 01:09:02 +0900 Subject: [PATCH 09/38] =?UTF-8?q?mfm.js=E3=81=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9F=E3=81=AE=E3=81=A7=E3=82=82=E3=81=86?= =?UTF-8?q?=E3=81=93=E3=81=AEresolutions=E3=81=AF=E3=81=84=E3=82=89?= =?UTF-8?q?=E3=81=AA=E3=81=84=20(#7581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index a6b0e5e66..088c3d6cd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "format": "gulp format" }, "resolutions": { - "mfm-js/twemoji-parser": "13.1.x", "chokidar": "^3.3.1", "lodash": "^4.17.21" }, From 1c6a1aee955135c88882192ff460c4b7fc353586 Mon Sep 17 00:00:00 2001 From: Skehmatics Date: Fri, 9 Jul 2021 10:55:12 -0700 Subject: [PATCH 10/38] Rich welcome content (#7588) * Add rich content (polls, media) to the welcome page notes * Add a simple scrolling animation to welcome page --- src/client/components/poll.vue | 11 ++-- src/client/pages/welcome.timeline.vue | 72 ++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/client/components/poll.vue b/src/client/components/poll.vue index 463ddab72..b5d430f93 100644 --- a/src/client/components/poll.vue +++ b/src/client/components/poll.vue @@ -10,7 +10,7 @@ -

+

{{ $t('_poll.totalVotes', { n: total }) }} · {{ showResult ? $ts._poll.vote : $ts._poll.showResult }} @@ -31,6 +31,11 @@ export default defineComponent({ note: { type: Object, required: true + }, + readOnly: { + type: Boolean, + required: false, + default: false, } }, data() { @@ -65,7 +70,7 @@ export default defineComponent({ } }, created() { - this.showResult = this.isVoted; + this.showResult = this.readOnly || this.isVoted; if (this.note.poll.expiresAt) { const update = () => { @@ -83,7 +88,7 @@ export default defineComponent({ this.showResult = !this.showResult; }, vote(id) { - if (this.closed || !this.poll.multiple && this.poll.choices.some(c => c.isVoted)) return; + if (this.readOnly || this.closed || !this.poll.multiple && this.poll.choices.some(c => c.isVoted)) return; os.api('notes/polls/vote', { noteId: this.note.id, choice: id diff --git a/src/client/pages/welcome.timeline.vue b/src/client/pages/welcome.timeline.vue index 12c8a4d4f..bd07ac78d 100644 --- a/src/client/pages/welcome.timeline.vue +++ b/src/client/pages/welcome.timeline.vue @@ -1,10 +1,22 @@ @@ -12,16 +24,21 @@ diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue index c7e2d30c7..2e0336878 100644 --- a/src/client/ui/default.sidebar.vue +++ b/src/client/ui/default.sidebar.vue @@ -45,7 +45,7 @@ import { defineComponent } from 'vue'; import { host } from '@client/config'; import { search } from '@client/scripts/search'; import * as os from '@client/os'; -import { sidebarDef } from '@client/sidebar'; +import { menuDef } from '@client/menu'; import { getAccounts, addAccount, login } from '@client/account'; import MkButton from '@client/components/ui/button.vue'; import { StickySidebar } from '@client/scripts/sticky-sidebar'; @@ -62,7 +62,7 @@ export default defineComponent({ host: host, accounts: [], connection: null, - menuDef: sidebarDef, + menuDef: menuDef, iconOnly: false, settingsWindowed: false, }; @@ -83,7 +83,7 @@ export default defineComponent({ }, watch: { - '$store.reactiveState.sidebarDisplay.value'() { + '$store.reactiveState.menuDisplay.value'() { this.calcViewState(); }, @@ -108,7 +108,7 @@ export default defineComponent({ methods: { calcViewState() { - this.iconOnly = (window.innerWidth <= 1400) || (this.$store.state.sidebarDisplay === 'icon'); + this.iconOnly = (window.innerWidth <= 1400) || (this.$store.state.menuDisplay === 'sideIcon'); this.settingsWindowed = (window.innerWidth > 1400); }, diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue index 3c87bf7ab..f18685d78 100644 --- a/src/client/ui/default.vue +++ b/src/client/ui/default.vue @@ -1,9 +1,16 @@