diff --git a/.config/example.yml b/.config/example.yml index 427377a694..b84a50c525 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -1,67 +1,136 @@ -# インスタンス名 -name: +name: example-instance-name # Name of your instance +description: example-description # Description of your instance -# インスタンスの紹介 -description: - -# サーバーのメンテナ情報 maintainer: - # メンテナの名前 - name: + name: example-maitainer-name # Your name + url: http://example.com/ # Your contact (http or mailto) + repository_url: https://github.com/syuilo/misskey # Repository URL + feedback_url: https://github.com/syuilo/misskey/issues # Feedback URL (e.g. github issue) - # メンテナの連絡先(URLかmailto形式のURL) - url: +# URL and Port settings overview +# e.g., If you want to realize following structure: +# +# +--- https://example.com:123 ----------+ +# +------+ |+-------------+ +---------------+| +# | User | ---> || Proxy (123) | ---> | Misskey (456) || +# +------+ |+-------------+ +---------------+| +# +--------------------------------------+ +# +# You need to set 'https://example.com:123' to 'url' prop and +# You need to set 456 to 'port' prop. +# +# In other words, the 'url' prop should be the final accessible URL seen by a user. +# 'port' prop is a port that the Misskey server should actually listen +# on and it is not necessarily the port that a user accesses. -# (Misskeyを動かす)URL -url: +url: http://localhost/ -# 待受ポート -port: +# A port that your Misskey server should listen. +# This value is not a port to use when accessing with a browser. +port: 80 -# TLSの設定(利用しない場合は省略してください) -https: - # 証明書のパス... - key: - cert: - -# MongoDBの設定 mongodb: host: localhost port: 27017 db: misskey - user: - pass: + user: example-misskey-user + pass: example-misskey-pass -# Redisの設定 redis: host: localhost port: 6379 - pass: + pass: example-pass -# reCAPTCHAの設定 -recaptcha: - site_key: - secret_key: +# Drive capacity of a local user (MB) +localDriveCapacityMb: 256 -# ServiceWrokerの設定 -sw: - # VAPIDの公開鍵 - public_key: +# Drive capacity of a remote user (MB) +remoteDriveCapacityMb: 8 - # VAPIDの秘密鍵 - private_key: - -# Google Maps API -google_maps_api_key: - -# Twitterインテグレーションの設定(利用しない場合は省略可能) -twitter: - # インテグレーション用アプリのコンシューマーキー - consumer_key: - - # インテグレーション用アプリのコンシューマーシークレット - consumer_secret: - -# true にすると、リモートのファイルをキャッシュしなくなります(直リンクします)。 -# ストレージ容量を節約することができますが、「リモートメディアを表示しない」設定をオンにしているユーザーは、リモートの画像などは見えなくなります。 +# If enabled: +# Server will not cache remote files (Using direct link instead). +# You can save your storage. +# Users cannot see remote images when they turn off "Show media from a remote server" setting. preventCache: false + +drive: + storage: 'db' + + # OR + + # storage: 'minio' + # bucket: + # prefix: + # config: + # endPoint: + # port: + # secure: + # accessKey: + # secretKey: + + # S3 example + # storage: 'minio' + # bucket: bucket-name + # prefix: files + # config: + # endPoint: s3-us-west-2.amazonaws.com + # region: us-west-2 + # secure: true + # accessKey: XXX + # secretKey: YYY + + # S3 example (with CDN, custom domain) + # storage: 'minio' + # bucket: drive.example.com + # prefix: files + # baseUrl: https://drive.example.com + # config: + # endPoint: s3-us-west-2.amazonaws.com + # region: us-west-2 + # secure: true + # accessKey: XXX + # secretKey: YYY + +# +# Below settings are optional +# + +# TLS +# https: +# # path for certification +# key: example-tls-key +# cert: example-tls-cert + +# Elasticsearch +# elasticsearch: +# host: localhost +# port: 9200 +# pass: null + +# reCAPTCHA +# recaptcha: +# site_key: example-site-key +# secret_key: example-secret-key + +# ServiceWorker +# sw: +# # Public key of VAPID +# public_key: example-sw-public-key + +# # Private key of VAPID +# private_key: example-sw-private-key + +# google_maps_api_key: example-google-maps-api-key + +# Twitter integration +# twitter: +# consumer_key: example-twitter-consumer-key +# consumer_secret: example-twitter-consumer-secret-key + +# Ghost +# Ghost account is an account used for the purpose of delegating +# followers when putting users in the list. +# ghost: user-id-of-your-ghost-account + +# Clustering +# clusterLimit: 1 diff --git a/.gitattributes b/.gitattributes index 9ea77a4b0e..58a7812fdf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,4 @@ *.psd -diff -text *.ai -diff -text yarn.lock -diff -text +package-lock.json -diff -text diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index e75e7608e7..0000000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..e4e5282e56 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,22 @@ +--- +name: Bug Report +about: Create a report to help us improve +--- + +# Summary + + +# Expected Behavior + + +# Actual Behavior + + +# Steps to Reproduce +1. +2. +3. + +# Environment + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..018e68df97 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: Feature Request +about: Suggest an idea for this project +--- + +# Summary + + +# Environment + + diff --git a/.gitignore b/.gitignore index 197c1ec4d2..51e6a31b74 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ npm-debug.log *.pem run.bat api-docs.json -package-lock.json *.log diff --git a/.npmrc b/.npmrc index 2fcf1d76c2..b680f3f72d 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,2 @@ -package-lock = false save-exact=true +package-lock = false diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..36cfce106e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "ducksoupdev.vue2", + "editorconfig.editorconfig", + "eg2.tslint", + "eg2.vscode-npm-script", + "hollowtree.vue-snippets", + "ms-vscode.typescript-javascript-grammar", + "octref.vetur", + "sysoev.language-stylus" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0214ff9f..08ad99977f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ ChangeLog This document describes breaking changes only. +5.0.0 +----- + +### Migration + +起動する前に、`node cli/migration/5.0.0`してください。 + +Please run `node cli/migration/5.0.0` before launch. + 4.0.0 ----- diff --git a/README.md b/README.md index 197fd6952d..9e1c834b40 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![][dependencies-badge]][dependencies-link] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![Greenkeeper badge](https://badges.greenkeeper.io/syuilo/misskey.svg)](https://greenkeeper.io/) -> Lead Maintainer: [syuilo][syuilo-link] +**Microblogging. Redefined.** **[Misskey](https://misskey.xyz)** is a completely open source, ultimately sophisticated professional microblogging software. @@ -18,14 +18,13 @@ ultimately sophisticated professional microblogging software. :sparkles: Features ---------------------------------------------------------------- +* Rich text contents * Reactions * User lists -* Customizable column view (known as MisskeyDeck) +* Customizable column view (called MisskeyDeck) * and widgets! * Private messages -* Mute -* Real-time timelines -* ActivityPub compatible +* ActivityPub support and more! You can see it with your own eyes at [misskey.xyz](https://misskey.xyz). @@ -44,15 +43,15 @@ If you want to... :heart: Backers & Sponsors ---------------------------------------------------------------- -| ![][nagarus-icon] | ![][dansup-icon] | -|:-:|:-:| -| [nagarus][nagarus-link] | [dansup][dansup-link] | +| | | | | +|:-:|:-:|:-:|:-:| +| [Gargron](https://www.patreon.com/mastodon) | [39ff](https://www.patreon.com/user/creators?u=12378075) | [dansup](https://www.patreon.com/dansup) | [Takashi Shibuya](https://www.patreon.com/user/creators?u=12531784) | :four_leaf_clover: Copyright ---------------------------------------------------------------- > Copyright (c) 2014-2018 syuilo -Misskey is an open-source software licensed under [GNU AGPLv3](LICENSE). +Misskey is an open-source software licensed under the [GNU AGPLv3](LICENSE). [![][agpl-3.0-badge]][AGPL-3.0] @@ -73,9 +72,3 @@ Misskey is an open-source software licensed under [GNU AGPLv3](LICENSE). [syuilo-link]: https://syuilo.com [syuilo-icon]: https://avatars2.githubusercontent.com/u/4439005?v=3&s=70 - -[nagarus-link]: https://www.patreon.com/user/creators?u=11601413 -[nagarus-icon]: https://c10.patreonusercontent.com/3/eyJ2IjoiMSIsInciOjIwMH0%3D/patreon-media/user/11601413/20cb15f209924302b399b99d3c98b850?token-time=2145916800&token-hash=IO31nK6VZCMWBWU2VAk2c824BX2QZ4DNPKyHHZXS0iw%3D -[dansup-link]: https://www.patreon.com/dansup -[dansup-icon]: https://c10.patreonusercontent.com/3/eyJ2IjoiMSIsInciOjIwMH0%3D/patreon-media/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb?token-time=2145916800&token-hash=opXAM_pnhUTuN1jCA6p_Nn_YsaqohY465YFjWFqMEEE%3D - diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 280d6a99ae..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,41 +0,0 @@ -# appveyor file -# http://www.appveyor.com/docs/appveyor-yml - -environment: - matrix: - - nodejs_version: 10.1.0 - -cache: - - node_modules - -build: off - -install: - # Update Node.js - # 標準で入っている Node.js を更新します (2014/11/13 時点では、v0.10.32 が標準) - - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) - - node --version - - # Update NPM - - npm install -g npm - - npm --version - - # Update node-gyp - # 必須! node-gyp のバージョンを上げないと、ネイティブモジュールのコンパイルに失敗します - - npm install -g node-gyp - - - npm install - -init: - # git clone の際の改行を変換しないようにします - - git config --global core.autocrlf false - -before_test: - # 設定ファイルを配置 - - cp ./.travis/default.yml ./.config - - cp ./.travis/test.yml ./.config - - - npm run build - -test_script: - - npm test diff --git a/assets/apple-touch-icon.png b/assets/apple-touch-icon.png index b3c4be42af..fc91b6bab9 100644 Binary files a/assets/apple-touch-icon.png and b/assets/apple-touch-icon.png differ diff --git a/assets/favicon/favicon.png b/assets/favicon/favicon.png index 2efa2cad61..de535d7e2a 100644 Binary files a/assets/favicon/favicon.png and b/assets/favicon/favicon.png differ diff --git a/assets/icons/128.png b/assets/icons/128.png index 0bfa5aeb3e..c0f5db0cef 100644 Binary files a/assets/icons/128.png and b/assets/icons/128.png differ diff --git a/assets/icons/192.png b/assets/icons/192.png index 3fa9b0dab7..0f92a83e45 100644 Binary files a/assets/icons/192.png and b/assets/icons/192.png differ diff --git a/assets/icons/256.png b/assets/icons/256.png index b3c4be42af..fc91b6bab9 100644 Binary files a/assets/icons/256.png and b/assets/icons/256.png differ diff --git a/assets/icons/64.png b/assets/icons/64.png index ab35f8fec1..2b14ba3b5f 100644 Binary files a/assets/icons/64.png and b/assets/icons/64.png differ diff --git a/assets/title.png b/assets/title.png index cacbb248d3..7cb7dcdd6e 100644 Binary files a/assets/title.png and b/assets/title.png differ diff --git a/cli/clean-cached-remote-files.js b/cli/clean-cached-remote-files.js index a9c38a4cdf..5b388c73b4 100644 --- a/cli/clean-cached-remote-files.js +++ b/cli/clean-cached-remote-files.js @@ -9,7 +9,7 @@ const q = { 'metadata._user.host': { $ne: null }, - 'metadata.isMetaOnly': false + 'metadata.withoutChunks': false }; async function main() { @@ -57,7 +57,7 @@ async function main() { DriveFile.update({ _id: file._id }, { $set: { - 'metadata.isMetaOnly': true + 'metadata.withoutChunks': true } }) ]).then(async () => { diff --git a/cli/init.js b/cli/init.js deleted file mode 100644 index 5a36509574..0000000000 --- a/cli/init.js +++ /dev/null @@ -1,168 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const yaml = require('js-yaml'); -const inquirer = require('inquirer'); -const chalk = require('chalk'); - -const configDirPath = `${__dirname}/../.config`; -const configPath = `${configDirPath}/default.yml`; - -const form = [{ - type: 'input', - name: 'maintainerName', - message: 'Your name:' -}, { - type: 'input', - name: 'maintainerUrl', - message: 'Your home page URL or your mailto URL:' -}, { - type: 'input', - name: 'url', - message: 'URL you want to run Misskey:', - validate: function(wannabeurl) { - return wannabeurl.match('^http\(s?\)://') ? true : - 'URL needs to start with http:// or https://'; - } -}, { - type: 'input', - name: 'port', - message: 'Listen port (e.g. 443):' -}, { - type: 'confirm', - name: 'https', - message: 'Use TLS?', - default: false -}, { - type: 'input', - name: 'https_key', - message: 'Path of tls key:', - when: ctx => ctx.https -}, { - type: 'input', - name: 'https_cert', - message: 'Path of tls cert:', - when: ctx => ctx.https -}, { - type: 'input', - name: 'https_ca', - message: 'Path of tls ca:', - when: ctx => ctx.https -}, { - type: 'input', - name: 'mongo_host', - message: 'MongoDB\'s host:', - default: 'localhost' -}, { - type: 'input', - name: 'mongo_port', - message: 'MongoDB\'s port:', - default: '27017' -}, { - type: 'input', - name: 'mongo_db', - message: 'MongoDB\'s db:', - default: 'misskey' -}, { - type: 'input', - name: 'mongo_user', - message: 'MongoDB\'s user:' -}, { - type: 'password', - name: 'mongo_pass', - message: 'MongoDB\'s password:' -}, { - type: 'input', - name: 'redis_host', - message: 'Redis\'s host:', - default: 'localhost' -}, { - type: 'input', - name: 'redis_port', - message: 'Redis\'s port:', - default: '6379' -}, { - type: 'password', - name: 'redis_pass', - message: 'Redis\'s password:' -}, { - type: 'confirm', - name: 'elasticsearch', - message: 'Use Elasticsearch?', - default: false -}, { - type: 'input', - name: 'es_host', - message: 'Elasticsearch\'s host:', - default: 'localhost', - when: ctx => ctx.elasticsearch -}, { - type: 'input', - name: 'es_port', - message: 'Elasticsearch\'s port:', - default: '9200', - when: ctx => ctx.elasticsearch -}, { - type: 'password', - name: 'es_pass', - message: 'Elasticsearch\'s password:', - when: ctx => ctx.elasticsearch -}, { - type: 'input', - name: 'recaptcha_site', - message: 'reCAPTCHA\'s site key:' -}, { - type: 'input', - name: 'recaptcha_secret', - message: 'reCAPTCHA\'s secret key:' -}]; - -inquirer.prompt(form).then(as => { - // Mapping answers - const conf = { - maintainer: { - name: as['maintainerName'], - url: as['maintainerUrl'] - }, - url: as['url'], - port: parseInt(as['port'], 10), - mongodb: { - host: as['mongo_host'], - port: parseInt(as['mongo_port'], 10), - db: as['mongo_db'], - user: as['mongo_user'], - pass: as['mongo_pass'] - }, - redis: { - host: as['redis_host'], - port: parseInt(as['redis_port'], 10), - pass: as['redis_pass'] - }, - elasticsearch: { - enable: as['elasticsearch'], - host: as['es_host'] || null, - port: parseInt(as['es_port'], 10) || null, - pass: as['es_pass'] || null - }, - recaptcha: { - site_key: as['recaptcha_site'], - secret_key: as['recaptcha_secret'] - } - }; - - if (as['https']) { - conf.https = { - key: as['https_key'] || null, - cert: as['https_cert'] || null, - ca: as['https_ca'] || null - }; - } - - console.log(`Thanks. Writing the configuration to ${chalk.bold(path.resolve(configPath))}`); - - try { - fs.writeFileSync(configPath, yaml.dump(conf)); - console.log(chalk.green('Well done.')); - } catch (e) { - console.error(e); - } -}); diff --git a/cli/mark-admin.js b/cli/mark-admin.js new file mode 100644 index 0000000000..e10035fde9 --- /dev/null +++ b/cli/mark-admin.js @@ -0,0 +1,23 @@ +const mongo = require('mongodb'); +const User = require('../built/models/user').default; + +const args = process.argv.slice(2); + +const user = args[0]; + +const q = user.startsWith('@') ? { + username: user.split('@')[1], + host: user.split('@')[2] || null +} : { _id: new mongo.ObjectID(user) }; + +console.log(`Mark as admin ${user}...`); + +User.update(q, { + $set: { + isAdmin: true + } +}).then(() => { + console.log(`Done ${user}`); +}, e => { + console.error(e); +}); diff --git a/cli/mark-verified.js b/cli/mark-verified.js new file mode 100644 index 0000000000..cdee91ddca --- /dev/null +++ b/cli/mark-verified.js @@ -0,0 +1,23 @@ +const mongo = require('mongodb'); +const User = require('../built/models/user').default; + +const args = process.argv.slice(2); + +const user = args[0]; + +const q = user.startsWith('@') ? { + username: user.split('@')[1], + host: user.split('@')[2] || null +} : { _id: new mongo.ObjectID(user) }; + +console.log(`Mark as verfied ${user}...`); + +User.update(q, { + $set: { + isVerified: true + } +}).then(() => { + console.log(`Done ${user}`); +}, e => { + console.error(e); +}); diff --git a/migration/2.0.0.js b/cli/migration/2.0.0.js similarity index 88% rename from migration/2.0.0.js rename to cli/migration/2.0.0.js index eb8f5730c7..f7298972e5 100644 --- a/migration/2.0.0.js +++ b/cli/migration/2.0.0.js @@ -3,8 +3,8 @@ const chalk = require('chalk'); const sequential = require('promise-sequential'); -const { default: User } = require('../built/models/user'); -const { default: DriveFile } = require('../built/models/drive-file'); +const { default: User } = require('../../built/models/user'); +const { default: DriveFile } = require('../../built/models/drive-file'); async function main() { const promiseGens = []; diff --git a/migration/2.4.0.js b/cli/migration/2.4.0.js similarity index 91% rename from migration/2.4.0.js rename to cli/migration/2.4.0.js index e9584a1dfc..aa37849aa1 100644 --- a/migration/2.4.0.js +++ b/cli/migration/2.4.0.js @@ -3,8 +3,8 @@ const chalk = require('chalk'); const sequential = require('promise-sequential'); -const { default: User } = require('../built/models/user'); -const { default: DriveFile } = require('../built/models/drive-file'); +const { default: User } = require('../../built/models/user'); +const { default: DriveFile } = require('../../built/models/drive-file'); async function main() { const promiseGens = []; diff --git a/cli/migration/5.0.0.js b/cli/migration/5.0.0.js new file mode 100644 index 0000000000..bef103fe4a --- /dev/null +++ b/cli/migration/5.0.0.js @@ -0,0 +1,9 @@ +const { default: DriveFile } = require('../../built/models/drive-file'); + +DriveFile.update({}, { + $rename: { + 'metadata.isMetaOnly': 'metadata.withoutChunks' + } +}, { + multi: true +}); diff --git a/cli/reset-password.js b/cli/reset-password.js new file mode 100644 index 0000000000..d94c90f3d5 --- /dev/null +++ b/cli/reset-password.js @@ -0,0 +1,29 @@ +const mongo = require('mongodb'); +const bcrypt = require('bcryptjs'); +const User = require('../built/models/user').default; + +const args = process.argv.slice(2); + +const user = args[0]; + +const q = user.startsWith('@') ? { + username: user.split('@')[1], + host: user.split('@')[2] || null +} : { _id: new mongo.ObjectID(user) }; + +console.log(`Resetting password for ${user}...`); + +const passwd = 'yo'; + +// Generate hash of password +const hash = bcrypt.hashSync(passwd); + +User.update(q, { + $set: { + password: hash + } +}).then(() => { + console.log(`Password of ${user} is now '${passwd}'`); +}, e => { + console.error(e); +}); diff --git a/docker/Dockerfile b/docker/Dockerfile index 7cee650de3..f089c02545 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -14,7 +14,7 @@ RUN pacman -S --noconfirm pacman RUN pacman-db-upgrade RUN pacman -S --noconfirm archlinux-keyring RUN pacman -Syyu --noconfirm -RUN pacman -S --noconfirm git nodejs npm mongodb redis imagemagick +RUN pacman -S --noconfirm git nodejs npm mongodb redis COPY misskey.sh /root/misskey.sh RUN chmod u+x /root/misskey.sh diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..b4ba573439 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,6 @@ +# Docs +These docs are for contributors of Misskey or admins of instance of Misskey. +Docs for users are located in `src/docs`. + +これらのドキュメントはMisskeyの開発者またはMisskeyインスタンス運営者向けです。 +利用者向けのドキュメントは`src/docs`にあります。 diff --git a/docs/manage.en.md b/docs/manage.en.md new file mode 100644 index 0000000000..a7296ce479 --- /dev/null +++ b/docs/manage.en.md @@ -0,0 +1,46 @@ +# Management guide + +## Check the status of the job queue +coming soon + +## Mark as 'admin' user +``` shell +node cli/mark-admin (User-ID or Username) +``` + +## Mark as 'verified' user +``` shell +node cli/mark-verified (User-ID or Username) +``` + +## Suspend users +``` shell +node cli/suspend (User-ID or Username) +``` +e.g. +``` shell +# Use id +node cli/suspend 57d01a501fdf2d07be417afe + +# Use username +node cli/suspend @syuilo + +# Use username (remote) +node cli/suspend @syuilo@misskey.xyz +``` + +## Reset password +``` shell +node cli/reset-password (User-ID or Username) +``` + +## Clean up cached remote files +``` shell +node cli/clean-cached-remote-files +``` + +## Clean up unused drive files +``` shell +node cli/clean-unused-drive-files +``` +> We recommend that you announce a user that unused drive files will be deleted before performing this operation, as it may delete the user's important files. diff --git a/docs/manage.ja.md b/docs/manage.ja.md index d56ed4c19b..f289037ad3 100644 --- a/docs/manage.ja.md +++ b/docs/manage.ja.md @@ -1,13 +1,46 @@ # 運営ガイド ## ジョブキューの状態を調べる -Misskeyのディレクトリで: +coming soon + +## 管理者ユーザーを設定する ``` shell -node_modules/kue/bin/kue-dashboard -p 3050 +node cli/mark-admin (ユーザーID または ユーザー名) +``` + +## 'verified'ユーザーを設定する +``` shell +node cli/mark-verified (ユーザーID または ユーザー名) ``` -ポート3050にアクセスするとUIが表示されます ## ユーザーを凍結する ``` shell -node cli/suspend (ユーザーID) +node cli/suspend (ユーザーID または ユーザー名) ``` +例: +``` shell +# ユーザーID +node cli/suspend 57d01a501fdf2d07be417afe + +# ユーザー名 +node cli/suspend @syuilo + +# ユーザー名 (リモート) +node cli/suspend @syuilo@misskey.xyz +``` + +## ユーザーのパスワードをリセットする +``` shell +node cli/reset-password (ユーザーID または ユーザー名) +``` + +## キャッシュされたリモートファイルをクリーンアップする +``` shell +node cli/clean-cached-remote-files +``` + +## 使われていないドライブのファイルをクリーンアップする +``` shell +node cli/clean-unused-drive-files +``` +> ユーザーの大事なファイルを削除する可能性があるので、この操作を実行する前にユーザーに告知することをお勧めします。 diff --git a/docs/setup.en.md b/docs/setup.en.md index 28e025521b..56632cc361 100644 --- a/docs/setup.en.md +++ b/docs/setup.en.md @@ -8,18 +8,13 @@ This guide describes how to install and setup Misskey. ---------------------------------------------------------------- -*1.* reCAPTCHA tokens +*1.* Create Misskey user ---------------------------------------------------------------- -Misskey requires reCAPTCHA tokens. -Please visit https://www.google.com/recaptcha/intro/ and generate keys. +Running misskey on root is not a good idea so we create a user for that. +In debian for exemple : -*(optional)* Generating VAPID keys ----------------------------------------------------------------- -If you want to enable ServiceWroker, you need to generate VAPID keys: - -``` shell -npm install web-push -g -web-push generate-vapid-keys +``` +adduser --disabled-password --disabled-login misskey ``` *2.* Install dependencies @@ -27,25 +22,52 @@ web-push generate-vapid-keys Please install and setup these softwares: #### Dependencies :package: -* *Node.js* and *npm* -* **[MongoDB](https://www.mongodb.com/)** +* **[Node.js](https://nodejs.org/en/)** +* **[MongoDB](https://www.mongodb.com/)** >= 3.6 * **[Redis](https://redis.io/)** -* **[ImageMagick](http://www.imagemagick.org/script/index.php)** >= 7.0 ##### Optional * [Elasticsearch](https://www.elastic.co/) - used to provide searching feature instead of MongoDB -*3.* Install Misskey ----------------------------------------------------------------- -1. `git clone -b master git://github.com/syuilo/misskey.git` -2. `cd misskey` -3. `npm install` -*4.* Prepare configuration +*3.* Setup MongoDB ---------------------------------------------------------------- -You need to generate config file via `npm run config` command. +In root : +1. `mongo` Go to the mongo shell +2. `use misskey` Use the misskey database +3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db. +4. `db.createUser( { user: "misskey", pwd: "", roles: [ { role: "readWrite", db: "misskey" } ] } )` Create the misskey user. +5. `exit` You're done ! -*5.* Build Misskey +*4.* Install Misskey +---------------------------------------------------------------- +1. `su - misskey` Connect to misskey user. +2. `git clone -b master git://github.com/syuilo/misskey.git` Clone the misskey repo from master branch. +3. `cd misskey` Navigate to misskey directory +4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) +5. `npm install` Install misskey dependencies. + +*(optional)* reCAPTCHA tokens +---------------------------------------------------------------- +If you want to enable reCAPTCHA, you need to generate reCAPTCHA tokens: +Please visit https://www.google.com/recaptcha/intro/ and generate keys. + +*(optional)* Generating VAPID keys +---------------------------------------------------------------- +If you want to enable ServiceWroker, you need to generate VAPID keys: +Unless you have set your global node_modules location elsewhere, you need to run this in root. + +``` shell +npm install web-push -g +web-push generate-vapid-keys +``` + +*5.* Make configuration file +---------------------------------------------------------------- +1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`. +2. Edit `default.yml` + +*6.* Build Misskey ---------------------------------------------------------------- Build misskey with the following: @@ -61,14 +83,48 @@ If you're still encountering errors about some modules, use node-gyp: 3. `node-gyp build` 4. `npm run build` -*6.* That is it. +*7.* That is it. ---------------------------------------------------------------- Well done! Now, you have an environment that run to Misskey. -### Launch -Just `sudo npm start`. GLHF! +### Launch normally +Just `npm start`. GLHF! + +### Launch with systemd + +1. Create a systemd service here: `/etc/systemd/system/misskey.service` +2. Edit it, and paste this and save: + +``` +[Unit] +Description=Misskey daemon + +[Service] +Type=simple +User=misskey +ExecStart=/usr/bin/npm start +WorkingDirectory=/home/misskey/misskey +TimeoutSec=60 +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=misskey +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +3. `systemctl daemon-reload ; systemctl enable misskey` Reload systemd and enable the misskey service. +4. `systemctl start misskey` Start the misskey service. + +You can check if the service is running with `systemctl status misskey`. ### Way to Update to latest version of your Misskey -1. `git reset --hard && git pull origin master` -2. `npm install` -3. `npm run build` +1. `git fetch` +2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` +3. `npm install` +4. `npm run build` + +---------------------------------------------------------------- + +If you have any questions or troubles, feel free to contact us! diff --git a/docs/setup.ja.md b/docs/setup.ja.md index 0f1e46761b..7c701b019f 100644 --- a/docs/setup.ja.md +++ b/docs/setup.ja.md @@ -8,10 +8,48 @@ Misskeyサーバーの構築にご関心をお寄せいただきありがとう ---------------------------------------------------------------- -*1.* reCAPTCHAトークンの用意 +*1.* Misskeyユーザーの作成 ---------------------------------------------------------------- -MisskeyはreCAPTCHAトークンを必要とします。 -https://www.google.com/recaptcha/intro/ にアクセスしてトークンを生成してください。 +Misskeyのrootで実行しない方がよいため、代わりにユーザーを作成します。 +Debianの例: + +``` +adduser --disabled-password --disabled-login misskey +``` + +*2.* 依存関係をインストールする +---------------------------------------------------------------- +これらのソフトウェアをインストール・設定してください: + +#### 依存関係 :package: +* **[Node.js](https://nodejs.org/en/)** +* **[MongoDB](https://www.mongodb.com/)** (3.6以上) +* **[Redis](https://redis.io/)** + +##### オプション +* [Elasticsearch](https://www.elastic.co/) - 検索機能を向上させるために用います。 + +*3.* MongoDBの設定 +---------------------------------------------------------------- +ルートで: +1. `mongo` mongoシェルを起動 +2. `use misskey` misskeyデータベースを使用 +3. `db.users.save( {dummy:"dummy"} )` ダミーデータを書き込みDBを初期化 +4. `db.createUser( { user: "misskey", pwd: "", roles: [ { role: "readWrite", db: "misskey" } ] } )` misskeyユーザーを作成 +5. `exit` mongoシェルを終了 + +*4.* Misskeyのインストール +---------------------------------------------------------------- +1. `su - misskey` misskeyユーザーを使用 +2. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン +3. `cd misskey` misskeyディレクトリに移動 +4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認 +5. `npm install` Misskeyの依存パッケージをインストール + +*(オプション)* reCAPTCHAトークン +---------------------------------------------------------------- +reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。 +https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。 *(オプション)* VAPIDキーペアの生成 ---------------------------------------------------------------- @@ -22,56 +60,67 @@ npm install web-push -g web-push generate-vapid-keys ``` -*2.* 依存関係をインストールする +*5.* 設定ファイルを作成する ---------------------------------------------------------------- -これらのソフトウェアをインストール・設定してください: +1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする。 +2. `default.yml` を編集する。 -#### 依存関係 :package: -* *Node.js* と *npm* -* **[MongoDB](https://www.mongodb.com/)** -* **[Redis](https://redis.io/)** -* **[ImageMagick](http://www.imagemagick.org/script/index.php)** - -##### オプション -* [Elasticsearch](https://www.elastic.co/) - 検索機能を向上させるために用います。 - -*3.* Misskeyのインストール +*6.* Misskeyのビルド ---------------------------------------------------------------- -1. `git clone -b master git://github.com/syuilo/misskey.git` -2. `cd misskey` -3. `npm install` -*4.* 設定ファイルを用意する ----------------------------------------------------------------- -`npm run config`コマンドを利用して、ガイドに従って情報を入力してください。 +次のコマンドでMisskeyをビルドしてください: -*5.* Misskeyのビルド ----------------------------------------------------------------- +`npm run build` + +Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。 + +何らかのモジュールでエラーが発生する場合はnode-gypを使ってください: 1. `npm install -g node-gyp` 2. `node-gyp configure` 3. `node-gyp build` 4. `npm run build` -*6.* 以上です! +*7.* 以上です! ---------------------------------------------------------------- お疲れ様でした。これでMisskeyを動かす準備は整いました。 -### 起動 -`sudo npm start`するだけです。GLHF! +### 通常起動 +`npm start`するだけです。GLHF! + +### systemdを用いた起動 +1. systemdサービスのファイルを作成: `/etc/systemd/system/misskey.service` +2. エディタで開き、以下のコードを貼り付けて保存: + +``` +[Unit] +Description=Misskey daemon + +[Service] +Type=simple +User=misskey +ExecStart=/usr/bin/npm start +WorkingDirectory=/home/misskey/misskey +TimeoutSec=60 +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=misskey +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +3. `systemctl daemon-reload ; systemctl enable misskey` systemdを再読み込みしmisskeyサービスを有効化 +4. `systemctl start misskey` misskeyサービスの起動 + +`systemctl status misskey`と入力すると、サービスの状態を調べることができます。 ### Misskeyを最新バージョンにアップデートする方法: -1. `git reset --hard && git pull origin master` -2. `npm install` -3. `npm run build` +1. `git fetch` +2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` +3. `npm install` +4. `npm run build` -## メモリが足りなくてビルドできない場合 -Misskeyの(クライアントの)ビルドには、目安として8GBくらいのメモリを必要とします。 -VPSなどでビルドする時は、もしかしたらメモリが足りなくなる可能性があります。 -そうなった場合、もしVPSではなくあなたのPCが十分なメモリを搭載しているなら、あなたのPC上でビルドし、生成されたファイルをVPSにFTPでアップロードする方法を採ることができます。 +---------------------------------------------------------------- -1. あなたのPC上にMisskeyをインストールする -2. 設定ファイルを用意する。設定ファイルは、サーバーに合わせた設定にします。 -3. npm run webpack -4. built/client をサーバーにアップロードする -5. サーバー上で、npm run gulp -6. 完了 +なにかお困りのことがありましたらお気軽にご連絡ください。 diff --git a/docs/translate.en.md b/docs/translate.en.md index cedb0bafc3..8ac65d3564 100644 --- a/docs/translate.en.md +++ b/docs/translate.en.md @@ -4,19 +4,19 @@ Misskey's Translation If you find an untranslated part on Misskey: -------------------------------------------- -1. Look for untranslated parts in the miskey's source code. +1. Look for untranslated parts in the misskey's source code. - For instance, if you find an untranslated part in: `src/client/app/mobile/views/pages/home.vue`. 2. Replace the untranslated portion with a character string of the form `%i18n:@foo%`. - In fact, `foo` should be a word that is appropriate for the situation and is easy to understand in English. - For example, if the untranslated portion is the following "タイムライン" you must write: `%i18n:@timeline%`. -3. Open each language file in /locales, check whether the file name (path) found in step 1 exists, if not, create it. +3. Open the `locales/ja.yml`, check whether the file name (path) found in step 1 exists, if not, create it. - Do not put the beginning of the path `src/client/app/` in the locale file. - For example, in this case we want to modify untranslated parts of `src/client/app/mobile/views/pages/home.vue`, so the key is `mobile/views/pages/home.vue`. -4. Add the translated text property using the `foo` keyword below the path that you found or created in step 2. Make sure to type your text in quotation marks. Text should always be inside of quotes. - - For example, in this case we add timeline: `timeline: "Timeline"` to `locales/en.yml`, and `timeline: "タイムライン"` to `locales/ja.yml`. +4. Add the text property using the `foo` keyword below the path that you found or created in step 2. Make sure to type your text in quotation marks. Text should always be inside of quotes. + - For example, in this case we add timeline: `timeline: "タイムライン"` to `locales/ja.yml`. 5. And done! diff --git a/docs/translate.ja.md b/docs/translate.ja.md index 4804478d1b..8a2ef25005 100644 --- a/docs/translate.ja.md +++ b/docs/translate.ja.md @@ -11,12 +11,12 @@ Misskey内の未翻訳箇所を見つけたら - `foo`は実際にはその場に適したわかりやすい(英語の)名前にしてください。 - 例えば未翻訳箇所が「タイムライン」というテキストだった場合、`%i18n:@timeline%`のようにします。 -3. /locales 内にあるそれぞれの言語ファイルを開き、1.で見つけたファイル名(パス)のキーが存在するか確認し、無ければ作成してください。 +3. `locales/ja.yml`を開き、1.で見つけたファイル名(パス)のキーが存在するか確認し、無ければ作成してください。 - パスの`src/client/app/`は省略してください。 - 例えば、今回の例では`src/client/app/mobile/views/pages/home.vue`の未翻訳箇所を修正したいので、キーは`mobile/views/pages/home.vue`になります。 -4. そのキーの直下に2.で置換した`foo`の部分をキーとし、翻訳後のテキストを値とするプロパティを追加します。 - - 例えば、今回の例で言うと`locales/ja.yml`に`timeline: "タイムライン"`、`locales/en.yml`に`timeline: "Timeline"`を追加します。 +4. そのキーの直下に2.で置換した`foo`の部分をキーとし、テキストを値とするプロパティを追加します。 + - 例えば、今回の例で言うと`locales/ja.yml`に`timeline: "タイムライン"`を追加します。 5. 完了です! diff --git a/elasticsearch/README.md b/elasticsearch/README.md deleted file mode 100644 index c7fcb245f0..0000000000 --- a/elasticsearch/README.md +++ /dev/null @@ -1,6 +0,0 @@ -How to create indexes -===================== - -``` shell -curl -XPOST localhost:9200/misskey -d @path/to/mappings.json -``` diff --git a/elasticsearch/mappings.json b/elasticsearch/mappings.json deleted file mode 100644 index 654ab17450..0000000000 --- a/elasticsearch/mappings.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "settings": { - "analysis": { - "analyzer": { - "bigram": { - "tokenizer": "bigram_tokenizer" - } - }, - "tokenizer": { - "bigram_tokenizer": { - "type": "nGram", - "min_gram": 2, - "max_gram": 2, - "token_chars": [ - "letter", - "digit" - ] - } - } - } - }, - "mappings": { - "user": { - "properties": { - "username": { - "type": "string", - "index": "analyzed", - "analyzer": "bigram" - }, - "name": { - "type": "string", - "index": "analyzed", - "analyzer": "bigram" - }, - "bio": { - "type": "string", - "index": "analyzed", - "analyzer": "kuromoji" - } - } - }, - "post": { - "properties": { - "text": { - "type": "string", - "index": "analyzed", - "analyzer": "kuromoji" - } - } - }, - "drive_file": { - "properties": { - "name": { - "type": "string", - "index": "analyzed", - "analyzer": "kuromoji" - }, - "user": { - "type": "string", - "index": "not_analyzed" - } - } - } - } -} diff --git a/gulpfile.ts b/gulpfile.ts index 49a80879d2..9145750333 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -9,6 +9,7 @@ import * as ts from 'gulp-typescript'; const sourcemaps = require('gulp-sourcemaps'); import tslint from 'gulp-tslint'; const cssnano = require('gulp-cssnano'); +const stylus = require('gulp-stylus'); import * as uglifyComposer from 'gulp-uglify/composer'; import pug = require('gulp-pug'); import * as rimraf from 'rimraf'; @@ -20,9 +21,8 @@ import * as replace from 'gulp-replace'; import * as htmlmin from 'gulp-htmlmin'; const uglifyes = require('uglify-es'); -import locales from './locales'; -import { fa } from './src/build/fa'; -const client = require('./built/client/meta.json'); +const locales = require('./locales'); +import { fa } from './src/misc/fa'; import config from './src/config'; const uglify = uglifyComposer(uglifyes, console); @@ -38,8 +38,6 @@ if (isDebug) { const constants = require('./src/const.json'); -require('./src/client/docs/gulpfile.ts'); - gulp.task('build', [ 'build:ts', 'build:copy', @@ -47,8 +45,6 @@ gulp.task('build', [ 'doc' ]); -gulp.task('rebuild', ['clean', 'build']); - gulp.task('build:ts', () => { const tsProject = ts.createProject('./tsconfig.json'); @@ -85,19 +81,19 @@ gulp.task('lint', () => ); gulp.task('format', () => -gulp.src('./src/**/*.ts') - .pipe(tslint({ - formatter: 'verbose', - fix: true - })) - .pipe(tslint.report()) + gulp.src('./src/**/*.ts') + .pipe(tslint({ + formatter: 'verbose', + fix: true + })) + .pipe(tslint.report()) ); gulp.task('mocha', () => - gulp.src([]) + gulp.src('./test/**/*.ts') .pipe(mocha({ exit: true, - compilers: 'ts:ts-node/register' + require: 'ts-node/register' } as any)) ); @@ -118,8 +114,9 @@ gulp.task('build:client', [ 'copy:client' ]); -gulp.task('build:client:script', () => - gulp.src(['./src/client/app/boot.js', './src/client/app/safe.js']) +gulp.task('build:client:script', () => { + const client = require('./built/client/meta.json'); + return gulp.src(['./src/client/app/boot.js', './src/client/app/safe.js']) .pipe(replace('VERSION', JSON.stringify(client.version))) .pipe(replace('API', JSON.stringify(config.api_url))) .pipe(replace('ENV', JSON.stringify(env))) @@ -127,8 +124,8 @@ gulp.task('build:client:script', () => .pipe(isProduction ? uglify({ toplevel: true } as any) : gutil.noop()) - .pipe(gulp.dest('./built/client/assets/')) as any -); + .pipe(gulp.dest('./built/client/assets/')); +}); gulp.task('build:client:styles', () => gulp.src('./src/client/app/init.css') @@ -201,3 +198,10 @@ gulp.task('build:client:pug', [ })) .pipe(gulp.dest('./built/client/app/')) ); + +gulp.task('doc', () => + gulp.src('./src/docs/**/*.styl') + .pipe(stylus()) + .pipe((cssnano as any)()) + .pipe(gulp.dest('./built/docs/assets/')) +); diff --git a/locales/en.yml b/locales/en.yml index 09d35003d1..febc4f6912 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -889,6 +889,7 @@ mobile/views/pages/settings/settings.profile.vue: saved: "Profile updated" uploading: "Uploading" upload-failed: "Failed to upload" + mobile/views/pages/search.vue: search: "Search" empty: "No posts were found for '{}'" diff --git a/locales/fr.yml b/locales/fr.yml index 94d5baeeeb..4a96ee28ed 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -40,7 +40,7 @@ common: hmm: "Hmm ... ?" surprise: "Wow" congrats: "Félicitations !" - angry: "En colère" + angry: "Faché" confused: "Confus" pudding: "Pudding" note-placeholders: diff --git a/locales/index.js b/locales/index.js new file mode 100644 index 0000000000..5b525c77df --- /dev/null +++ b/locales/index.js @@ -0,0 +1,27 @@ +/** + * Languages Loader + */ + +const fs = require('fs'); +const yaml = require('js-yaml'); + +const loadLang = lang => yaml.safeLoad( + fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8')); + +const native = loadLang('ja'); + +const langs = { + 'de': loadLang('de'), + 'en': loadLang('en'), + 'fr': loadLang('fr'), + 'ja': native, + 'pl': loadLang('pl'), + 'es': loadLang('es') +}; + +Object.values(langs).forEach(locale => { + // Extend native language (Japanese) + locale = Object.assign({}, native, locale); +}); + +module.exports = langs; diff --git a/locales/index.ts b/locales/index.ts deleted file mode 100644 index 45b5df0957..0000000000 --- a/locales/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Languages Loader - */ - -import * as fs from 'fs'; -import * as yaml from 'js-yaml'; - -export type LangKey = 'de' | 'en' | 'fr' | 'ja' | 'pl' | 'es'; -export type LocaleObject = { [key: string]: any }; - -const loadLang = (lang: LangKey) => yaml.safeLoad( - fs.readFileSync(`./locales/${lang}.yml`, 'utf-8')) as LocaleObject; - -const native = loadLang('ja'); - -const langs: { [key: string]: LocaleObject } = { - 'de': loadLang('de'), - 'en': loadLang('en'), - 'fr': loadLang('fr'), - 'ja': native, - 'pl': loadLang('pl'), - 'es': loadLang('es') -}; - -Object.entries(langs).map(([, locale]) => { - // Extend native language (Japanese) - locale = Object.assign({}, native, locale); -}); - -export function isAvailableLanguage(lang: string): lang is LangKey { - return lang in langs; -} - -export default langs; diff --git a/locales/ja.yml b/locales/ja.yml index fc790d8c53..9e65066d9d 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -6,6 +6,14 @@ common: misskey: "A ⭐ of fediverse" about-title: "A ⭐ of fediverse." about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた分散マイクロブログSNSです。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。" + + customization-tips: + title: "カスタマイズのヒント" + paragraph1: "ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。" + paragraph2: "一部のウィジェットは、クリックすることで表示を変更することができます。" + paragraph3: "ウィジェットを削除するには、ヘッダーの「ゴミ箱」と書かれたエリアにウィジェットをドラッグ&ドロップします。" + paragraph4: "カスタマイズを終了するには、右上の「完了」をクリックします。" + gotit: "Got it!" time: unknown: "なぞのじかん" @@ -19,6 +27,8 @@ common: months_ago: "{}ヶ月前" years_ago: "{}年前" + trash: "ゴミ箱" + weekday-short: sunday: "日" monday: "月" @@ -56,6 +66,7 @@ common: my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。" i-like-sushi: "私は(プリンよりむしろ)寿司が好き" show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示" + verified-user: "認証済みのユーザー" reversi: drawn: "引き分け" @@ -63,6 +74,7 @@ common: opponent-turn: "相手のターンです" turn-of: "{}のターンです" past-turn-of: "{}のターン" + won: "{}の勝ち" widgets: analog-clock: "アナログ時計" @@ -93,6 +105,7 @@ common: widgets: "ウィジェット" home: "ホーム" local: "ローカル" + hybrid: "ソーシャル" global: "グローバル" notifications: "通知" list: "リスト" @@ -279,6 +292,11 @@ common/views/widgets/memo.vue: title: "付箋" memo: "ここに書いて!" save: "保存" + +common/views/widgets/slideshow.vue: + folder-customize-mode: "フォルダを指定するには、カスタマイズモードを終了してください" + folder: "クリックしてフォルダを指定してください" + no-image: "このフォルダには画像がありません" common/views/pages/follow.vue: signed-in-as: "{}としてサインイン中" @@ -329,6 +347,8 @@ desktop/views/components/drive.file.vue: banner: "バナー" contextmenu: rename: "名前を変更" + mark-as-sensitive: "閲覧注意に設定" + unmark-as-sensitive: "閲覧注意を解除" copy-url: "URLをコピー" download: "ダウンロード" else-files: "その他..." @@ -376,6 +396,14 @@ desktop/views/components/drive.vue: upload: "ファイルをアップロード" url-upload: "URLからアップロード" +desktop/views/components/media-image.vue: + sensitive: "閲覧注意" + click-to-show: "クリックして表示" + +desktop/views/components/media-video.vue: + sensitive: "閲覧注意" + click-to-show: "クリックして表示" + desktop/views/components/follow-button.vue: following: "フォロー中" follow: "フォロー" @@ -440,12 +468,16 @@ desktop/views/components/notes.note.vue: desktop/views/components/notes.vue: error: "読み込みに失敗しました。" retry: "リトライ" + load-more: "もっと読み込む" desktop/views/components/notifications.vue: more: "もっと見る" empty: "ありません!" desktop/views/components/post-form.vue: + add-visible-user: "+ユーザーを追加" + attach-location-information: "位置情報を添付する" + hide-contents: "内容を隠す" reply-placeholder: "この投稿への返信..." quote-placeholder: "この投稿を引用..." submit: "投稿" @@ -464,7 +496,13 @@ desktop/views/components/post-form.vue: insert-a-kao: "v('ω')v" create-poll: "アンケートを作成" text-remain: "残り{}文字" - + recent-tags: "最近" + click-to-tagging: "クリックでタグ付け" + visibility: "公開範囲" + geolocation-alert: "お使いの端末は位置情報に対応していません" + error: "エラー" + enter-username: "ユーザー名を入力してください" + desktop/views/components/post-form-window.vue: note: "新規投稿" reply: "返信" @@ -512,6 +550,8 @@ desktop/views/components/settings.vue: display: "デザインと表示" customize: "ホームをカスタマイズ" + choose-wallpaper: "壁紙を選択" + delete-wallpaper: "壁紙を削除" dark-mode: "ダークモード" circle-icons: "円形のアイコンを使用" gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" @@ -621,8 +661,12 @@ desktop/views/components/settings.profile.vue: description: "自己紹介" birthday: "誕生日" save: "保存" + locked-account: "アカウントの保護" + is-locked: "投稿を非公開にする" + other: "その他" is-bot: "このアカウントはBotです" is-cat: "このアカウントはCatです" + profile-updated: "プロフィールを更新しました" desktop/views/components/sub-note-content.vue: private: "この投稿は非公開です" @@ -636,6 +680,7 @@ desktop/views/components/taskmanager.vue: desktop/views/components/timeline.vue: home: "ホーム" local: "ローカル" + hybrid: "ソーシャル" global: "グローバル" list: "リスト" @@ -648,7 +693,7 @@ desktop/views/components/ui.header.account.vue: favorites: "お気に入り" lists: "リスト" follow-requests: "フォロー申請" - customize: "カスタマイズ" + customize: "ホームのカスタマイズ" settings: "設定" signout: "サインアウト" dark: "闇に飲まれる" @@ -698,6 +743,7 @@ desktop/views/components/window.vue: desktop/views/pages/deck/deck.tl-column.vue: is-media-only: "メディア投稿のみ" is-media-view: "メディアビュー" + edit: "オプション" desktop/views/pages/deck/deck.note.vue: reposted-by: "{}がRenote" @@ -844,6 +890,14 @@ mobile/views/components/drive.file-detail.vue: hash: "ハッシュ (md5)" exif: "EXIF" +mobile/views/components/media-image.vue: + sensitive: "閲覧注意" + click-to-show: "クリックして表示" + +mobile/views/components/media-video.vue: + sensitive: "閲覧注意" + click-to-show: "クリックして表示" + mobile/views/components/follow-button.vue: following: "フォロー中" follow: "フォロー" @@ -958,6 +1012,7 @@ mobile/views/pages/following.vue: mobile/views/pages/home.vue: home: "ホーム" local: "ローカル" + hybrid: "ソーシャル" global: "グローバル" mobile/views/pages/messaging.vue: @@ -1088,11 +1143,17 @@ docs: properties: "プロパティ" endpoints: params: "パラメータ" + no-params: "パラメータはありません" res: "レスポンス" + require-credential: "このエンドポイントは認証情報が必須です。" + require-permission: "このエンドポイントは{permission}の権限を必要とします。" + has-limit: "レートリミットがあります。" + duration-limit: "直近{duration}ミリ秒の間のこのエンドポイントへのリクエスト数の合計が{max}を超える場合はリクエストできません。" + min-interval-limit: "前回のリクエストから{interval}ミリ秒経っていない場合はリクエストできません。" + show-src: "このエンドポイントのソースコードも閲覧できます。" + show-src-link: "コードをGitHubで見る" + generated: "このドキュメントはAPI定義に基づき自動生成されています。" props: name: "名前" type: "型" - optional: "オプション" description: "説明" - yes: "はい" - no: "いいえ" diff --git a/migration/README.md b/migration/README.md deleted file mode 100644 index d52e84b35a..0000000000 --- a/migration/README.md +++ /dev/null @@ -1,11 +0,0 @@ -Misskeyの破壊的変更に対応するいくつかのスニペットがあります。 -MongoDBシェルで実行する必要のあるものとnodeで直接実行する必要のあるものがあります。 -ファイル名が `shell.` から始まるものは前者、 `node.` から始まるものは後者です。 - -MongoDBシェルで実行する場合、`use`でデータベースを選択しておく必要があります。 - -nodeで実行するいくつかのスニペットは、並列処理させる数を引数で設定できるものがあります。 -処理中にエラーで落ちる場合は、メモリが足りていない可能性があるので、少ない数に設定してみてください。 -※デフォルトは`5`です。 - -ファイルを作成する際は `../init-migration-file.sh -t _type_ -n _name_` を実行すると _type_._unixtime_._name_.js が生成されます diff --git a/migration/init-migration-file.sh b/migration/init-migration-file.sh deleted file mode 100644 index c6a2b862e6..0000000000 --- a/migration/init-migration-file.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -usage() { - echo "$0 [-t type] [-n name]" - echo " type: [node | shell]" - echo " name: if no present, set untitled" - exit 0 -} - -while getopts :t:n:h OPT -do - case $OPT in - t) type=$OPTARG - ;; - n) name=$OPTARG - ;; - h) usage - ;; - \?) usage - ;; - :) usage - ;; - esac -done - -if [ "$type" = "" ] -then - echo "no type present!!!" - usage -fi - -if [ "$name" = "" ] -then - name="untitled" -fi - -touch "$(realpath $(dirname $BASH_SOURCE))/migration/$type.$(date +%s).$name.js" diff --git a/package.json b/package.json index a9ae2ba591..4647e3d89a 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,18 @@ { "name": "misskey", "author": "syuilo ", - "version": "4.15.0", - "clientVersion": "1.0.6878", + "version": "5.8.0", + "clientVersion": "1.0.7664", "codename": "nighthike", "main": "./built/index.js", "private": true, "scripts": { - "config": "node ./cli/init.js", "start": "node ./built", "debug": "DEBUG=misskey:* node ./built", - "swagger": "node ./swagger.js", "build": "webpack && gulp build", "webpack": "webpack", "watch": "webpack --watch", "gulp": "gulp build", - "rebuild": "gulp rebuild", "clean": "gulp clean", "cleanall": "gulp cleanall", "lint": "gulp lint", @@ -27,15 +24,15 @@ "@fortawesome/fontawesome-free-brands": "5.0.13", "@fortawesome/fontawesome-free-regular": "5.0.13", "@fortawesome/fontawesome-free-solid": "5.0.13", - "@koa/cors": "2.2.1", + "@koa/cors": "2.2.2", "@prezzemolo/rap": "0.1.2", "@prezzemolo/zip": "0.0.3", "@types/bcryptjs": "2.4.1", + "@types/dateformat": "1.0.1", "@types/debug": "0.0.30", "@types/deep-equal": "1.0.1", - "@types/elasticsearch": "5.0.24", + "@types/elasticsearch": "5.0.25", "@types/file-type": "5.2.1", - "@types/gm": "1.18.0", "@types/gulp": "3.8.36", "@types/gulp-htmlmin": "1.3.32", "@types/gulp-mocha": "0.0.32", @@ -43,31 +40,29 @@ "@types/gulp-replace": "0.0.31", "@types/gulp-uglify": "3.0.5", "@types/gulp-util": "3.0.34", - "@types/inquirer": "0.0.42", "@types/is-root": "1.0.0", "@types/is-url": "1.2.28", - "@types/js-yaml": "3.11.1", + "@types/js-yaml": "3.11.2", "@types/jsdom": "11.0.6", "@types/koa": "2.0.46", - "@types/koa-bodyparser": "5.0.0", + "@types/koa-bodyparser": "5.0.1", "@types/koa-compress": "2.0.8", "@types/koa-favicon": "2.0.19", "@types/koa-logger": "3.1.0", "@types/koa-mount": "3.0.1", "@types/koa-multer": "1.0.0", - "@types/koa-router": "7.0.30", + "@types/koa-router": "7.0.31", "@types/koa-send": "4.1.1", "@types/koa-views": "2.0.3", - "@types/koa__cors": "2.2.2", - "@types/kue": "0.11.9", - "@types/license-checker": "15.0.0", + "@types/koa__cors": "2.2.3", + "@types/minio": "6.0.2", "@types/mkdirp": "0.5.2", "@types/mocha": "5.2.3", - "@types/mongodb": "3.1.0", + "@types/mongodb": "3.1.2", "@types/ms": "0.7.30", - "@types/node": "10.5.1", - "@types/nopt": "3.0.29", + "@types/node": "10.5.4", "@types/parse5": "5.0.0", + "@types/portscanner": "2.1.0", "@types/pug": "2.0.4", "@types/qrcode": "1.2.0", "@types/ratelimiter": "2.1.28", @@ -76,11 +71,14 @@ "@types/request-promise-native": "1.0.15", "@types/rimraf": "2.0.2", "@types/seedrandom": "2.4.27", + "@types/sharp": "0.17.9", + "@types/showdown": "1.7.5", "@types/single-line-log": "1.1.0", "@types/speakeasy": "2.0.2", + "@types/systeminformation": "3.23.0", "@types/tmp": "0.0.33", "@types/uuid": "3.4.3", - "@types/webpack": "4.4.4", + "@types/webpack": "4.4.8", "@types/webpack-stream": "3.2.10", "@types/websocket": "0.0.39", "@types/ws": "5.1.2", @@ -88,51 +86,54 @@ "autosize": "4.0.2", "autwh": "0.1.0", "bcryptjs": "2.4.3", + "bee-queue": "1.2.2", "bootstrap-vue": "2.0.0-rc.11", - "cafy": "8.0.0", + "cafy": "11.3.0", "chalk": "2.4.1", + "commander": "2.16.0", "crc-32": "1.2.0", - "css-loader": "0.28.11", + "css-loader": "1.0.0", + "dateformat": "3.0.3", "debug": "3.1.0", "deep-equal": "1.0.1", "deepcopy": "0.6.3", "diskusage": "0.2.4", "dompurify": "1.0.5", - "elasticsearch": "15.0.0", - "element-ui": "2.4.2", - "emojilib": "2.2.12", + "elasticsearch": "15.1.1", + "element-ui": "2.4.5", + "emojilib": "2.3.0", "escape-regexp": "0.0.1", "eslint": "5.0.1", - "eslint-plugin-vue": "4.5.0", + "eslint-plugin-vue": "4.7.1", "eventemitter3": "3.1.0", "exif-js": "2.3.0", "file-loader": "1.1.11", - "file-type": "8.0.0", + "file-type": "8.1.0", "fuckadblock": "3.2.1", - "gm": "1.23.1", "gulp": "3.9.1", "gulp-cssnano": "2.1.3", "gulp-htmlmin": "4.0.0", "gulp-imagemin": "4.1.0", "gulp-mocha": "6.0.0", "gulp-pug": "4.0.1", - "gulp-rename": "1.3.0", + "gulp-rename": "1.4.0", "gulp-replace": "1.0.0", "gulp-sourcemaps": "2.6.4", "gulp-stylus": "2.7.0", "gulp-tslint": "8.1.3", "gulp-typescript": "4.0.2", - "gulp-uglify": "3.0.0", + "gulp-uglify": "3.0.1", "gulp-util": "3.0.8", - "hard-source-webpack-plugin": "0.10.1", + "hard-source-webpack-plugin": "0.12.0", "highlight.js": "9.12.0", - "html-minifier": "3.5.17", + "html-minifier": "3.5.19", "http-signature": "1.2.0", - "inquirer": "6.0.0", + "insert-text-at-cursor": "0.1.1", "is-root": "2.0.0", "is-url": "1.2.4", + "jquery": "3.3.1", "js-yaml": "3.12.0", - "jsdom": "11.11.0", + "jsdom": "11.12.0", "koa": "2.5.1", "koa-bodyparser": "4.2.1", "koa-compress": "3.0.0", @@ -145,32 +146,30 @@ "koa-send": "5.0.0", "koa-slow": "2.1.0", "koa-views": "6.1.4", - "kue": "0.11.6", - "license-checker": "20.1.0", "loader-utils": "1.1.0", "mecab-async": "0.1.2", + "minio": "6.0.0", "mkdirp": "0.5.1", "mocha": "5.2.0", "moji": "0.5.1", - "mongodb": "3.1.0", + "mongodb": "3.1.1", "monk": "6.0.6", "ms": "2.1.1", "nan": "2.10.0", - "node-sass": "4.9.0", + "node-sass": "4.9.2", "node-sass-json-importer": "3.3.1", - "nopt": "4.0.1", "nprogress": "0.2.0", "object-assign-deep": "0.4.0", "on-build-webpack": "0.1.0", "os-utils": "0.0.14", "parse5": "5.0.0", + "portscanner": "2.2.0", "progress-bar-webpack-plugin": "1.11.0", - "prominence": "0.2.0", "promise-sequential": "1.1.1", "pug": "2.0.3", "punycode": "2.1.1", - "qrcode": "1.2.0", - "ratelimiter": "3.1.0", + "qrcode": "1.2.2", + "ratelimiter": "3.2.0", "recaptcha-promise": "0.1.3", "reconnecting-websocket": "3.2.2", "redis": "2.8.0", @@ -181,22 +180,24 @@ "s-age": "1.1.2", "sass-loader": "7.0.3", "seedrandom": "2.4.3", + "sharp": "0.20.5", + "showdown": "1.8.6", + "showdown-highlightjs-extension": "0.1.2", "single-line-log": "1.1.2", "speakeasy": "2.0.0", "style-loader": "0.21.0", "stylus": "0.54.5", "stylus-loader": "3.0.2", "summaly": "2.0.6", - "swagger-jsdoc": "1.9.7", + "systeminformation": "3.42.4", "syuilo-password-strength": "0.0.1", - "tcp-port-used": "0.1.2", "textarea-caret": "3.1.0", "tmp": "0.0.33", "ts-loader": "4.4.1", "ts-node": "7.0.0", "tslint": "5.10.0", "typescript": "2.9.2", - "typescript-eslint-parser": "16.0.1", + "typescript-eslint-parser": "17.0.1", "uglify-es": "3.3.9", "url-loader": "1.0.1", "uuid": "3.3.2", @@ -205,18 +206,19 @@ "vue-cropperjs": "2.2.1", "vue-js-modal": "1.3.16", "vue-json-tree-view": "2.1.4", - "vue-loader": "15.2.4", + "vue-loader": "15.2.6", "vue-router": "3.0.1", + "vue-style-loader": "4.1.1", "vue-template-compiler": "2.5.16", "vuedraggable": "2.16.0", "vuex": "3.0.1", - "vuex-persistedstate": "^2.5.4", + "vuex-persistedstate": "2.5.4", "web-push": "3.3.2", "webfinger.js": "2.6.6", - "webpack": "4.14.0", - "webpack-cli": "3.0.8", + "webpack": "4.16.3", + "webpack-cli": "3.1.0", "websocket": "1.0.26", - "ws": "5.2.1", + "ws": "6.0.0", "xev": "2.0.1" }, "greenkeeper": { diff --git a/src/acct/render.ts b/src/acct/render.ts deleted file mode 100644 index c29bfb4764..0000000000 --- a/src/acct/render.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IUser } from '../models/user'; - -export default (user: IUser) => { - return user.host === null ? user.username : `${user.username}@${user.host}`; -}; diff --git a/src/client/app/auth/views/form.vue b/src/client/app/auth/views/form.vue index 152b900429..80086e3861 100644 --- a/src/client/app/auth/views/form.vue +++ b/src/client/app/auth/views/form.vue @@ -2,7 +2,7 @@

