From 69a029e57ad9b6b484bd3460eeafa4d0cc997cd8 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 c4ac527495d15c3b47e1c56f35bdb63c4182a55a 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 cc2c4e4a6a9a983c97e8a3b5f9fa40c2df3f45d4 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 b36abcc2101184d0bb1336d52410046906a62f10 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 b4f66ee9256c784f5d0fdf4aaef456bf63f25bf0 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 1b11bf208067c7ea4a65d513cc15187b565df84b 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 5279477a4a1aeb51e8826e5e4243060b7f007f84 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 @@