From 2ec4cc9a072e321fd95f172f207e52b10bf228ac Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Mon, 6 Aug 2018 07:19:30 +0000 Subject: [PATCH 01/20] fix(package): update webpack to version 4.16.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11090ef42..ec0e7becb 100644 --- a/package.json +++ b/package.json @@ -214,7 +214,7 @@ "vuex-persistedstate": "2.5.4", "web-push": "3.3.2", "webfinger.js": "2.6.6", - "webpack": "4.16.4", + "webpack": "4.16.5", "webpack-cli": "3.1.0", "websocket": "1.0.26", "ws": "6.0.0", From 5f208a7d99cf985e9c613e2d65656ae4d46aa68a Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Aug 2018 18:28:27 +0900 Subject: [PATCH 02/20] =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E6=A4=9C=E7=B4=A2API=E3=82=92=E7=B5=B1=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/views/components/autocomplete.vue | 2 +- src/server/api/endpoints/users/search.ts | 161 +++++++++++++++--- .../api/endpoints/users/search_by_username.ts | 70 -------- 3 files changed, 143 insertions(+), 90 deletions(-) delete mode 100644 src/server/api/endpoints/users/search_by_username.ts diff --git a/src/client/app/common/views/components/autocomplete.vue b/src/client/app/common/views/components/autocomplete.vue index cd6066877..b274eaa0a 100644 --- a/src/client/app/common/views/components/autocomplete.vue +++ b/src/client/app/common/views/components/autocomplete.vue @@ -132,7 +132,7 @@ export default Vue.extend({ this.users = users; this.fetching = false; } else { - (this as any).api('users/search_by_username', { + (this as any).api('users/search', { query: this.q, limit: 30 }).then(users => { diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index d443d35b4..eda3f9572 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -1,33 +1,156 @@ import $ from 'cafy'; -import User, { pack, ILocalUser } from '../../../../models/user'; const escapeRegexp = require('escape-regexp'); +import User, { pack, ILocalUser, validateUsername, IUser } from '../../../../models/user'; +import getParams from '../../get-params'; + +export const meta = { + desc: { + ja: 'ユーザーを検索します。' + }, + + requireCredential: false, + + params: { + query: $.str.note({ + desc: { + ja: 'クエリ' + } + }), + + offset: $.num.optional.min(0).note({ + default: 0, + desc: { + ja: 'オフセット' + } + }), + + limit: $.num.optional.range(1, 100).note({ + default: 10, + desc: { + ja: '取得する数' + } + }), + + localOnly: $.bool.optional.note({ + default: false, + desc: { + ja: 'ローカルユーザーのみ検索対象にするか否か' + } + }), + }, +}; /** * Search a user */ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'query' parameter - const [query, queryError] = $.str.pipe(x => x != '').get(params.query); - if (queryError) return rej('invalid query param'); + const [ps, psErr] = getParams(meta, params); + if (psErr) return rej(psErr); - // Get 'max' parameter - const [max = 10, maxErr] = $.num.optional.range(1, 30).get(params.max); - if (maxErr) return rej('invalid max param'); + const isUsername = validateUsername(ps.query.replace('@', '')); - const escapedQuery = escapeRegexp(query); + let users: IUser[] = []; - // Search users - const users = await User - .find({ - host: null, - $or: [{ - usernameLower: new RegExp(escapedQuery.replace('@', '').toLowerCase()) + if (isUsername) { + users = await User + .find({ + host: null, + usernameLower: new RegExp('^' + escapeRegexp(ps.query.replace('@', '').toLowerCase())) }, { - name: new RegExp(escapedQuery) - }] - }, { - limit: max - }); + limit: ps.limit, + skip: ps.offset + }); + + if (users.length < ps.limit && !ps.localOnly) { + const otherUsers = await User + .find({ + host: { $ne: null }, + usernameLower: new RegExp('^' + escapeRegexp(ps.query.replace('@', '').toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } + + if (users.length < ps.limit) { + const otherUsers = await User + .find({ + _id: { $nin: users.map(u => u._id) }, + host: null, + usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } + + if (users.length < ps.limit && !ps.localOnly) { + const otherUsers = await User + .find({ + _id: { $nin: users.map(u => u._id) }, + host: { $ne: null }, + usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } + } + + if (users.length < ps.limit) { + const otherUsers = await User + .find({ + _id: { $nin: users.map(u => u._id) }, + host: null, + name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } + + if (users.length < ps.limit && !ps.localOnly) { + const otherUsers = await User + .find({ + _id: { $nin: users.map(u => u._id) }, + host: { $ne: null }, + name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } + + if (users.length < ps.limit) { + const otherUsers = await User + .find({ + _id: { $nin: users.map(u => u._id) }, + host: null, + name: new RegExp(escapeRegexp(ps.query.toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } + + if (users.length < ps.limit && !ps.localOnly) { + const otherUsers = await User + .find({ + _id: { $nin: users.map(u => u._id) }, + host: { $ne: null }, + name: new RegExp(escapeRegexp(ps.query.toLowerCase())) + }, { + limit: ps.limit - users.length + }); + + users = users.concat(otherUsers); + } // Serialize res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); diff --git a/src/server/api/endpoints/users/search_by_username.ts b/src/server/api/endpoints/users/search_by_username.ts deleted file mode 100644 index bfab37838..000000000 --- a/src/server/api/endpoints/users/search_by_username.ts +++ /dev/null @@ -1,70 +0,0 @@ -import $ from 'cafy'; -import User, { pack, ILocalUser } from '../../../../models/user'; -const escapeRegexp = require('escape-regexp'); - -/** - * Search a user by username - */ -export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { - // Get 'query' parameter - const [query, queryError] = $.str.get(params.query); - if (queryError) return rej('invalid query param'); - - // Get 'offset' parameter - const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset); - if (offsetErr) return rej('invalid offset param'); - - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); - if (limitErr) return rej('invalid limit param'); - - let users = await User - .find({ - host: null, - usernameLower: new RegExp('^' + escapeRegexp(query.toLowerCase())) - }, { - limit: limit, - skip: offset - }); - - if (users.length < limit) { - const otherUsers = await User - .find({ - host: { $ne: null }, - usernameLower: new RegExp('^' + escapeRegexp(query.toLowerCase())) - }, { - limit: limit - users.length - }); - - users = users.concat(otherUsers); - } - - if (users.length < limit) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: null, - usernameLower: new RegExp(escapeRegexp(query.toLowerCase())) - }, { - limit: limit - users.length - }); - - users = users.concat(otherUsers); - } - - if (users.length < limit) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: { $ne: null }, - usernameLower: new RegExp(escapeRegexp(query.toLowerCase())) - }, { - limit: limit - users.length - }); - - users = users.concat(otherUsers); - } - - // Serialize - res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); -}); From b6157e0012e23842f24f38dfdd652c7d719e4da3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Aug 2018 18:30:19 +0900 Subject: [PATCH 03/20] 5.18.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ec0e7becb..2b8a1f36d 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "misskey", "author": "syuilo ", - "version": "5.17.0", - "clientVersion": "1.0.8026", + "version": "5.18.0", + "clientVersion": "1.0.8033", "codename": "nighthike", "main": "./built/index.js", "private": true, From 99f4ab700028cc8d959d173d1d17b25f26b7e4a3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Aug 2018 21:35:49 +0900 Subject: [PATCH 04/20] :v: --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index 18eff8176..0dda8b05b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,8 @@ Error.stackTraceLimit = Infinity; +require('events').EventEmitter.defaultMaxListeners = 128; + import * as os from 'os'; import * as cluster from 'cluster'; import * as debug from 'debug'; From 4260ec713fa51a7e50a09445dfe0a23840b63f9e Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 6 Aug 2018 21:48:23 +0900 Subject: [PATCH 05/20] Improve error handling --- src/queue/processors/http/deliver.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/queue/processors/http/deliver.ts b/src/queue/processors/http/deliver.ts index e06866da4..75e46559d 100644 --- a/src/queue/processors/http/deliver.ts +++ b/src/queue/processors/http/deliver.ts @@ -7,6 +7,11 @@ export default async (job: bq.Job, done: any): Promise => { await request(job.data.user, job.data.to, job.data.content); done(); } catch (res) { + if (!res.hasOwnProperty('statusCode')) { + console.warn(`deliver failed (unknown): ${res}`); + return done(); + } + if (res.statusCode == null) return done(); if (res.statusCode >= 400 && res.statusCode < 500) { // HTTPステータスコード4xxはクライアントエラーであり、それはつまり From f4d247cfae808bd0b4dc365f0c0383994052b3cb Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 7 Aug 2018 00:21:42 +0900 Subject: [PATCH 06/20] New translations ja.yml (English) --- locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.yml b/locales/en.yml index 0324ca49c..6e75b9838 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -207,7 +207,7 @@ common/views/components/games/reversi/reversi.room.vue: cancel-ready: "Cancel \"Ready\"" common/views/components/connect-failed.vue: title: "Unable to connect to the server" - description: "There is a problem with your Internet connection, or the server may be down or under maintenance. Please try again later." + description: "There is a problem with your Internet connection, or the server may be down or under maintenance. Please {try again} later." thanks: "Thank you for using Misskey." troubleshoot: "Troubleshoot" common/views/components/connect-failed.troubleshooter.vue: From d733a1b445db1e70f55b3cbac4786d81486bc34a Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 7 Aug 2018 03:20:26 +0900 Subject: [PATCH 07/20] i18n --- locales/ja.yml | 27 ++++++++++++------- .../app/common/scripts/date-stringify.ts | 13 --------- .../app/common/views/components/signin.vue | 2 +- src/client/app/desktop/api/update-avatar.ts | 16 +++++------ src/client/app/desktop/api/update-banner.ts | 16 +++++------ .../desktop/views/components/note-detail.vue | 3 +-- .../desktop/views/components/note-preview.vue | 3 +-- .../views/components/notes.note.sub.vue | 3 +-- .../desktop/views/components/notes.note.vue | 3 +-- .../views/components/user-lists-window.vue | 2 +- .../desktop/views/pages/user/user.profile.vue | 4 +-- .../app/desktop/views/widgets/post-form.vue | 2 +- 12 files changed, 43 insertions(+), 51 deletions(-) delete mode 100644 src/client/app/common/scripts/date-stringify.ts diff --git a/locales/ja.yml b/locales/ja.yml index 1e0359254..6aac8acca 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -11,6 +11,7 @@ common: warning: "Misskeyは広告を掲載していませんが、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。" application-authorization: "アプリの連携" close: "閉じる" + got-it: "わかった" customization-tips: title: "カスタマイズのヒント" paragraph1: "ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。" @@ -41,13 +42,6 @@ common: trash: "ゴミ箱" - date: - full-year: "年" - month: "月" - day: "日" - hours: "時" - minutes: "分" - weekday-short: sunday: "日" monday: "月" @@ -311,6 +305,8 @@ common/views/components/signin.vue: token: "トークン" signing-in: "やってます..." signin: "サインイン" + or: "または" + signin-with-twitter: "Twitterでログイン" common/views/components/signup.vue: username: "ユーザー名" @@ -438,6 +434,18 @@ common/views/pages/follow.vue: request-pending: "フォロー許可待ち" follow-request: "フォロー申請" +desktop: + banner-crop-title: "バナーとして表示する部分を選択" + banner: "バナー" + uploading-banner: "新しいバナーをアップロードしています" + banner-updated: "バナーを更新しました" + choose-banner: "バナーにする画像を選択" + avatar-crop-title: "アバターとして表示する部分を選択" + avatar: "アバター" + uploading-avatar: "新しいアバターをアップロードしています" + avatar-updated: "アバターを更新しました" + choose-avatar: "アバターにする画像を選択" + desktop/views/components/activity.chart.vue: total: "Black ... Total" notes: "Blue ... Notes" @@ -855,11 +863,10 @@ desktop/views/components/received-follow-requests-window.vue: accept: "承認" reject: "拒否" - - desktop/views/components/user-lists-window.vue: title: "リスト" create-list: "リストを作成" + list-name: "リスト名" desktop/views/components/user-preview.vue: notes: "投稿" @@ -964,6 +971,8 @@ desktop/views/pages/user/user.profile.vue: mute: "ミュートする" muted: "ミュートしています" unmute: "ミュート解除" + push-to-a-list: "リストに追加" + list-pushed: "{user}を{list}に追加しました。" desktop/views/pages/user/user.header.vue: posts: "投稿" diff --git a/src/client/app/common/scripts/date-stringify.ts b/src/client/app/common/scripts/date-stringify.ts deleted file mode 100644 index 2b8e52556..000000000 --- a/src/client/app/common/scripts/date-stringify.ts +++ /dev/null @@ -1,13 +0,0 @@ -export default date => { - if (typeof date == 'string') date = new Date(date); - return ( - date.getFullYear() + '%i18n:common.date.full-year%' + - (date.getMonth() + 1) + '%i18n:common.date.month%' + - date.getDate() + '%i18n:common.date.day%' + - ' ' + - date.getHours() + '%i18n:common.date.hours%' + - date.getMinutes() + '%i18n:common.date.minutes%' + - ' ' + - `(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})` - ); -}; diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue index 58241cef0..deaeeca6a 100644 --- a/src/client/app/common/views/components/signin.vue +++ b/src/client/app/common/views/components/signin.vue @@ -12,7 +12,7 @@ {{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }} -

またはTwitterでログイン

+

%i18n:@or%%i18n:@signin-with-twitter%

diff --git a/src/client/app/desktop/api/update-avatar.ts b/src/client/app/desktop/api/update-avatar.ts index 887367a24..83820f92b 100644 --- a/src/client/app/desktop/api/update-avatar.ts +++ b/src/client/app/desktop/api/update-avatar.ts @@ -8,7 +8,7 @@ export default (os: OS) => (cb, file = null) => { const w = os.new(CropWindow, { image: file, - title: 'アバターとして表示する部分を選択', + title: '%i18n:desktop.avatar-crop-title%', aspectRatio: 1 / 1 }); @@ -18,11 +18,11 @@ export default (os: OS) => (cb, file = null) => { data.append('file', blob, file.name + '.cropped.png'); os.api('drive/folders/find', { - name: 'アイコン' + name: '%i18n:desktop.avatar%' }).then(iconFolder => { if (iconFolder.length === 0) { os.api('drive/folders/create', { - name: 'アイコン' + name: '%i18n:desktop.avatar%' }).then(iconFolder => { upload(data, iconFolder); }); @@ -41,7 +41,7 @@ export default (os: OS) => (cb, file = null) => { const upload = (data, folder) => { const dialog = os.new(ProgressDialog, { - title: '新しいアバターをアップロードしています' + title: '%i18n:desktop.uploading-avatar%' }); document.body.appendChild(dialog.$el); @@ -76,10 +76,10 @@ export default (os: OS) => (cb, file = null) => { }); os.apis.dialog({ - title: '%fa:info-circle%アバターを更新しました', - text: '新しいアバターが反映されるまで時間がかかる場合があります。', + title: '%fa:info-circle% %i18n:desktop.avatar-updated%', + text: null, actions: [{ - text: 'わかった' + text: '%i18n:common.got-it%' }] }); @@ -92,7 +92,7 @@ export default (os: OS) => (cb, file = null) => { } else { os.apis.chooseDriveFile({ multiple: false, - title: '%fa:image%アバターにする画像を選択' + title: '%fa:image% %i18n:desktop.choose-avatar%' }).then(file => { fileSelected(file); }); diff --git a/src/client/app/desktop/api/update-banner.ts b/src/client/app/desktop/api/update-banner.ts index 4e6dd4e2c..33c4e306a 100644 --- a/src/client/app/desktop/api/update-banner.ts +++ b/src/client/app/desktop/api/update-banner.ts @@ -8,7 +8,7 @@ export default (os: OS) => { const cropImage = file => new Promise((resolve, reject) => { const w = os.new(CropWindow, { image: file, - title: 'バナーとして表示する部分を選択', + title: '%i18n:desktop.banner-crop-title%', aspectRatio: 16 / 9 }); @@ -18,11 +18,11 @@ export default (os: OS) => { data.append('file', blob, file.name + '.cropped.png'); os.api('drive/folders/find', { - name: 'バナー' + name: '%i18n:desktop.banner%' }).then(bannerFolder => { if (bannerFolder.length === 0) { os.api('drive/folders/create', { - name: 'バナー' + name: '%i18n:desktop.banner%' }).then(iconFolder => { resolve(upload(data, iconFolder)); }); @@ -43,7 +43,7 @@ export default (os: OS) => { const upload = (data, folder) => new Promise((resolve, reject) => { const dialog = os.new(ProgressDialog, { - title: '新しいバナーをアップロードしています' + title: '%i18n:desktop.uploading-banner%' }); document.body.appendChild(dialog.$el); @@ -79,10 +79,10 @@ export default (os: OS) => { }); os.apis.dialog({ - title: '%fa:info-circle%バナーを更新しました', - text: '新しいバナーが反映されるまで時間がかかる場合があります。', + title: '%fa:info-circle% %i18n:desktop.banner-updated%', + text: null, actions: [{ - text: 'わかった' + text: '%i18n:common.got-it%' }] }); @@ -95,7 +95,7 @@ export default (os: OS) => { ? Promise.resolve(file) : os.apis.chooseDriveFile({ multiple: false, - title: '%fa:image%バナーにする画像を選択' + title: '%fa:image% %i18n:desktop.choose-banner%' }); return selectedFile diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue index 36a588922..b6980fae7 100644 --- a/src/client/app/desktop/views/components/note-detail.vue +++ b/src/client/app/desktop/views/components/note-detail.vue @@ -79,7 +79,6 @@