{{ app.name }}があなたのアカウントにアクセスすることを許可しますか?

- +
diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts index 2e58649ac2..c93609bc59 100644 --- a/src/client/app/common/scripts/compose-notification.ts +++ b/src/client/app/common/scripts/compose-notification.ts @@ -1,6 +1,6 @@ -import getNoteSummary from '../../../../renderers/get-note-summary'; -import getReactionEmoji from '../../../../renderers/get-reaction-emoji'; -import getUserName from '../../../../renderers/get-user-name'; +import getNoteSummary from '../../../../misc/get-note-summary'; +import getReactionEmoji from '../../../../misc/get-reaction-emoji'; +import getUserName from '../../../../misc/get-user-name'; type Notification = { title: string; @@ -17,21 +17,21 @@ export default function(type, data): Notification { return { title: 'ファイルがアップロードされました', body: data.name, - icon: data.url + '?thumbnail&size=64' + icon: data.url }; case 'unread_messaging_message': return { title: `${getUserName(data.user)}さんからメッセージ:`, body: data.text, // TODO: getMessagingMessageSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' + icon: data.user.avatarUrl }; case 'reversi_invited': return { title: '対局への招待があります', body: `${getUserName(data.parent)}さんから`, - icon: data.parent.avatarUrl + '?thumbnail&size=64' + icon: data.parent.avatarUrl }; case 'notification': @@ -40,28 +40,28 @@ export default function(type, data): Notification { return { title: `${getUserName(data.user)}さんから:`, body: getNoteSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' + icon: data.user.avatarUrl }; case 'reply': return { title: `${getUserName(data.user)}さんから返信:`, body: getNoteSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' + icon: data.user.avatarUrl }; case 'quote': return { title: `${getUserName(data.user)}さんが引用:`, body: getNoteSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' + icon: data.user.avatarUrl }; case 'reaction': return { title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`, body: getNoteSummary(data.note), - icon: data.user.avatarUrl + '?thumbnail&size=64' + icon: data.user.avatarUrl }; default: diff --git a/src/client/app/common/scripts/streaming/reversi-game.ts b/src/client/app/common/scripts/streaming/games/reversi/reversi-game.ts similarity index 55% rename from src/client/app/common/scripts/streaming/reversi-game.ts rename to src/client/app/common/scripts/streaming/games/reversi/reversi-game.ts index 5638b3013f..e6b02fcfdb 100644 --- a/src/client/app/common/scripts/streaming/reversi-game.ts +++ b/src/client/app/common/scripts/streaming/games/reversi/reversi-game.ts @@ -1,9 +1,9 @@ -import Stream from './stream'; -import MiOS from '../../../mios'; +import Stream from '../../stream'; +import MiOS from '../../../../../mios'; export class ReversiGameStream extends Stream { constructor(os: MiOS, me, game) { - super(os, 'reversi-game', { + super(os, 'games/reversi-game', { i: me ? me.token : null, game: game.id }); diff --git a/src/client/app/common/scripts/streaming/reversi.ts b/src/client/app/common/scripts/streaming/games/reversi/reversi.ts similarity index 73% rename from src/client/app/common/scripts/streaming/reversi.ts rename to src/client/app/common/scripts/streaming/games/reversi/reversi.ts index 2e4395f0f1..1f4fd8c63e 100644 --- a/src/client/app/common/scripts/streaming/reversi.ts +++ b/src/client/app/common/scripts/streaming/games/reversi/reversi.ts @@ -1,10 +1,10 @@ -import StreamManager from './stream-manager'; -import Stream from './stream'; -import MiOS from '../../../mios'; +import StreamManager from '../../stream-manager'; +import Stream from '../../stream'; +import MiOS from '../../../../../mios'; export class ReversiStream extends Stream { constructor(os: MiOS, me) { - super(os, 'reversi', { + super(os, 'games/reversi', { i: me.token }); } diff --git a/src/client/app/common/scripts/streaming/hybrid-timeline.ts b/src/client/app/common/scripts/streaming/hybrid-timeline.ts new file mode 100644 index 0000000000..cd290797c4 --- /dev/null +++ b/src/client/app/common/scripts/streaming/hybrid-timeline.ts @@ -0,0 +1,34 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../../mios'; + +/** + * Hybrid timeline stream connection + */ +export class HybridTimelineStream extends Stream { + constructor(os: MiOS, me) { + super(os, 'hybrid-timeline', { + i: me.token + }); + } +} + +export class HybridTimelineStreamManager extends StreamManager { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new HybridTimelineStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/views/components/analog-clock.vue b/src/client/app/common/views/components/analog-clock.vue index 53fb2a8dad..43ae2ca933 100644 --- a/src/client/app/common/views/components/analog-clock.vue +++ b/src/client/app/common/views/components/analog-clock.vue @@ -39,13 +39,17 @@ export default Vue.extend({ dark: { type: Boolean, default: false + }, + smooth: { + type: Boolean, + default: false } }, data() { return { now: new Date(), - clock: null, + enabled: true, graduationsPadding: 0.5, handsPadding: 1, @@ -74,6 +78,9 @@ export default Vue.extend({ return themeColor; }, + ms(): number { + return this.now.getMilliseconds() * this.smooth; + } s(): number { return this.now.getSeconds(); }, @@ -85,13 +92,13 @@ export default Vue.extend({ }, hAngle(): number { - return Math.PI * (this.h % 12 + this.m / 60) / 6; + return Math.PI * (this.h % 12 + (this.m + (this.s + this.ms / 1000) / 60) / 60) / 6; }, mAngle(): number { - return Math.PI * (this.m + this.s / 60) / 30; + return Math.PI * (this.m + (this.s + this.ms / 1000) / 60) / 30; }, sAngle(): number { - return Math.PI * this.s / 30; + return Math.PI * (this.s + this.ms / 1000) / 30; }, graduations(): any { @@ -106,11 +113,17 @@ export default Vue.extend({ }, mounted() { - this.clock = setInterval(this.tick, 1000); + const update = () => { + if (this.enabled) { + this.tick(); + requestAnimationFrame(update); + } + }; + update(); }, beforeDestroy() { - clearInterval(this.clock); + this.enabled = false; }, methods: { diff --git a/src/client/app/common/views/components/autocomplete.vue b/src/client/app/common/views/components/autocomplete.vue index 84173d20b5..cd6066877c 100644 --- a/src/client/app/common/views/components/autocomplete.vue +++ b/src/client/app/common/views/components/autocomplete.vue @@ -2,11 +2,16 @@
  1. - + {{ user | userName }} @{{ user | acct }}
+
    +
  1. + {{ hashtag }} +
  2. +
  1. {{ emoji.emoji }} @@ -48,33 +53,33 @@ emjdb.sort((a, b) => a.name.length - b.name.length); export default Vue.extend({ props: ['type', 'q', 'textarea', 'complete', 'close', 'x', 'y'], + data() { return { fetching: true, users: [], + hashtags: [], emojis: [], select: -1, emojilib } }, + computed: { items(): HTMLCollection { return (this.$refs.suggests as Element).children; } }, + updated() { //#region 位置調整 - const margin = 32; - - if (this.x + this.$el.offsetWidth > window.innerWidth - margin) { - this.$el.style.left = (this.x - this.$el.offsetWidth) + 'px'; - this.$el.style.marginLeft = '-16px'; + if (this.x + this.$el.offsetWidth > window.innerWidth) { + this.$el.style.left = (window.innerWidth - this.$el.offsetWidth) + 'px'; } else { this.$el.style.left = this.x + 'px'; - this.$el.style.marginLeft = '0'; } - if (this.y + this.$el.offsetHeight > window.innerHeight - margin) { + if (this.y + this.$el.offsetHeight > window.innerHeight) { this.$el.style.top = (this.y - this.$el.offsetHeight) + 'px'; this.$el.style.marginTop = '0'; } else { @@ -83,6 +88,7 @@ export default Vue.extend({ } //#endregion }, + mounted() { this.textarea.addEventListener('keydown', this.onKeydown); @@ -100,6 +106,7 @@ export default Vue.extend({ }); }); }, + beforeDestroy() { this.textarea.removeEventListener('keydown', this.onKeydown); @@ -107,6 +114,7 @@ export default Vue.extend({ el.removeEventListener('mousedown', this.onMousedown); }); }, + methods: { exec() { this.select = -1; @@ -117,7 +125,8 @@ export default Vue.extend({ } if (this.type == 'user') { - const cache = sessionStorage.getItem(this.q); + const cacheKey = 'autocomplete:user:' + this.q; + const cache = sessionStorage.getItem(cacheKey); if (cache) { const users = JSON.parse(cache); this.users = users; @@ -131,9 +140,33 @@ export default Vue.extend({ this.fetching = false; // キャッシュ - sessionStorage.setItem(this.q, JSON.stringify(users)); + sessionStorage.setItem(cacheKey, JSON.stringify(users)); }); } + } else if (this.type == 'hashtag') { + if (this.q == null || this.q == '') { + this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]'); + this.fetching = false; + } else { + const cacheKey = 'autocomplete:hashtag:' + this.q; + const cache = sessionStorage.getItem(cacheKey); + if (cache) { + const hashtags = JSON.parse(cache); + this.hashtags = hashtags; + this.fetching = false; + } else { + (this as any).api('hashtags/search', { + query: this.q, + limit: 30 + }).then(hashtags => { + this.hashtags = hashtags; + this.fetching = false; + + // キャッシュ + sessionStorage.setItem(cacheKey, JSON.stringify(hashtags)); + }); + } + } } else if (this.type == 'emoji') { const matched = []; emjdb.some(x => { @@ -228,12 +261,13 @@ export default Vue.extend({ diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue index a65b62882f..a924b62e64 100644 --- a/src/client/app/common/views/components/avatar.vue +++ b/src/client/app/common/views/components/avatar.vue @@ -31,7 +31,7 @@ export default Vue.extend({ : this.user.avatarColor && this.user.avatarColor.length == 3 ? `rgb(${ this.user.avatarColor.join(',') })` : null, - backgroundImage: this.lightmode ? null : `url(${ this.user.avatarUrl }?thumbnail)`, + backgroundImage: this.lightmode ? null : `url(${ this.user.avatarUrl })`, borderRadius: this.$store.state.settings.circleIcons ? '100%' : null }; } diff --git a/src/client/app/common/views/components/reversi.game.vue b/src/client/app/common/views/components/games/reversi/reversi.game.vue similarity index 93% rename from src/client/app/common/views/components/reversi.game.vue rename to src/client/app/common/views/components/games/reversi/reversi.game.vue index a2a6fd0dc1..dd2c94a603 100644 --- a/src/client/app/common/views/components/reversi.game.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.game.vue @@ -8,7 +8,7 @@

    %i18n:common.reversi.opponent-turn%

    %i18n:common.reversi.my-turn%

    - +

@@ -26,8 +26,8 @@ :class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }" @click="set(i)" :title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`"> - - + +
@@ -58,8 +58,8 @@ @@ -144,22 +143,4 @@ export default Vue.extend({ .mk-signup min-width 302px - - .agree-tou - padding 4px - border-radius 4px - - &:hover - background #f4f4f4 - - &:active - background #eee - - &, * - cursor pointer - - p - display inline - color #555 - diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue index 38979871c1..1e625f69ed 100644 --- a/src/client/app/common/views/components/url-preview.vue +++ b/src/client/app/common/views/components/url-preview.vue @@ -2,6 +2,11 @@