Compare commits

...

115 Commits

Author SHA1 Message Date
syuilo
3a17ff0983 11.27.1 2019-08-01 15:47:02 +09:00
syuilo
9a9270bbe9 New Crowdin translations (#5230)
* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)
2019-08-01 15:44:53 +09:00
Acid Chicken (硫酸鶏)
512eee4f51 Update README.md [AUTOGEN] (#5236) 2019-08-01 01:32:00 +09:00
MeiMei
db01fa0eef Enable s3ForcePathStyle (#5234) 2019-07-31 18:11:11 +09:00
syuilo
0c49a1ebd5 11.27.0 2019-07-29 04:36:11 +09:00
syuilo
636d6394e3 Update CHANGELOG.md 2019-07-29 04:34:56 +09:00
syuilo
c67c091b3a New Crowdin translations (#5217)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)
2019-07-29 01:56:01 +09:00
Oni-Men
333604898c fix #5214 ウィジェットが選択されていないときは追加されないように (#5227)
* fix #5214

* null削除の取り消し

* 空白文字の調整
2019-07-28 14:12:33 +09:00
syuilo
076ac3b614 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-28 10:40:54 +09:00
syuilo
0e1468b159 Update CHANGELOG.md 2019-07-28 10:40:42 +09:00
syuilo
66409029e7 非ログイン時の警告処理 (#5219)
* Update note-mixin.ts

* Update note-mixin.ts

* ✌️

* Update note-mixin.ts

* Update note-menu.vue
2019-07-28 10:35:53 +09:00
syuilo
8ec6b2ec11 Fix error 2019-07-28 10:31:16 +09:00
MeiMei
14736620ec HTTPリクエストのKeep-AliveとPrxoy対応など (#5226)
* DriveのKeep-Alive, Proxy と APのProxy対応

* request系でKeep-Aliveするように

* fix lookup-dns-cache.d.ts

* remove debug output
2019-07-28 09:49:02 +09:00
Xeltica
831ca53b63 「削除して編集」機能を追加 (#5182)
* 「削除して編集」機能を追加

* UXの調整

* 殆どの情報を保持したまま編集できるように

* update lang
2019-07-28 05:33:12 +09:00
MeiMei
6138d46509 AP Emojiのupdatedは採用しないように (#5220)
* AP Emojiのupdatedは採用しない

* updated判定は残す
2019-07-28 05:32:40 +09:00
syuilo
c3003cb363 Improve mobile notifications view 2019-07-28 05:25:12 +09:00
syuilo
4277e53433 通知のフィルタ (#5224)
* ✌️

* Deck
2019-07-28 04:44:09 +09:00
MeiMei
6516bd2ade Fix: リンクバリデーションリンクが一瞬表示されてしまう (#5216)
* Fix: リンクバリデーションリンクが一瞬表示されてしまう

* use link
2019-07-26 00:17:34 +09:00
syuilo
27d22f954a 11.26.2 2019-07-25 02:23:42 +09:00
Satsuki Yanagi
88f5e8e8e2 Bye moment from package.json (#5215)
* Bye moment from package.json

* Use Mapped types for argument type definition
2019-07-25 01:36:48 +09:00
syuilo
fd2ae6d3cf Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-24 02:17:04 +09:00
syuilo
238edd36f7 Fix error 2019-07-24 02:16:41 +09:00
MeiMei
5d847f9808 Fix: some post form errors (#5212)
* Fix: type mismatch in post-form-attaches

* Fix: 'this is null' in post-form-window
2019-07-24 02:15:40 +09:00
syuilo
ac914af9c3 Fix #5210 2019-07-23 00:19:29 +09:00
Aya Morisawa
636f90ca0c Improve CONTRIBUTING.md (#5158) 2019-07-22 14:57:23 +09:00
syuilo
29469bb7c6 Better error 2019-07-22 10:18:27 +09:00
syuilo
4f043b1841 Update signup.ts 2019-07-22 10:16:25 +09:00
syuilo
85008303f5 Prevent username reusing 2019-07-22 10:15:00 +09:00
syuilo
3432d6e615 Update dependencies 🚀 2019-07-22 07:31:02 +09:00
syuilo
855c990a17 11.26.1 2019-07-21 22:59:57 +09:00
syuilo
eef2dc2f62 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-21 22:59:15 +09:00
syuilo
fa54140973 Update CHANGELOG.md 2019-07-21 22:58:57 +09:00
syuilo
e770d32916 New Crowdin translations (#5206)
* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)
2019-07-21 22:58:51 +09:00
Oni-Men
8aeabf530c Patch #5060 (#5205)
* fix #5060

* forgot ";" :p
2019-07-21 22:27:36 +09:00
syuilo
6fbf1cfc28 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-21 21:59:51 +09:00
syuilo
5480df35bc Update CHANGELOG.md 2019-07-21 21:58:48 +09:00
Aya Morisawa
8b5104d564 Update deprecated vscode extension (#5209) 2019-07-21 21:57:15 +09:00
Aya Morisawa
3c1192d6bf Unignore .vscode/extensions.json (#5208) 2019-07-21 20:27:43 +09:00
Satsuki Yanagi
eb8ef35122 Fix activity calendar for desktop widget (#5200)
* Fix activity calendar for desktop widget

* fetch past 21 weeks for activity widgets

* Cleanup

* forEach -> map

* Revert "forEach -> map"

This reverts commit b72e180ee4.
2019-07-21 19:12:16 +09:00
syuilo
8a31e5fd0f Update CHANGELOG.md 2019-07-21 03:27:17 +09:00
syuilo
751728db84 Update CHANGELOG.md 2019-07-21 03:25:10 +09:00
MeiMei
e695f54ef0 投稿フォームが画面外にはみ出さないように (#5203)
* 上下はみ出したウィンドウは上基準で収めるように

* 投稿フォームを画面より長くしないように
2019-07-20 17:50:01 +09:00
MeiMei
b2ed45ae38 Fix: スクロールしてると絵文字ピッカーの位置がずれる (#5204) 2019-07-20 17:47:40 +09:00
syuilo
5e36f75f8a Clean up: Remove unused imports 2019-07-20 04:09:33 +09:00
rinsuki
7ac13a386c Improve GenerateVideoThumbnail (#5186)
* Improve GenerateVideoThumbnail

* use fluent-ffmpeg

* Update src/services/drive/generate-video-thumbnail.ts
2019-07-20 03:28:14 +09:00
Satsuki Yanagi
56c8ad9df3 Improve usability of quote note by pasting url (#5196) 2019-07-20 03:23:16 +09:00
MeiMei
f1ab918ecd Fix question (#5197) 2019-07-20 03:20:06 +09:00
Acid Chicken (硫酸鶏)
42af8c7695 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-20 02:02:48 +09:00
imgbot[bot]
0de2e150cb [ImgBot] Optimize images (#5189)
/assets/ss/user.jpg -- 148.15kb -> 148.15kb (0%)
2019-07-20 01:56:09 +09:00
Acid Chicken (硫酸鶏)
1ac498c8fe Update README.md [AUTOGEN] (#5199) 2019-07-20 01:53:09 +09:00
Acid Chicken (硫酸鶏)
545d29a40a Update README.md [AUTOGEN] (#5193) 2019-07-20 00:40:08 +09:00
syuilo
73bbef2922 Merge branch 'master' into develop 2019-07-19 06:14:21 +09:00
syuilo
26567cdeb2 🎨 2019-07-19 06:13:56 +09:00
syuilo
84941cbb97 Update CHANGELOG.md 2019-07-19 04:13:20 +09:00
syuilo
cd5b24d4eb Merge branch 'develop' 2019-07-19 03:38:05 +09:00
syuilo
c8abd512e1 11.26.0 2019-07-19 03:37:48 +09:00
syuilo
de9bd2651b Remove unused import 2019-07-19 03:37:34 +09:00
syuilo
c432310cae MFMの引用がインライン表示になっている問題を修正 2019-07-19 03:30:37 +09:00
syuilo
1c95cdffdc Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-19 03:27:53 +09:00
syuilo
c3ec668e16 Update CHANGELOG.md 2019-07-19 03:27:49 +09:00
rinsuki
2af79e9855 特定ホストへのメンションの特別処理をクライアントに追加 (#5185)
* 特定ホストへのメンションの特別処理をクライアントに追加

Fix #5168

* Apply suggestions from code review

Co-Authored-By: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* Apply suggestions from code review

Co-Authored-By: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* Apply suggestions from code review

Co-Authored-By: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* Fix indent
2019-07-19 03:13:47 +09:00
Oni-Men
f6ac6f9c6f fix #5071 (#5184) 2019-07-18 20:56:48 +09:00
MeiMei
d8c835fa51 Fix signin (#5181)
* Revert "Fix signin history (#5180)"

This reverts commit a97c14a7b7.

* fix signin

* failはfail専用に

* fix password less 200
2019-07-18 05:26:58 +09:00
Satsuki Yanagi
a97c14a7b7 Fix signin history (#5180)
* Fix #5179

* Fix wrong value in signin history
2019-07-18 04:22:44 +09:00
syuilo
54ecf97c22 New Crowdin translations (#5175)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)
2019-07-18 02:03:47 +09:00
MeiMei
9c4e64b7b5 Send Delete activity on suspend (#5165)
* Send Delete Person activity

* Delete activityの後にフォロー解除する

* アカウント削除でもDelete activity
2019-07-18 02:03:28 +09:00
MeiMei
ef44eda69e Mastodonのリンクの所有者認証に対応 (#5161)
* Profile metadata を設定できるように

* API desc
2019-07-18 00:11:39 +09:00
Satsuki Yanagi
f1a7ab639b Resolve #5117 (#5171)
* Resolve #5117

* 出来立てのユーザーがストリームコネクションを持ってるはずがない
2019-07-18 00:00:03 +09:00
Satsuki Yanagi
0d8286cb2a Improve #5176 (#5178)
* 🎨

* Follow review

* Remove unnecessary colon
2019-07-17 23:59:21 +09:00
Satsuki Yanagi
8e4ad4b919 Improve usability of users view (#5176)
* Improve usability of users view

Resolve #5173

* Fix query

* Follow review and fix

* Follow review
2019-07-17 18:59:10 +09:00
Satsuki Yanagi
9a09ed6290 Cleanup source (#5177)
* Cleanup source

* 🙏
2019-07-17 17:13:38 +09:00
Satsuki Yanagi
9ca36021b0 Fix #5172 (#5174) 2019-07-17 13:28:27 +09:00
Acid Chicken (硫酸鶏)
eaebb95827 Update README.md [AUTOGEN] (#5163) 2019-07-17 04:28:11 +09:00
syuilo
b8cce2067c New translations ja-JP.yml (French) (#5170) 2019-07-17 01:34:32 +09:00
syuilo
07a0631964 New translations ja-JP.yml (French) (#5169) 2019-07-17 01:25:56 +09:00
MeiMei
ebc2b05231 fix build error (#5162) 2019-07-17 00:59:19 +09:00
Acid Chicken (硫酸鶏)
4c79dd4e96 Use yarn (#5154)
* Revert "✌️"

This reverts commit b5b437b878.

* Welcome back, yarn.lock

[lockfiles for all](https://yarnpkg.com/blog/2016/11/24/lockfiles-for-all/)

* Use alpine package registry instead of npm's

* Avoid npx

* Remove `"`

* Follow review

refs: https://github.com/syuilo/misskey/pull/5154#discussion_r303227256

* Update lockfile

* Use yarn instead of npm run

refs: https://github.com/syuilo/misskey/pull/5154#discussion_r303227285

* Back to npm

* Follow review

refs: https://github.com/syuilo/misskey/pull/5154#discussion_r303292279
2019-07-15 21:32:09 +09:00
syuilo
abc57519a7 New Crowdin translations (#5155)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Czech)
2019-07-15 20:47:49 +09:00
Aya Morisawa
a23a10d375 Improve CONTRIBUTING.md (#5157) 2019-07-15 20:47:09 +09:00
Satsuki Yanagi
01e7716170 (コ`・ヘ・´ケ) (#5156) 2019-07-15 02:28:30 +09:00
Acid Chicken (硫酸鶏)
0a29ce13b6 Nothing 2019-07-14 11:08:13 +09:00
Acid Chicken (硫酸鶏)
6d0ee61661 Corriger les omissions 2019-07-14 11:04:27 +09:00
Acid Chicken (硫酸鶏)
9c684fd6c4 Welcome back, yarn.lock
[lockfiles for all](https://yarnpkg.com/blog/2016/11/24/lockfiles-for-all/)
2019-07-14 10:16:30 +09:00
Acid Chicken (硫酸鶏)
cbb8edd5ed Revert "Add yarn.lock to .gitignore (#3408)"
This reverts commit e8439679a5.
2019-07-14 10:09:00 +09:00
Acid Chicken (硫酸鶏)
f933fa0e78 Add FAQ in CONTRIBUTING.md 2019-07-14 10:06:10 +09:00
Acid Chicken (硫酸鶏)
b3e5198f23 Use yarn instead of npm 2019-07-14 10:04:52 +09:00
Acid Chicken (硫酸鶏)
6e042ca344 Revert "Docker: Back to npm from yarn (#4730)"
This reverts commit 112a72abdf.
2019-07-14 09:58:45 +09:00
Acid Chicken (硫酸鶏)
9381842af2 Bye @types/is-root 2019-07-14 09:42:22 +09:00
Acid Chicken (硫酸鶏)
a6644c540a Update dependency resolutions 2019-07-14 09:38:49 +09:00
syuilo
063427a660 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-14 04:02:41 +09:00
syuilo
6846067a5d Update CONTRIBUTING.md 2019-07-14 04:02:24 +09:00
Satsuki Yanagi
b7273c90ae Fix #5128 (#5153) 2019-07-14 03:56:34 +09:00
syuilo
a6f5e23069 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-14 03:54:02 +09:00
syuilo
f37f22b163 Fix bug 2019-07-14 03:53:45 +09:00
syuilo
9440fdb2d0 New Crowdin translations (#5152)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)
2019-07-14 03:33:31 +09:00
syuilo
96c19b2607 New Crowdin translations (#5151)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)
2019-07-14 03:28:30 +09:00
syuilo
b34c1379e9 Resolve #3238 2019-07-14 03:18:45 +09:00
syuilo
0fc965f342 New translations ja-JP.yml (Chinese Simplified) (#5149) 2019-07-13 02:04:09 +09:00
syuilo
617db05808 New translations ja-JP.yml (Chinese Simplified) (#5148) 2019-07-13 01:45:09 +09:00
syuilo
6a20ab687c New translations ja-JP.yml (Chinese Simplified) (#5146) 2019-07-13 01:13:50 +09:00
syuilo
6c3bcdad54 New translations ja-JP.yml (Chinese Simplified) (#5145) 2019-07-13 01:04:06 +09:00
syuilo
aeea275ec2 New Crowdin translations (#5144)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)
2019-07-12 22:36:09 +09:00
Satsuki Yanagi
b3c6e28717 Improve usability (#5142)
Fix #3862
2019-07-12 22:24:29 +09:00
MeiMei
70691e1523 Fix: proxy-media後のContent-Typeが違う (#5143) 2019-07-12 22:21:47 +09:00
syuilo
2cb032b0e0 Fix #5140 2019-07-12 03:17:31 +09:00
syuilo
ebbf5268ac Improve usability 2019-07-12 02:08:13 +09:00
syuilo
b2030e8403 🎨 2019-07-12 00:40:10 +09:00
syuilo
82493bb741 Update dependencies 🚀 2019-07-11 23:46:46 +09:00
syuilo
aa15901c23 New Crowdin translations (#5131)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)
2019-07-10 18:47:19 +09:00
syuilo
0bd4d069a2 New Crowdin translations (#5130)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Danish)
2019-07-10 18:37:38 +09:00
syuilo
ab871c6991 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-07-10 18:30:58 +09:00
syuilo
feec5e88fc Pages: Implement radio button 2019-07-10 18:30:51 +09:00
MeiMei
a091cbb93a Prevent duplicate user registration (#5129) 2019-07-10 03:47:07 +09:00
Satsuki Yanagi
a59ab79da0 Fix drive browser showing false empty (#5127) 2019-07-09 17:38:14 +09:00
syuilo
8ca4d39440 Update doc 2019-07-09 17:06:12 +09:00
141 changed files with 14482 additions and 464 deletions

2
.gitattributes vendored
View File

@@ -1,5 +1,3 @@
*.svg -diff -text *.svg -diff -text
*.psd -diff -text *.psd -diff -text
*.ai -diff -text *.ai -diff -text
yarn.lock -diff -text
package-lock.json -diff -text

4
.gitignore vendored
View File

@@ -1,5 +1,6 @@
# Visual Studio Code # Visual Studio Code
/.vscode /.vscode
!/.vscode/extensions.json
# Intelij-IDEA # Intelij-IDEA
/.idea /.idea
@@ -7,9 +8,6 @@
# Node.js # Node.js
/node_modules /node_modules
# yarn
yarn.lock
# config # config
/.config/* /.config/*
!/.config/example.yml !/.config/example.yml

View File

@@ -2,10 +2,10 @@
"recommendations": [ "recommendations": [
"ducksoupdev.vue2", "ducksoupdev.vue2",
"editorconfig.editorconfig", "editorconfig.editorconfig",
"eg2.tslint",
"eg2.vscode-npm-script", "eg2.vscode-npm-script",
"hollowtree.vue-snippets", "hollowtree.vue-snippets",
"ms-vscode.typescript-javascript-grammar", "ms-vscode.typescript-javascript-grammar",
"ms-vscode.vscode-typescript-tslint-plugin",
"octref.vetur", "octref.vetur",
"sysoev.language-stylus" "sysoev.language-stylus"
] ]

View File

@@ -1,9 +1,75 @@
ChangeLog ChangeLog
========= =========
If you encounter any problems with updating, please try the following: 11.27.1 (2019/08/01)
1. `npm run clean` or `npm run cleanall` --------------------
2. Retry update (Don't forget `npm i`) ### 🐛Fixes
* オブジェクトストレージに関する問題を修正
11.27.0 (2019/07/29)
--------------------
### ✨Improvements
* 「削除して編集」機能を追加
* HTTPリクエストのKeep-AliveとPrxoy対応(サーバーのパフォーマンス向上)
* 通知を種類でフィルタリングして表示できるように
* モバイルで通知ページを表示することができるように
* 非ログイン時の警告処理
### 🐛Fixes
* リモートの絵文字が更新されない問題を修正
* リンクバリデーションリンクが一瞬表示されてしまう問題を修正
* 選択していない状態でウィジェットが追加できる問題を修正
11.26.2 (2019/07/25)
--------------------
### 🐛Fixes
* すでに使われたことのあるユーザー名を再度使えないように
* モバイルのウィジェットページが常に i/update-client-setting を呼び続ける問題を修正
* 投稿フォームのヘッダに添付ファイル数がちゃんと表示されない問題を修正
11.26.1 (2019/07/21)
--------------------
### 🐛Fixes
* リモートアンケートの期限が保存されないのを修正
* 自分をブロックしているユーザーがおすすめユーザーに表示されている問題を修正
* スクロールしてると絵文字ピッカーの位置がずれる問題を修正
* 投稿フォームが画面外にはみ出さないように
* 投稿フォームの「引用付き」の表示が見づらい問題の修正
* 投稿フォームにもうリノートや引用ノートのデータがある場合はリンクを貼っても「引用として添付しますか?」のダイアログボックスが出ないように
* 「タイムライン上部に投稿フォームを表示する」機能の使用時、ノートを投稿しても引用ノートのデータが残る問題の修正
* デスクトップ版のアクティビティウィジェットの日付とデータの表示が変だったのを修正
11.26.0 (2019/07/19)
--------------------
### ✨Improvements
* モデレーターログを記録して確認できるように
* プロフィールに追加情報を設定できるように
* Mastodonのリンクの所有者認証に対応
* AP: Delete Person アクティビティを配信するように
* AP: Delete アクティビティの後にフォロー解除するように
* AP: アカウント削除でもDelete activityを配信するように
* Pages: ラジオボタンを追加
* AdminページのUsers Viewでユーザーのレコードをクリックすることですぐユーザーを照会できるように
* AdminページのUsers Viewでユーザー一覧からユーザー名とホスト情報で検索できるように
* 特定ホストへのメンションの特別処理をクライアントに追加
* 設定画面でデスクトップ・モバイルモード変更時はすぐにrefreshするか伺うように
* ペーストされたファイル名のテンプレート変更時すぐどのようになるか見れるように
* (コ`・ヘ・´ケ)を追加
### 🐛Fixes
* ログインのログが正しく保存されない問題を修正
* 同じユーザー名のユーザーが作成できてしまうことがある問題を修正
* 報告されたレポート内容が表示されない問題を修正
* リモートのプロフィールの追加情報が表示されない問題を修正
* 「見つける」のタグが大文字小文字区別されている問題を修正
* 管理画面のインスタンス一覧でソートが正しく機能していない問題を修正
* プロフィール設定でバナーに動画を設定すると以降編集ができない問題を修正
* ウェブ検索エンジンの設定でグリッチが発生する問題を修正
* MFMの引用がインライン表示になっている問題を修正
* アンケートの期限入力部分のタイトル表示がおかしい問題を修正
* 画面上の項目がすべていなくなると実際はロードされてないだけのファイルやフォルダーがあるにも関わらず「もっと読み込む」ボタンがなくなり「このフォルダーは空です」っていうplaceholderが表示されてしまう問題を修正
* proxy-media後のContent-Typeが違う問題を修正
* ビルド時にエラーが出るのを修正
11.25.1 (2019/07/09) 11.25.1 (2019/07/09)
-------------------- --------------------
@@ -641,9 +707,9 @@ mongodb:
db: misskey db: misskey
``` ```
3. migration ブランチに切り替え 3. migration ブランチに切り替え
4. `npm i` 4. `yarn install`
5. `npm run build` 5. `yarn build`
6. `npm run migrate` 6. `yarn migrate`
7. master ブランチに戻す 7. master ブランチに戻す
8. enjoy 8. enjoy

View File

@@ -7,18 +7,24 @@ Feature suggestions and bug reports are filed in https://github.com/syuilo/missk
* Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one. * Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one.
* If you have multiple independent issues, please submit them separately. * If you have multiple independent issues, please submit them separately.
## Branches
* **master** branch is tracking the latest release and used for production purposes.
* **develop** branch is where we work for the next release.
* **l10n_develop** branch is reserved for localization management.
## Localization (l10n) ## Localization (l10n)
Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management. Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management.
You can improve our translations with your Crowdin account. You can improve our translations with your Crowdin account.
Changes you make in Crowdin will be merged into develop branch. Your changes in Crowdin are automatically submitted as a PR (with the title "New Crowdin translations") to the repository.
The owner [@syuilo](https://github.com/syuilo) merges the PR into the develop branch before the next release.
If you can't find the language you want to contribute with, please open an issue. If your language is not listed in Crowdin, please open an issue.
![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg) ![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
## Internationalization (i18n) ## Internationalization (i18n)
Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n). Misskey uses the Vue.js plugin [Vue I18n](https://github.com/kazupon/vue-i18n).
Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/introduction.html .
## Documentation ## Documentation
* Documents for contributors are located in [`/docs`](/docs). * Documents for contributors are located in [`/docs`](/docs).
@@ -29,9 +35,14 @@ Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
* Test codes are located in [`/test`](/test). * Test codes are located in [`/test`](/test).
## Continuous integration ## Continuous integration
Misskey uses CircleCI for automated test. Misskey uses CircleCI for executing automated tests.
Configuration files are located in [`/.circleci`](/.circleci). Configuration files are located in [`/.circleci`](/.circleci).
## FAQ
### How to resolve conflictions occurred at yarn.lock?
Just execute `yarn` to fix it.
## Glossary ## Glossary
### AP ### AP
Stands for _**A**ctivity**P**ub_. Stands for _**A**ctivity**P**ub_.
@@ -51,11 +62,15 @@ Convert な(na) to にゃ(nya)
#### Denyaize #### Denyaize
Revert Nyaize Revert Nyaize
## Code style ## TypeScript Coding Style
### セミコロンを省略しない ### Do not omit semicolons
ASI Hazardを避けるためでもある This is to avoid Automatic Semicolon Insertion (ASI) hazard.
### 中括弧を省略しない Ref:
* https://www.ecma-international.org/ecma-262/#sec-automatic-semicolon-insertion
* https://github.com/tc39/ecma262/pull/1062
### Do not omit curly brackets
Bad: Bad:
``` ts ``` ts
if (foo) if (foo)
@@ -73,18 +88,38 @@ if (foo) {
} }
``` ```
ただし**`if`が一行**の時だけは省略しても良い As a special case, you can omit the curly brackets if
* the body of the `if`-statement have only one statement and,
* the `if`-statement does not have `else`-clause.
Good: Good:
``` ts ``` ts
if (foo) bar; if (foo) bar;
``` ```
### `export default`を使わない Make sure that the condition and the body statement are on the same line.
インテリセンスと相性が悪かったりするため
参考: ### Do not use `==` when it can simply be replaced with `===`.
* https://gfx.hatenablog.com/entry/2017/11/24/135343 🥰
### Use only boolean (or null related) values in the condition of an `if`-statement.
Bad:
``` ts
if (foo.length)
```
Good:
``` ts
if (foo.length > 0)
```
### Do not use `export default`
This is because the current language support does not work well with `export default`.
Ref:
* https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html * https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
* https://gfx.hatenablog.com/entry/2017/11/24/135343
Bad: Bad:
``` ts ``` ts

View File

@@ -23,9 +23,9 @@ RUN apk add --no-cache \
zlib-dev zlib-dev
COPY package.json ./ COPY package.json ./
RUN npm i RUN yarn install
COPY . ./ COPY . ./
RUN npm run build RUN yarn build
FROM base AS runner FROM base AS runner

View File

@@ -104,38 +104,36 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<!-- PATREON_START --> <!-- PATREON_START -->
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20010324/b8af4bd31ae34fbf8806cc0e6228e400/1.png?token-time=2145916800&token-hash=iyiocfousNIUwASmatsIDq8EOsmLUdrQNkWyktHlmJg%3D" alt="Nemo" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td> <td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td> <td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td> <td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/user?u=20010324">Nemo</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td> <td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td> <td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td> <td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
<td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td> <td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
</tr></table> </tr></table>
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td> <td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td> <td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td> <td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td> <td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td> <td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td> <td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td> <td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
@@ -175,7 +173,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td> <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table> </tr></table>
**Last updated:** Mon, 01 Jul 2019 21:44:06 UTC **Last updated:** Wed, 31 Jul 2019 15:23:08 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

@@ -68,7 +68,7 @@ Build misskey with the following:
*5.* Init DB *5.* Init DB
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
docker-compose run --rm web npm run init docker-compose run --rm web yarn run init
``` ```
*6.* That is it. *6.* That is it.

View File

@@ -68,7 +68,7 @@ cp docker_example.env docker.env
*5.* データベースを初期化 *5.* データベースを初期化
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
docker-compose run --rm web npm run init docker-compose run --rm web yarn run init
``` ```
*6.* 以上です! *6.* 以上です!

View File

@@ -27,6 +27,7 @@ Please install and setup these softwares:
* **[Redis](https://redis.io/)** * **[Redis](https://redis.io/)**
##### Optional ##### Optional
* [Yarn](https://yarnpkg.com/) *Optional but recommended for security reason. If you won't install it, use `npx yarn` instead of `yarn`.*
* [Elasticsearch](https://www.elastic.co/) - required to enable the search feature * [Elasticsearch](https://www.elastic.co/) - required to enable the search feature
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
@@ -50,7 +51,7 @@ Please install and setup these softwares:
5. Install misskey dependencies. 5. Install misskey dependencies.
`npm install` `yarn`
*4.* Configure Misskey *4.* Configure Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
@@ -65,21 +66,20 @@ Please install and setup these softwares:
Build misskey with the following: Build misskey with the following:
`NODE_ENV=production npm run build` `NODE_ENV=production yarn build`
If you're on Debian, you will need to install the `build-essential`, `python` package. If you're on Debian, you will need to install the `build-essential`, `python` package.
If you're still encountering errors about some modules, use node-gyp: If you're still encountering errors about some modules, use node-gyp:
1. `npm install -g node-gyp` 1. `npx node-gyp configure`
2. `node-gyp configure` 2. `npx node-gyp build`
3. `node-gyp build` 3. `NODE_ENV=production yarn build`
4. `NODE_ENV=production npm run build`
*6.* Init DB *6.* Init DB
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
npm run init yarn run init
``` ```
*7.* That is it. *7.* That is it.
@@ -130,12 +130,16 @@ You can check if the service is running with `systemctl status misskey`.
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git checkout master` 1. `git checkout master`
2. `git pull` 2. `git pull`
3. `npm install` 3. `yarn install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production yarn build`
5. `npm run migrate` 5. `yarn migrate`
6. Restart your Misskey process to apply changes 6. Restart your Misskey process to apply changes
7. Enjoy 7. Enjoy
If you encounter any problems with updating, please try the following:
1. `yarn clean` or `yarn cleanall`
2. Retry update (Don't forget `yarn install`
---------------------------------------------------------------- ----------------------------------------------------------------
If you have any questions or troubles, feel free to contact us! If you have any questions or troubles, feel free to contact us!

View File

@@ -27,7 +27,8 @@ Installez les paquets suivants :
* **[Redis](https://redis.io/)** * **[Redis](https://redis.io/)**
##### Optionnels ##### Optionnels
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche * [Yarn](https://yarnpkg.com/) - *recommander pour des raisons de sécurité. Si vous ne l'installez pas, utilisez `npx yarn` au lieu de` yarn`.*
* [Elasticsearch](https://www.elastic.co/) - *requis pour pouvoir activer la fonctionnalité de recherche.*
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
*3.* Installation de Misskey *3.* Installation de Misskey
@@ -50,7 +51,7 @@ Installez les paquets suivants :
5. Installez les dépendances de misskey. 5. Installez les dépendances de misskey.
`npm install` `yarn install`
*4.* Création du fichier de configuration *4.* Création du fichier de configuration
---------------------------------------------------------------- ----------------------------------------------------------------
@@ -65,23 +66,22 @@ Installez les paquets suivants :
Construisez Misskey comme ceci : Construisez Misskey comme ceci :
`NODE_ENV=production npm run build` `NODE_ENV=production yarn build`
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`. Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`.
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp: Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
1. `npm install -g node-gyp` 1. `npx node-gyp configure`
2. `node-gyp configure` 2. `npx node-gyp build`
3. `node-gyp build` 3. `NODE_ENV=production yarn build`
4. `NODE_ENV=production npm run build`
*6.* C'est tout. *6.* C'est tout.
---------------------------------------------------------------- ----------------------------------------------------------------
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
### Lancement conventionnel ### Lancement conventionnel
Lancez tout simplement `NODE_ENV=production npm start`. Bonne chance et amusez-vous bien ! Lancez tout simplement `NODE_ENV=production yarn start`. Bonne chance et amusez-vous bien !
### Démarrage avec systemd ### Démarrage avec systemd
@@ -124,9 +124,9 @@ Vous pouvez vérifier si le service a démarré en utilisant la commande `system
### Méthode de mise à jour vers la plus récente version de Misskey ### Méthode de mise à jour vers la plus récente version de Misskey
1. `git checkout master` 1. `git checkout master`
2. `git pull` 2. `git pull`
3. `npm install` 3. `yarn install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production yarn build`
5. `npm run migrate` 5. `yarn migrate`
---------------------------------------------------------------- ----------------------------------------------------------------

View File

@@ -27,6 +27,8 @@ adduser --disabled-password --disabled-login misskey
* **[Redis](https://redis.io/)** * **[Redis](https://redis.io/)**
##### オプション ##### オプション
* [Yarn](https://yarnpkg.com/)
* セキュリティの観点から推奨されます。 yarn をインストールしない方針の場合は、文章中の `yarn` を適宜 `npx yarn` と読み替えてください。
* [Elasticsearch](https://www.elastic.co/) * [Elasticsearch](https://www.elastic.co/)
* 検索機能を有効にするためにはインストールが必要です。 * 検索機能を有効にするためにはインストールが必要です。
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
@@ -51,7 +53,7 @@ adduser --disabled-password --disabled-login misskey
5. Misskeyの依存パッケージをインストール 5. Misskeyの依存パッケージをインストール
`npm install` `yarn install`
*4.* 設定ファイルを作成する *4.* 設定ファイルを作成する
---------------------------------------------------------------- ----------------------------------------------------------------
@@ -66,20 +68,19 @@ adduser --disabled-password --disabled-login misskey
次のコマンドでMisskeyをビルドしてください: 次のコマンドでMisskeyをビルドしてください:
`NODE_ENV=production npm run build` `NODE_ENV=production yarn build`
Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。 Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。
何らかのモジュールでエラーが発生する場合はnode-gypを使ってください: 何らかのモジュールでエラーが発生する場合はnode-gypを使ってください:
1. `npm install -g node-gyp` 1. `npx node-gyp configure`
2. `node-gyp configure` 2. `npx node-gyp build`
3. `node-gyp build` 3. `NODE_ENV=production yarn build`
4. `NODE_ENV=production npm run build`
*6.* データベースを初期化 *6.* データベースを初期化
---------------------------------------------------------------- ----------------------------------------------------------------
``` shell ``` shell
npm run init yarn run init
``` ```
*7.* 以上です! *7.* 以上です!
@@ -87,7 +88,7 @@ npm run init
お疲れ様でした。これでMisskeyを動かす準備は整いました。 お疲れ様でした。これでMisskeyを動かす準備は整いました。
### 通常起動 ### 通常起動
`NODE_ENV=production npm start`するだけです。GLHF! `NODE_ENV=production yarn start`するだけです。GLHF!
### systemdを用いた起動 ### systemdを用いた起動
1. systemdサービスのファイルを作成 1. systemdサービスのファイルを作成
@@ -120,7 +121,7 @@ npm run init
3. systemdを再読み込みしmisskeyサービスを有効化 3. systemdを再読み込みしmisskeyサービスを有効化
`systemctl daemon-reload ; systemctl enable misskey` `systemctl daemon-reload; systemctl enable misskey`
4. misskeyサービスの起動 4. misskeyサービスの起動
@@ -131,11 +132,11 @@ npm run init
### Misskeyを最新バージョンにアップデートする方法: ### Misskeyを最新バージョンにアップデートする方法:
1. `git checkout master` 1. `git checkout master`
2. `git pull` 2. `git pull`
3. `npm install` 3. `yarn install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production yarn build`
5. `npm run migrate` 5. `yarn migrate`
なにか問題が発生した場合は、`npm run clean`または`npm run cleanall`すると直る場合があります。 なにか問題が発生した場合は、`yarn clean`または`yarn cleanall`すると直る場合があります。
---------------------------------------------------------------- ----------------------------------------------------------------

View File

@@ -34,6 +34,15 @@ common:
signup: "Registrovat" signup: "Registrovat"
signout: "Odhlásit" signout: "Odhlásit"
reload-to-apply-the-setting: "Pro uplatnění tohoto nastavení musíte znovu načíst tuto stránku. Chcete ji načíst teď?" reload-to-apply-the-setting: "Pro uplatnění tohoto nastavení musíte znovu načíst tuto stránku. Chcete ji načíst teď?"
notification-types:
all: "Všechny"
pollVote: "Hlasy"
follow: "Sledovaní"
reply: "Odpovědi"
quote: "Citace"
renote: "Renotovat"
mention: "Zmínky"
reaction: "Reakce"
got-it: "Rozumím!" got-it: "Rozumím!"
customization-tips: customization-tips:
title: "Tipy pro přizpůsobení" title: "Tipy pro přizpůsobení"
@@ -89,6 +98,7 @@ common:
"read:notifications": "Prohlížet oznámení" "read:notifications": "Prohlížet oznámení"
"write:notifications": "Pracovat s oznámeními" "write:notifications": "Pracovat s oznámeními"
"read:reactions": "Prohlížet reakce" "read:reactions": "Prohlížet reakce"
"write:reactions": "Narabět s reakcemi"
"write:votes": "Hlasovat" "write:votes": "Hlasovat"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose" follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
@@ -98,6 +108,7 @@ common:
hide-contents: "Schovat obsah" hide-contents: "Schovat obsah"
reply-placeholder: "Odpovědět na tento příspěvek" reply-placeholder: "Odpovědět na tento příspěvek"
quote-placeholder: "Citovat tento příspěvek" quote-placeholder: "Citovat tento příspěvek"
quote-attached: "Přiložit citaci"
submit: "Odeslat" submit: "Odeslat"
reply: "Odpovědět" reply: "Odpovědět"
renote: "Renotovat" renote: "Renotovat"
@@ -179,6 +190,7 @@ common:
remember-note-visibility: "Zapamatovat viditelnost příspěvků" remember-note-visibility: "Zapamatovat viditelnost příspěvků"
web-search-engine: "Webové vyhledávače" web-search-engine: "Webové vyhledávače"
web-search-engine-desc: "Například: https://www.google.com/?#q={{query}}" web-search-engine-desc: "Například: https://www.google.com/?#q={{query}}"
paste: "Vložit"
keep-cw: "Zachovat varování o obsahu" keep-cw: "Zachovat varování o obsahu"
keep-cw-desc: "Při odpovědi na příspěvek bude varování o obsahu nastaveno stejně jako původní příspěvek." keep-cw-desc: "Při odpovědi na příspěvek bude varování o obsahu nastaveno stejně jako původní příspěvek."
i-like-sushi: "Mam radši sushi (než puding)" i-like-sushi: "Mam radši sushi (než puding)"
@@ -262,6 +274,8 @@ common:
load-raw-images: "Zobrazovat obrázky v původní kvalitě" load-raw-images: "Zobrazovat obrázky v původní kvalitě"
load-remote-media: "Zobrazovat média ze vzdáleného serveru" load-remote-media: "Zobrazovat média ze vzdáleného serveru"
sync: "Synchronizace" sync: "Synchronizace"
save: "Uložit"
saved: "Uloženo"
search: "Hledání" search: "Hledání"
delete: "Odstranit" delete: "Odstranit"
loading: "Načítám..." loading: "Načítám..."
@@ -452,9 +466,12 @@ common/views/components/messaging.vue:
no-history: "Žádná historie" no-history: "Žádná historie"
user: "Uživatel" user: "Uživatel"
group: "Skupina" group: "Skupina"
start-with-user: "Zahájit konverzaci s uživatelem"
start-with-group: "Zahájit skupinovou konverzaci"
common/views/components/messaging-room.vue: common/views/components/messaging-room.vue:
new-message: "Máte novou zprávu" new-message: "Máte novou zprávu"
common/views/components/messaging-room.form.vue: common/views/components/messaging-room.form.vue:
input-message-here: "Sem zadejte zprávu"
send: "Odeslat" send: "Odeslat"
attach-from-local: "Přiložit soubory z Vašeho zařízení" attach-from-local: "Přiložit soubory z Vašeho zařízení"
common/views/components/messaging-room.message.vue: common/views/components/messaging-room.message.vue:
@@ -642,6 +659,7 @@ common/views/components/profile-editor.vue:
saved: "Profil byl úspěšně aktualizován" saved: "Profil byl úspěšně aktualizován"
uploading: "Nahrávám" uploading: "Nahrávám"
upload-failed: "Nahrávání selhalo" upload-failed: "Nahrávání selhalo"
unable-to-process: "Operace nemohla být dokončena."
email: "Nastavení e-mailů" email: "Nastavení e-mailů"
email-address: "Emailová adresa" email-address: "Emailová adresa"
email-verified: "Váš e-mail byl ověřen" email-verified: "Váš e-mail byl ověřen"
@@ -658,6 +676,9 @@ common/views/components/profile-editor.vue:
danger-zone: "Nebezpečná zóna" danger-zone: "Nebezpečná zóna"
delete-account: "Smazat účet" delete-account: "Smazat účet"
account-deleted: "Váš účet byl smazán. Může chvilku trvat než zmizí všechna data." account-deleted: "Váš účet byl smazán. Může chvilku trvat než zmizí všechna data."
profile-metadata: "Metadata profilu"
metadata-label: "Popis"
metadata-content: "Obsah"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Uživatel" users: "Uživatel"
rename: "Přejmenovat seznam" rename: "Přejmenovat seznam"
@@ -731,6 +752,7 @@ desktop:
avatar: "Avatar" avatar: "Avatar"
uploading-avatar: "Nahrál nový avatar" uploading-avatar: "Nahrál nový avatar"
avatar-updated: "Vaše avatar byl aktualizován" avatar-updated: "Vaše avatar byl aktualizován"
unable-to-process: "Operace nemohla být dokončena."
invalid-filetype: "Tento formát souboru není podporován" invalid-filetype: "Tento formát souboru není podporován"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Černá ... Celkem" total: "Černá ... Celkem"
@@ -1039,6 +1061,8 @@ admin/views/users.vue:
reset-password-confirm: "Opravdu chcete resetovat Vaše heslo?" reset-password-confirm: "Opravdu chcete resetovat Vaše heslo?"
password-updated: "Heslo je nyní \"{password}\"" password-updated: "Heslo je nyní \"{password}\""
update-remote-user: "Aktualizovat informace o vzdáleném účtu" update-remote-user: "Aktualizovat informace o vzdáleném účtu"
username: "Přezdívka"
host: "Hostitel"
users: users:
title: "Uživatel" title: "Uživatel"
state: state:
@@ -1055,6 +1079,11 @@ admin/views/users.vue:
admin/views/moderators.vue: admin/views/moderators.vue:
add-moderator: add-moderator:
title: "Vytvořit moderátora" title: "Vytvořit moderátora"
logs:
title: "Logy"
moderator: "Moderátoři"
type: "Operace"
info: "Informace"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
title: "Přidat emoji" title: "Přidat emoji"
@@ -1246,6 +1275,8 @@ mobile/views/pages/search.vue:
not-found: "Pro '{q}' nebyly nalezeny žádné příspěvky." not-found: "Pro '{q}' nebyly nalezeny žádné příspěvky."
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "Vybrat soubory" select-file: "Vybrat soubory"
mobile/views/pages/notifications.vue:
notifications: "Oznámení"
mobile/views/pages/user/home.vue: mobile/views/pages/user/home.vue:
activity: "Aktivita" activity: "Aktivita"
frequently-replied-users: "Častá zmínění" frequently-replied-users: "Častá zmínění"
@@ -1292,6 +1323,8 @@ pages:
_action: _action:
_dialog: _dialog:
content: "Obsah" content: "Obsah"
_radioButton:
title: "Titulek"
script: script:
categories: categories:
random: "Náhodně" random: "Náhodně"

View File

@@ -35,6 +35,12 @@ common:
signout: "Log ud" signout: "Log ud"
reload-to-apply-the-setting: "Denne indstilling slår først igennem, når du har genindlæst siden. Vil du genindlæse siden nu?" reload-to-apply-the-setting: "Denne indstilling slår først igennem, når du har genindlæst siden. Vil du genindlæse siden nu?"
fetching-as-ap-object: "Tilladelse til sammenkobling" fetching-as-ap-object: "Tilladelse til sammenkobling"
notification-types:
all: "Alle"
follow: "Følger"
reply: "Svar"
renote: "Gen-postering"
reaction: "Reaktion"
got-it: "Det er OK" got-it: "Det er OK"
customization-tips: customization-tips:
title: "Tips om tilpasning" title: "Tips om tilpasning"
@@ -251,6 +257,8 @@ common:
disable-via-mobile: "Marker aldrig posten som \"fra mobil\"" disable-via-mobile: "Marker aldrig posten som \"fra mobil\""
load-raw-images: "Vis vedhæftede bilag i original kvalitet" load-raw-images: "Vis vedhæftede bilag i original kvalitet"
load-remote-media: "Vis medie-materiale fra en ekstern server" load-remote-media: "Vis medie-materiale fra en ekstern server"
save: "Gem"
saved: "Gemt"
search: "Søg" search: "Søg"
delete: "Slet" delete: "Slet"
loading: "Henter" loading: "Henter"
@@ -684,6 +692,7 @@ common/views/components/profile-editor.vue:
saved: "Profil er opdateret med succes" saved: "Profil er opdateret med succes"
uploading: "Overfører" uploading: "Overfører"
upload-failed: "Fejl ved overførsel" upload-failed: "Fejl ved overførsel"
unable-to-process: "Handlingen kunne ikke gennemføres."
email: "Email indstillinger" email: "Email indstillinger"
email-address: "Email adresse" email-address: "Email adresse"
email-verified: "Din email er blevet bekræftet" email-verified: "Din email er blevet bekræftet"
@@ -703,6 +712,7 @@ common/views/components/profile-editor.vue:
danger-zone: "Risici" danger-zone: "Risici"
delete-account: "Slet kontoen" delete-account: "Slet kontoen"
account-deleted: "Kontoen er slettet. Det kan vare lidt, inden alle data forsvinder." account-deleted: "Kontoen er slettet. Det kan vare lidt, inden alle data forsvinder."
metadata-content: "Indhold"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Bruger" users: "Bruger"
rename: "Omdøb listen" rename: "Omdøb listen"
@@ -809,6 +819,7 @@ desktop:
uploading-avatar: "Overfør en ny avatar" uploading-avatar: "Overfør en ny avatar"
avatar-updated: "Avatar er overført med succes" avatar-updated: "Avatar er overført med succes"
choose-avatar: "Vælg et billede til din avatar" choose-avatar: "Vælg et billede til din avatar"
unable-to-process: "Handlingen kunne ikke gennemføres."
invalid-filetype: "Denne filtype kan ikke benyttes her" invalid-filetype: "Denne filtype kan ikke benyttes her"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Sort ... Total" total: "Sort ... Total"
@@ -1276,6 +1287,8 @@ admin/views/users.vue:
remote-user-updated: "Informationen om den eksterne bruger er nu blevet opdateret." remote-user-updated: "Informationen om den eksterne bruger er nu blevet opdateret."
delete-all-files: "Slet alle filer" delete-all-files: "Slet alle filer"
delete-all-files-confirm: "Er du sikker på, at alle filerne skal slettes?" delete-all-files-confirm: "Er du sikker på, at alle filerne skal slettes?"
username: "Brugernavn"
host: "Vært"
users: users:
title: "Bruger" title: "Bruger"
sort: sort:
@@ -1306,6 +1319,11 @@ admin/views/moderators.vue:
added: "Redaktør er oprettet" added: "Redaktør er oprettet"
remove: "Fjern" remove: "Fjern"
removed: "Redaktøren er nu fjernet" removed: "Redaktøren er nu fjernet"
logs:
title: "Logs"
moderator: "Redaktører"
type: "Drift"
info: "Information"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
title: "Tilføj emoji" title: "Tilføj emoji"
@@ -1594,6 +1612,8 @@ mobile/views/pages/search.vue:
not-found: "Ingen poster fundet for \"{q}\"" not-found: "Ingen poster fundet for \"{q}\""
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "Vælg fil(er)" select-file: "Vælg fil(er)"
mobile/views/pages/notifications.vue:
notifications: "Notifikationer"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "Logget ind som {}" signed-in-as: "Logget ind som {}"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
@@ -1715,6 +1735,10 @@ pages:
_dialog: _dialog:
content: "Indhold" content: "Indhold"
resetRandom: "Nulstil tilfældigt tal" resetRandom: "Nulstil tilfældigt tal"
_radioButton:
name: "Variabel navn"
title: "Titel"
default: "Standard værdi"
script: script:
categories: categories:
flow: "Kontrol" flow: "Kontrol"

View File

@@ -35,6 +35,9 @@ common:
signout: "Ausloggen" signout: "Ausloggen"
reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?" reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?"
fetching-as-ap-object: "Hole Daten…" fetching-as-ap-object: "Hole Daten…"
notification-types:
reply: "Antworten"
renote: "Anmerkung"
got-it: "Verstanden!" got-it: "Verstanden!"
customization-tips: customization-tips:
title: "Anpassung-Tipps" title: "Anpassung-Tipps"
@@ -248,6 +251,8 @@ common:
disable-via-mobile: "Beitrag nicht als „vom Handy“ markieren" disable-via-mobile: "Beitrag nicht als „vom Handy“ markieren"
load-raw-images: "Anhänge in voller Größe laden" load-raw-images: "Anhänge in voller Größe laden"
load-remote-media: "Zeige Inhalte von fremden Servern" load-remote-media: "Zeige Inhalte von fremden Servern"
save: "Speichern"
saved: "Gespeichert"
search: "Suche" search: "Suche"
delete: "Löschen" delete: "Löschen"
loading: "Laden" loading: "Laden"
@@ -565,6 +570,7 @@ common/views/components/profile-editor.vue:
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
save: "Speichern" save: "Speichern"
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
export: "Exportieren" export: "Exportieren"
import: "Importieren" import: "Importieren"
export-targets: export-targets:
@@ -598,6 +604,7 @@ common/views/widgets/memo.vue:
save: "Speichern" save: "Speichern"
desktop: desktop:
banner: "Banner" banner: "Banner"
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Schwarz ... komplett" total: "Schwarz ... komplett"
notes: "Blau ... Hinweise" notes: "Blau ... Hinweise"
@@ -782,6 +789,7 @@ admin/views/drive.vue:
local: "Lokal" local: "Lokal"
delete: "Löschen" delete: "Löschen"
admin/views/users.vue: admin/views/users.vue:
username: "Benutzername"
users: users:
origin: origin:
local: "Lokal" local: "Lokal"
@@ -871,6 +879,8 @@ mobile/views/pages/note.vue:
next: "Nächster Kommentar" next: "Nächster Kommentar"
mobile/views/pages/search.vue: mobile/views/pages/search.vue:
search: "Suchen" search: "Suchen"
mobile/views/pages/notifications.vue:
notifications: "Benachrichtigungen"
mobile/views/pages/user/home.vue: mobile/views/pages/user/home.vue:
activity: "Aktivität" activity: "Aktivität"
keywords: "Schlagwörter" keywords: "Schlagwörter"

View File

@@ -34,8 +34,20 @@ common:
signup: "Sign up" signup: "Sign up"
signout: "Logout" signout: "Logout"
reload-to-apply-the-setting: "You'll need to reload the page to reflect this setting. Do you want to reload it now?" reload-to-apply-the-setting: "You'll need to reload the page to reflect this setting. Do you want to reload it now?"
fetching-as-ap-object: "Inquiring to union" fetching-as-ap-object: "Inquiring to fediverse"
unfollow-confirm: "Do you want to unfollow {name}?" unfollow-confirm: "Do you want to unfollow {name}?"
signin-required: "Please Log In"
notification-type: "Notification Type"
notification-types:
all: "All"
pollVote: "Votes"
follow: "Following"
receiveFollowRequest: "Follow requests"
reply: "Reply"
quote: "Quote"
renote: "Renote"
mention: "Mentions"
reaction: "Reaction"
got-it: "Got it!" got-it: "Got it!"
customization-tips: customization-tips:
title: "Customization tips" title: "Customization tips"
@@ -202,6 +214,7 @@ common:
use-avatar-reversi-stones: "Use avatar as a stone in reversi" use-avatar-reversi-stones: "Use avatar as a stone in reversi"
disable-animated-mfm: "Disable animated texts in a post" disable-animated-mfm: "Disable animated texts in a post"
disable-showing-animated-images: "Do not play animated images" disable-showing-animated-images: "Do not play animated images"
enable-quick-notification-view: "Enable Quick Notification View"
suggest-recent-hashtags: "Show recent popular hashtags on the post form" suggest-recent-hashtags: "Show recent popular hashtags on the post form"
always-show-nsfw: "Always show NSFW contents" always-show-nsfw: "Always show NSFW contents"
always-mark-nsfw: "Always mark posts with media attachments as NSFW" always-mark-nsfw: "Always mark posts with media attachments as NSFW"
@@ -278,6 +291,8 @@ common:
load-raw-images: "Show attached images in original quality" load-raw-images: "Show attached images in original quality"
load-remote-media: "Show media from a remote server" load-remote-media: "Show media from a remote server"
sync: "Sync" sync: "Sync"
save: "Save"
saved: "Saved"
home-profile: "Home profile" home-profile: "Home profile"
deck-profile: "Deck profile" deck-profile: "Deck profile"
search: "Search" search: "Search"
@@ -529,6 +544,8 @@ common/views/components/note-menu.vue:
unpin: "Unpin" unpin: "Unpin"
delete: "Delete" delete: "Delete"
delete-confirm: "Are you sure you want to delete this post?" delete-confirm: "Are you sure you want to delete this post?"
delete-and-edit: "Delete and Edit"
delete-and-edit-confirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it."
remote: "Show original note" remote: "Show original note"
pin-limit-exceeded: "You can't pin any more posts." pin-limit-exceeded: "You can't pin any more posts."
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
@@ -616,7 +633,7 @@ common/views/components/signin.vue:
signin-with-twitter: "Log in with Twitter" signin-with-twitter: "Log in with Twitter"
signin-with-github: "Sign in with GitHub" signin-with-github: "Sign in with GitHub"
signin-with-discord: "Sign in with Discord" signin-with-discord: "Sign in with Discord"
login-failed: "Logging in has failed. Make sure you have entered the correct username and password." login-failed: "Unable to log in. The username or password you entered is incorrect."
tap-key: "Click on the Security Key to log in" tap-key: "Click on the Security Key to log in"
enter-2fa-code: "Enter your verification code" enter-2fa-code: "Enter your verification code"
common/views/components/signup.vue: common/views/components/signup.vue:
@@ -710,7 +727,7 @@ common/views/components/profile-editor.vue:
you-can-include-hashtags: "You can also include hashtags in your profile description." you-can-include-hashtags: "You can also include hashtags in your profile description."
language: "Language" language: "Language"
birthday: "Birthday" birthday: "Birthday"
avatar: "Icon" avatar: "Avatar"
banner: "Banner" banner: "Banner"
is-cat: "This account is a Cat" is-cat: "This account is a Cat"
is-bot: "This account is a Bot" is-bot: "This account is a Bot"
@@ -723,6 +740,9 @@ common/views/components/profile-editor.vue:
saved: "Profile updated successfully" saved: "Profile updated successfully"
uploading: "Uploading" uploading: "Uploading"
upload-failed: "Failed to upload" upload-failed: "Failed to upload"
unable-to-process: "The operation could not be completed."
avatar-not-an-image: "The file specified as an avatar is not an image"
banner-not-an-image: "The file specified as a banner is not an image"
email: "Email settings" email: "Email settings"
email-address: "Email Address" email-address: "Email Address"
email-verified: "Your email has been verified." email-verified: "Your email has been verified."
@@ -742,6 +762,9 @@ common/views/components/profile-editor.vue:
danger-zone: "Cautious options" danger-zone: "Cautious options"
delete-account: "Remove the account" delete-account: "Remove the account"
account-deleted: "The account has been deleted. It may take some time until all of the data disappears." account-deleted: "The account has been deleted. It may take some time until all of the data disappears."
profile-metadata: "Profile metadata"
metadata-label: "Label"
metadata-content: "Content"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "User" users: "User"
rename: "Rename list" rename: "Rename list"
@@ -851,6 +874,7 @@ desktop:
uploading-avatar: "Uploading a new avatar" uploading-avatar: "Uploading a new avatar"
avatar-updated: "Successfully updated the avatar" avatar-updated: "Successfully updated the avatar"
choose-avatar: "Select an image for the avatar" choose-avatar: "Select an image for the avatar"
unable-to-process: "The operation could not be completed."
invalid-filetype: "This filetype is not acceptable here" invalid-filetype: "This filetype is not acceptable here"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Black ... Total" total: "Black ... Total"
@@ -1363,6 +1387,8 @@ admin/views/users.vue:
remote-user-updated: "The information regarding the remote user has been updated." remote-user-updated: "The information regarding the remote user has been updated."
delete-all-files: "Delete all files" delete-all-files: "Delete all files"
delete-all-files-confirm: "Are you sure that you want to delete all files?" delete-all-files-confirm: "Are you sure that you want to delete all files?"
username: "Username"
host: "Host"
users: users:
title: "Users" title: "Users"
sort: sort:
@@ -1393,6 +1419,12 @@ admin/views/moderators.vue:
added: "Registered a Moderator." added: "Registered a Moderator."
remove: "Discharge" remove: "Discharge"
removed: "The moderator has been discharged" removed: "The moderator has been discharged"
logs:
title: "Logs"
moderator: "Moderators"
type: "Operations"
at: "Timestamp"
info: "Information"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
title: "Add emoji" title: "Add emoji"
@@ -1681,6 +1713,8 @@ mobile/views/pages/search.vue:
not-found: "No posts were found for '{q}'" not-found: "No posts were found for '{q}'"
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "Choose files" select-file: "Choose files"
mobile/views/pages/notifications.vue:
notifications: "Notifications"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "Signed in as {}" signed-in-as: "Signed in as {}"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
@@ -1861,6 +1895,12 @@ pages:
message: "Message to display when pressed" message: "Message to display when pressed"
variable: "Variable to send" variable: "Variable to send"
no-variable: "None" no-variable: "None"
radioButton: "Choices"
_radioButton:
name: "Variable name"
title: "Title"
values: "Item of choices that delimited by line breaks"
default: "Default value"
script: script:
categories: categories:
flow: "Control" flow: "Control"

View File

@@ -31,6 +31,10 @@ common:
signin: "Iniciar sesión" signin: "Iniciar sesión"
signup: "¡Regístrate!" signup: "¡Regístrate!"
signout: "Cerrar sesión" signout: "Cerrar sesión"
notification-types:
all: "Todo"
reply: "Responder"
renote: "Volver a publicar"
got-it: "¡Listo!" got-it: "¡Listo!"
customization-tips: customization-tips:
title: "Consejos de personalización" title: "Consejos de personalización"
@@ -197,6 +201,8 @@ common:
update-available-desc: "Las actualizaciones se aplicarán cuando la página se vuelva a cargar." update-available-desc: "Las actualizaciones se aplicarán cuando la página se vuelva a cargar."
advanced-settings: "Configuraciones avanzadas" advanced-settings: "Configuraciones avanzadas"
navbar-position-left: "Izquierda" navbar-position-left: "Izquierda"
save: "Guardar"
saved: "Guardado"
search: "Buscar" search: "Buscar"
delete: "eliminar" delete: "eliminar"
loading: "cargando" loading: "cargando"
@@ -567,6 +573,7 @@ common/views/components/profile-editor.vue:
saved: "Perfil actualizado con exito" saved: "Perfil actualizado con exito"
uploading: "Subiendo" uploading: "Subiendo"
upload-failed: "Error al subir" upload-failed: "Error al subir"
unable-to-process: "La operación no se puede llevar a cabo"
email: "Preferencias de correo" email: "Preferencias de correo"
email-address: "Correo electrónico" email-address: "Correo electrónico"
email-verified: "Tu cuenta de correo ha sido verificada." email-verified: "Tu cuenta de correo ha sido verificada."
@@ -669,6 +676,7 @@ desktop:
uploading-avatar: "Cargando un nuevo avatar" uploading-avatar: "Cargando un nuevo avatar"
avatar-updated: "Avatar actualizado" avatar-updated: "Avatar actualizado"
choose-avatar: "Escoge una imagen de avatar" choose-avatar: "Escoge una imagen de avatar"
unable-to-process: "La operación no se puede llevar a cabo"
invalid-filetype: "Este tipo de archivo no es compatible aquí" invalid-filetype: "Este tipo de archivo no es compatible aquí"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Negro ... Total" total: "Negro ... Total"
@@ -962,12 +970,18 @@ admin/views/drive.vue:
mark-as-sensitive: "Marcar como 'sensible'" mark-as-sensitive: "Marcar como 'sensible'"
unmark-as-sensitive: "Desmarcar como 'sensible'" unmark-as-sensitive: "Desmarcar como 'sensible'"
admin/views/users.vue: admin/views/users.vue:
username: "Usuario"
host: "Host"
users: users:
state: state:
all: "Todo" all: "Todo"
moderator: "Moderadores" moderator: "Moderadores"
origin: origin:
local: "Local" local: "Local"
admin/views/moderators.vue:
logs:
title: "Registros"
moderator: "Moderadores"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
add: "Agregar" add: "Agregar"
@@ -1079,6 +1093,8 @@ mobile/views/pages/games/reversi.vue:
reversi: "Reversi" reversi: "Reversi"
mobile/views/pages/search.vue: mobile/views/pages/search.vue:
search: "Buscar" search: "Buscar"
mobile/views/pages/notifications.vue:
notifications: "Notificaciones"
mobile/views/pages/user/home.vue: mobile/views/pages/user/home.vue:
activity: "Actividad" activity: "Actividad"
mobile/views/pages/user/home.photos.vue: mobile/views/pages/user/home.photos.vue:

View File

@@ -35,6 +35,18 @@ common:
signout: "Se déconnecter" signout: "Se déconnecter"
reload-to-apply-the-setting: "Le rechargement de la page est nécessaire pour appliquer ces paramètres. Désirez-vous la recharger maintenant ?" reload-to-apply-the-setting: "Le rechargement de la page est nécessaire pour appliquer ces paramètres. Désirez-vous la recharger maintenant ?"
unfollow-confirm: "Désirez-vous vous désabonner de {name} ?" unfollow-confirm: "Désirez-vous vous désabonner de {name} ?"
signin-required: "Veuillez vous connecter"
notification-type: "Type de notification"
notification-types:
all: "Tout"
pollVote: "Sondage"
follow: "Abonnements"
receiveFollowRequest: "Demandes dabonnements"
reply: "Répondre"
quote: "Cité par"
renote: "Republier"
mention: "Mentions"
reaction: "Réaction"
got-it: "Jai compris !" got-it: "Jai compris !"
customization-tips: customization-tips:
title: "Conseils de personnalisation" title: "Conseils de personnalisation"
@@ -118,6 +130,7 @@ common:
add-visible-user: "Ajouter un utilisateur" add-visible-user: "Ajouter un utilisateur"
cw-placeholder: "Commenter le contenu (optionnel)" cw-placeholder: "Commenter le contenu (optionnel)"
username-prompt: "Saisir un nom d'utilisateur" username-prompt: "Saisir un nom d'utilisateur"
enter-file-name: "Éditer le nom du fichier"
weekday-short: weekday-short:
sunday: "D" sunday: "D"
monday: "L" monday: "L"
@@ -184,6 +197,7 @@ common:
remember-note-visibility: "Se souvenir du mode de visibilité de la publication" remember-note-visibility: "Se souvenir du mode de visibilité de la publication"
web-search-engine: "Moteur de recherche Web" web-search-engine: "Moteur de recherche Web"
web-search-engine-desc: "Exemple: https://www.google.com/?#q={{query}}" web-search-engine-desc: "Exemple: https://www.google.com/?#q={{query}}"
paste: "Coller"
keep-cw: "Maintenir l'avertissement de contenu" keep-cw: "Maintenir l'avertissement de contenu"
keep-cw-desc: "Lorsque vous répondez à un message, le même avertissement de contenu est reprit par défaut dans la réponse, le même que celui qui a été défini dans le message original." keep-cw-desc: "Lorsque vous répondez à un message, le même avertissement de contenu est reprit par défaut dans la réponse, le même que celui qui a été défini dans le message original."
i-like-sushi: "Je préfère les sushis plutôt que le pudding" i-like-sushi: "Je préfère les sushis plutôt que le pudding"
@@ -267,6 +281,8 @@ common:
load-raw-images: "Afficher les photos jointes dans leur qualité originale" load-raw-images: "Afficher les photos jointes dans leur qualité originale"
load-remote-media: "Afficher les médias depuis le serveur distant" load-remote-media: "Afficher les médias depuis le serveur distant"
sync: "Synchroniser" sync: "Synchroniser"
save: "Enregistrer"
saved: "enregistré"
search: "Recherche" search: "Recherche"
delete: "Supprimer" delete: "Supprimer"
loading: "Chargement en cours…" loading: "Chargement en cours…"
@@ -388,6 +404,7 @@ common/views/components/games/reversi/reversi.room.vue:
black-or-white: "Noirs/Blancs" black-or-white: "Noirs/Blancs"
black-is: "{} Noirs" black-is: "{} Noirs"
rules: "Règles" rules: "Règles"
is-llotheo: "Celui ou celle qui a le moins de pièces gagne (Llotheo)"
looped-map: "Carte en boucle" looped-map: "Carte en boucle"
can-put-everywhere: "Peut poser partout" can-put-everywhere: "Peut poser partout"
settings-of-the-bot: "Configuration du bot" settings-of-the-bot: "Configuration du bot"
@@ -515,6 +532,7 @@ common/views/components/note-menu.vue:
unpin: "Désépingler" unpin: "Désépingler"
delete: "Supprimer" delete: "Supprimer"
delete-confirm: "Supprimer cette publication ?" delete-confirm: "Supprimer cette publication ?"
delete-and-edit: "Effacer et éditer"
remote: "Afficher la note originale" remote: "Afficher la note originale"
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
mention: "Mention" mention: "Mention"
@@ -555,6 +573,7 @@ common/views/components/poll-editor.vue:
remove: "Supprimer ce choix" remove: "Supprimer ce choix"
add: "+ Ajouter un choix" add: "+ Ajouter un choix"
destroy: "Annuler ce sondage" destroy: "Annuler ce sondage"
multiple: "Autoriser le multi-choix"
expiration: "Valide jusqu'à" expiration: "Valide jusqu'à"
infinite: "Illimité" infinite: "Illimité"
at: "Choisir une date et une durée" at: "Choisir une date et une durée"
@@ -581,6 +600,12 @@ common/views/components/emoji-picker.vue:
symbols: "Symboles" symbols: "Symboles"
flags: "Drapeaux" flags: "Drapeaux"
common/views/components/settings/app-type.vue: common/views/components/settings/app-type.vue:
title: "Mode"
intro: "Vous pouvez choisir, si vous voulez utiliser la disposition de bureau ou mobile."
choices:
auto: "Choisir la disposition automatiquement"
desktop: "Toujours utiliser la disposition de bureau"
mobile: "Toujours utiliser la disposition mobile"
info: "Le rechargement de la page est requis afin d'appliquer les modifications." info: "Le rechargement de la page est requis afin d'appliquer les modifications."
common/views/components/signin.vue: common/views/components/signin.vue:
username: "Nom d'utilisateur·rice" username: "Nom d'utilisateur·rice"
@@ -698,6 +723,9 @@ common/views/components/profile-editor.vue:
saved: "Profil mis à jour avec succès" saved: "Profil mis à jour avec succès"
uploading: "En cours denvoi …" uploading: "En cours denvoi …"
upload-failed: "Échec de l'envoi" upload-failed: "Échec de l'envoi"
unable-to-process: "L'opération n'a pas pu être complétée"
avatar-not-an-image: "Le fichier sélectionné pour votre avatar n'est pas une image"
banner-not-an-image: "Le fichier sélectionné pour votre bannière n'est pas une image"
email: "Paramètres de messagerie" email: "Paramètres de messagerie"
email-address: "Adresse de courrier électronique" email-address: "Adresse de courrier électronique"
email-verified: "Ladresse du courrier électronique a été vérifiée." email-verified: "Ladresse du courrier électronique a été vérifiée."
@@ -717,6 +745,9 @@ common/views/components/profile-editor.vue:
danger-zone: "Zone de danger" danger-zone: "Zone de danger"
delete-account: "Supprimer le compte" delete-account: "Supprimer le compte"
account-deleted: "Le compte a été supprimé. Cela peut prendre un certain temps avant que toutes les données disparaissent." account-deleted: "Le compte a été supprimé. Cela peut prendre un certain temps avant que toutes les données disparaissent."
profile-metadata: "Métadonnées du profil"
metadata-label: "Étiquette"
metadata-content: "Contenu"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "Utilisateur·rice" users: "Utilisateur·rice"
rename: "Renommer la liste" rename: "Renommer la liste"
@@ -825,6 +856,7 @@ desktop:
uploading-avatar: "Téléversement du nouvel avatar" uploading-avatar: "Téléversement du nouvel avatar"
avatar-updated: "Mise à jour de lavatar avec succès" avatar-updated: "Mise à jour de lavatar avec succès"
choose-avatar: "Choisir un avatar" choose-avatar: "Choisir un avatar"
unable-to-process: "L'opération n'a pas pu être complétée"
invalid-filetype: "Ce format de fichier nest pas pris en charge" invalid-filetype: "Ce format de fichier nest pas pris en charge"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Noirs ... Total" total: "Noirs ... Total"
@@ -875,9 +907,12 @@ desktop/views/components/drive.file.vue:
copied: "Copié" copied: "Copié"
copied-url-to-clipboard: "L'URL a été copiée dans le presse-papier" copied-url-to-clipboard: "L'URL a été copiée dans le presse-papier"
desktop/views/components/drive.folder.vue: desktop/views/components/drive.folder.vue:
upload-folder: "Emplacement de téléversement par défaut"
unable-to-process: "L'opération n'a pas pu être complétée" unable-to-process: "L'opération n'a pas pu être complétée"
circular-reference-detected: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer." circular-reference-detected: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer."
unhandled-error: "Erreur inconnue" unhandled-error: "Erreur inconnue"
unable-to-delete: "Ne peut pas être supprimé"
has-child-files-or-folders: "Ce dossier n'est pas vide, il ne peut pas être supprimé"
contextmenu: contextmenu:
move-to-this-folder: "Déplacer dans ce dossier" move-to-this-folder: "Déplacer dans ce dossier"
show-in-new-window: "Ouvrir dans une nouvelle fenêtre" show-in-new-window: "Ouvrir dans une nouvelle fenêtre"
@@ -885,6 +920,7 @@ desktop/views/components/drive.folder.vue:
rename-folder: "Renommer le dossier" rename-folder: "Renommer le dossier"
input-new-folder-name: "Entrer un nouveau nom" input-new-folder-name: "Entrer un nouveau nom"
else-folders: "Avancé" else-folders: "Avancé"
set-as-upload-folder: "Spécifier en tant que dossier de téléversement par défaut"
desktop/views/components/drive.vue: desktop/views/components/drive.vue:
search: "Rechercher" search: "Rechercher"
empty-draghover: "Drop Welcome!" empty-draghover: "Drop Welcome!"
@@ -990,10 +1026,12 @@ desktop/views/components/settings.2fa.vue:
success: "Sauvegarde des paramètres avec succès !" success: "Sauvegarde des paramètres avec succès !"
failed: "Lopération a échoué. Veuillez vous assurer que le jeton a été saisi correctement." failed: "Lopération a échoué. Veuillez vous assurer que le jeton a été saisi correctement."
info: "À partir de maintenant, à chaque fois que vous vous connectez entrez votre mot de passe ainsi que le jeton généré sur votre appareil." info: "À partir de maintenant, à chaque fois que vous vous connectez entrez votre mot de passe ainsi que le jeton généré sur votre appareil."
totp-header: "Application d'authentification"
security-key-header: "Clé de sécurité" security-key-header: "Clé de sécurité"
last-used: "Dernière utilisation :" last-used: "Dernière utilisation :"
activate-key: "Cliquez pour activer la clé de sécurité" activate-key: "Cliquez pour activer la clé de sécurité"
security-key-name: "Nom de la clé" security-key-name: "Nom de la clé"
something-went-wrong: "Oula ! Il y a eu un problème lors de lenregistrement de la clé."
key-unregistered: "La clé a été supprimée" key-unregistered: "La clé a été supprimée"
use-password-less-login: "Utiliser une connexion sans mot de passe" use-password-less-login: "Utiliser une connexion sans mot de passe"
common/views/components/media-image.vue: common/views/components/media-image.vue:
@@ -1020,7 +1058,9 @@ common/views/components/drive-settings.vue:
max: "Maximale" max: "Maximale"
in-use: "utilisé" in-use: "utilisé"
stats: "Statistiques" stats: "Statistiques"
default-upload-folder: "Emplacement par défaut du dossier de transfert"
default-upload-folder-name: "Dossier·s" default-upload-folder-name: "Dossier·s"
change-default-upload-folder: "Changer de dossier"
common/views/components/mute-and-block.vue: common/views/components/mute-and-block.vue:
mute-and-block: "Silencés / Bloqués" mute-and-block: "Silencés / Bloqués"
mute: "Mettre en sourdine" mute: "Mettre en sourdine"
@@ -1127,11 +1167,13 @@ admin/views/queue.vue:
deliver: "Délivrées" deliver: "Délivrées"
inbox: "Reçues" inbox: "Reçues"
db: "Base de données" db: "Base de données"
objectStorage: "Stockage d'objets"
state: "État" state: "État"
states: states:
active: "en cours" active: "en cours"
delayed: "Programmé" delayed: "Programmé"
waiting: "En file d'attente" waiting: "En file d'attente"
result-is-truncated: "Le résultat est tronqué"
other-queues: "Autres files dattente" other-queues: "Autres files dattente"
admin/views/logs.vue: admin/views/logs.vue:
logs: "Journaux" logs: "Journaux"
@@ -1164,12 +1206,14 @@ admin/views/instance.vue:
languages-desc: "Vous pouvez en définir plus dune, séparées par des espaces." languages-desc: "Vous pouvez en définir plus dune, séparées par des espaces."
tos-url: "URL des conditions d'utilisation" tos-url: "URL des conditions d'utilisation"
repository-url: "URL du dépôt" repository-url: "URL du dépôt"
feedback-url: "URL pour les commentaires"
maintainer-config: "Informations de ladministrateur" maintainer-config: "Informations de ladministrateur"
maintainer-name: "Nom de ladministrateur" maintainer-name: "Nom de ladministrateur"
maintainer-email: "Contact administratif" maintainer-email: "Contact administratif"
advanced-config: "Autres réglages" advanced-config: "Autres réglages"
note-and-tl: "Notes et fils" note-and-tl: "Notes et fils"
drive-config: "Paramètres du lecteur" drive-config: "Paramètres du lecteur"
use-object-storage: "Utiliser le stockage d'objets"
object-storage-base-url: "URL" object-storage-base-url: "URL"
object-storage-prefix: "Préfixe" object-storage-prefix: "Préfixe"
object-storage-endpoint: "Point de terminaison" object-storage-endpoint: "Point de terminaison"
@@ -1311,6 +1355,8 @@ admin/views/users.vue:
remote-user-updated: "Les informations de lutilisateur·rice distant·e ont étés mis à jour" remote-user-updated: "Les informations de lutilisateur·rice distant·e ont étés mis à jour"
delete-all-files: "Supprimer tous les fichiers" delete-all-files: "Supprimer tous les fichiers"
delete-all-files-confirm: "Êtes vous surs de vouloir supprimer tous les fichiers ?" delete-all-files-confirm: "Êtes vous surs de vouloir supprimer tous les fichiers ?"
username: "Nom d'utilisateur·rice"
host: "Hôte"
users: users:
title: "Utilisateur·rice·s" title: "Utilisateur·rice·s"
sort: sort:
@@ -1341,6 +1387,12 @@ admin/views/moderators.vue:
added: "Ajouté en tant que modérateur" added: "Ajouté en tant que modérateur"
remove: "Révoquer" remove: "Révoquer"
removed: "Le modérateur a été révoqué" removed: "Le modérateur a été révoqué"
logs:
title: "Journaux"
moderator: "Modérateurs"
type: "Actions"
at: "Date de modification"
info: "Informations"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
title: "Ajouter un émoji" title: "Ajouter un émoji"
@@ -1617,6 +1669,8 @@ mobile/views/pages/search.vue:
not-found: "Aucune publication trouvée pour « {q} »." not-found: "Aucune publication trouvée pour « {q} »."
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "Choisissez un fichier" select-file: "Choisissez un fichier"
mobile/views/pages/notifications.vue:
notifications: "Notifications"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "Connecté·e en tant que {}" signed-in-as: "Connecté·e en tant que {}"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
@@ -1675,6 +1729,7 @@ deck/deck.user-column.vue:
activity: "Activité" activity: "Activité"
timeline: "Fil dactualité" timeline: "Fil dactualité"
pinned-notes: "Notes épinglées" pinned-notes: "Notes épinglées"
pinned-page: "Page épinglée"
docs: docs:
edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?" edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?"
edit-this-page-on-github-link: "Éditez cette page sur GitHub !" edit-this-page-on-github-link: "Éditez cette page sur GitHub !"
@@ -1790,6 +1845,11 @@ pages:
message: "Message à afficher lorsque appuyé" message: "Message à afficher lorsque appuyé"
variable: "Variable à envoyer" variable: "Variable à envoyer"
no-variable: "Aucune" no-variable: "Aucune"
radioButton: "Choix"
_radioButton:
name: "Nom de la variable"
title: "Titre"
default: "Valeur par défaut"
script: script:
categories: categories:
flow: "Contrôle" flow: "Contrôle"

View File

@@ -37,6 +37,18 @@ common:
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?" reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
fetching-as-ap-object: "連合に照会中" fetching-as-ap-object: "連合に照会中"
unfollow-confirm: "{name}さんをフォロー解除しますか?" unfollow-confirm: "{name}さんをフォロー解除しますか?"
signin-required: "ログインしてください"
notification-type: "通知の種類"
notification-types:
all: "すべて"
pollVote: "投票"
follow: "フォロー"
receiveFollowRequest: "フォローリクエスト"
reply: "返信"
quote: "引用"
renote: "Renote"
mention: "言及"
reaction: "リアクション"
got-it: "わかった" got-it: "わかった"
customization-tips: customization-tips:
@@ -214,6 +226,7 @@ common:
use-avatar-reversi-stones: "リバーシの石にアバターを使う" use-avatar-reversi-stones: "リバーシの石にアバターを使う"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない" disable-showing-animated-images: "アニメーション画像を再生しない"
enable-quick-notification-view: "通知のクイックビューを有効にする"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
@@ -290,6 +303,8 @@ common:
load-raw-images: "添付された画像を高画質で表示する" load-raw-images: "添付された画像を高画質で表示する"
load-remote-media: "リモートサーバーのメディアを表示する" load-remote-media: "リモートサーバーのメディアを表示する"
sync: "同期" sync: "同期"
save: "保存"
saved: "保存しました"
home-profile: "ホームのプロファイル" home-profile: "ホームのプロファイル"
deck-profile: "デッキのプロファイル" deck-profile: "デッキのプロファイル"
@@ -567,6 +582,8 @@ common/views/components/note-menu.vue:
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
delete-confirm: "この投稿を削除しますか?" delete-confirm: "この投稿を削除しますか?"
delete-and-edit: "削除して編集"
delete-and-edit-confirm: "この投稿を削除してもう一度編集しますか?この投稿へのリアクション、リノート、返信も全て削除されます。"
remote: "投稿元で見る" remote: "投稿元で見る"
pin-limit-exceeded: "これ以上ピン留めできません。" pin-limit-exceeded: "これ以上ピン留めできません。"
@@ -780,6 +797,9 @@ common/views/components/profile-editor.vue:
saved: "プロフィールを保存しました" saved: "プロフィールを保存しました"
uploading: "アップロード中" uploading: "アップロード中"
upload-failed: "アップロードに失敗しました" upload-failed: "アップロードに失敗しました"
unable-to-process: "操作を完了できません"
avatar-not-an-image: "アイコンとして指定したファイルは画像ではありません"
banner-not-an-image: "バナーとして指定したファイルは画像ではありません"
email: "メール設定" email: "メール設定"
email-address: "メールアドレス" email-address: "メールアドレス"
email-verified: "メールアドレスが確認されました" email-verified: "メールアドレスが確認されました"
@@ -799,6 +819,9 @@ common/views/components/profile-editor.vue:
danger-zone: "危険な設定" danger-zone: "危険な設定"
delete-account: "アカウントを削除" delete-account: "アカウントを削除"
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。" account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
profile-metadata: "プロフィール補足情報"
metadata-label: "ラベル"
metadata-content: "内容"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "ユーザー" users: "ユーザー"
@@ -925,6 +948,7 @@ desktop:
uploading-avatar: "新しいアバターをアップロードしています" uploading-avatar: "新しいアバターをアップロードしています"
avatar-updated: "アバターを更新しました" avatar-updated: "アバターを更新しました"
choose-avatar: "アバターにする画像を選択" choose-avatar: "アバターにする画像を選択"
unable-to-process: "操作を完了できません"
invalid-filetype: "この形式のファイルはサポートされていません" invalid-filetype: "この形式のファイルはサポートされていません"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
@@ -1496,6 +1520,8 @@ admin/views/users.vue:
remote-user-updated: "リモートユーザー情報を更新しました" remote-user-updated: "リモートユーザー情報を更新しました"
delete-all-files: "すべてのファイルを削除" delete-all-files: "すべてのファイルを削除"
delete-all-files-confirm: "すべてのファイルを削除しますか?" delete-all-files-confirm: "すべてのファイルを削除しますか?"
username: "ユーザー名"
host: "ホスト"
users: users:
title: "ユーザー" title: "ユーザー"
sort: sort:
@@ -1527,6 +1553,12 @@ admin/views/moderators.vue:
added: "モデレーターを登録しました" added: "モデレーターを登録しました"
remove: "解除" remove: "解除"
removed: "モデレーター登録を解除しました" removed: "モデレーター登録を解除しました"
logs:
title: "ログ"
moderator: "モデレーター"
type: "操作"
at: "日時"
info: "情報"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
@@ -1866,6 +1898,9 @@ mobile/views/pages/search.vue:
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/pages/notifications.vue:
notifications: "通知"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "{}としてサインイン中" signed-in-as: "{}としてサインイン中"
@@ -2066,6 +2101,13 @@ pages:
variable: "送信する変数" variable: "送信する変数"
no-variable: "なし" no-variable: "なし"
radioButton: "選択肢"
_radioButton:
name: "変数名"
title: "タイトル"
values: "改行で区切った選択肢"
default: "デフォルト値"
script: script:
categories: categories:
flow: "制御" flow: "制御"

View File

@@ -27,6 +27,12 @@ common:
load-more: "もっとあらへんのか!" load-more: "もっとあらへんのか!"
enter-password: "パスワードを入れてや" enter-password: "パスワードを入れてや"
2fa: "二段階認証" 2fa: "二段階認証"
notification-types:
all: "すべて"
follow: "フォロー"
reply: "返す"
renote: "Renote"
reaction: "リアクション"
got-it: "ほい" got-it: "ほい"
customization-tips: customization-tips:
title: "カスタマイズのヒント" title: "カスタマイズのヒント"
@@ -123,6 +129,8 @@ common:
password: "パスワード" password: "パスワード"
other: "その他" other: "その他"
timeline: "タイムライン" timeline: "タイムライン"
save: "保存"
saved: "保存したで!"
search: "検索" search: "検索"
delete: "削除" delete: "削除"
loading: "読み込み中" loading: "読み込み中"
@@ -470,6 +478,7 @@ common/views/components/profile-editor.vue:
saved: "プロフィールを保存したで" saved: "プロフィールを保存したで"
uploading: "アップロードしとります" uploading: "アップロードしとります"
upload-failed: "これアップロードでけへんわ" upload-failed: "これアップロードでけへんわ"
unable-to-process: "あかん、無理やわ"
email: "メール設定" email: "メール設定"
email-address: "メールアドレス" email-address: "メールアドレス"
email-verified: "このメールアドレスOKや" email-verified: "このメールアドレスOKや"
@@ -563,6 +572,7 @@ desktop:
uploading-avatar: "新しいアバターをアップロードしとるで" uploading-avatar: "新しいアバターをアップロードしとるで"
avatar-updated: "アバターを更新したで" avatar-updated: "アバターを更新したで"
choose-avatar: "アバターにする画像選んでや" choose-avatar: "アバターにする画像選んでや"
unable-to-process: "あかん、無理やわ"
invalid-filetype: "この形式のファイル無理やねん" invalid-filetype: "この形式のファイル無理やねん"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "黒いの ... 全部" total: "黒いの ... 全部"
@@ -942,6 +952,8 @@ admin/views/users.vue:
reset-password: "パスワードをリセット" reset-password: "パスワードをリセット"
password-updated: "パスワードは現在「{password} 」やで" password-updated: "パスワードは現在「{password} 」やで"
suspend: "凍結" suspend: "凍結"
username: "ユーザー名"
host: "ホスト"
users: users:
title: "ユーザー" title: "ユーザー"
state: state:
@@ -949,6 +961,11 @@ admin/views/users.vue:
moderator: "モデレーター" moderator: "モデレーター"
origin: origin:
local: "ローカル" local: "ローカル"
admin/views/moderators.vue:
logs:
moderator: "モデレーター"
type: "操作"
info: "情報"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
add: "増やす" add: "増やす"
@@ -1165,6 +1182,8 @@ mobile/views/pages/search.vue:
not-found: "ワイは「{q}」なんて投稿知らんわ、無いんちゃう?知らんけど。" not-found: "ワイは「{q}」なんて投稿知らんわ、無いんちゃう?知らんけど。"
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "ファイル選んでや" select-file: "ファイル選んでや"
mobile/views/pages/notifications.vue:
notifications: "通知"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "あんたは橋の下で拾った{}や!" signed-in-as: "あんたは橋の下で拾った{}や!"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:

View File

@@ -36,6 +36,18 @@ common:
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?" reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
fetching-as-ap-object: "연합에서 조회 중" fetching-as-ap-object: "연합에서 조회 중"
unfollow-confirm: "{name} 님을 팔로우 해제하시겠습니까?" unfollow-confirm: "{name} 님을 팔로우 해제하시겠습니까?"
signin-required: "로그인 해주세요"
notification-type: "알림의 종류"
notification-types:
all: "모두"
pollVote: "투표"
follow: "팔로잉"
receiveFollowRequest: "팔로우 요청"
reply: "답글 달기"
quote: "인용"
renote: "리노트"
mention: "멘션"
reaction: "리액션"
got-it: "알겠습니다" got-it: "알겠습니다"
customization-tips: customization-tips:
title: "커스터마이징 도움말" title: "커스터마이징 도움말"
@@ -88,7 +100,7 @@ common:
"read:mutes": "뮤트 보기" "read:mutes": "뮤트 보기"
"write:mutes": "뮤트 수정" "write:mutes": "뮤트 수정"
"write:notes": "글 작성, 삭제" "write:notes": "글 작성, 삭제"
"read:notifications": " 보기" "read:notifications": "알림 보기"
"write:notifications": "알림 수정" "write:notifications": "알림 수정"
"read:reactions": "리액션 보기" "read:reactions": "리액션 보기"
"write:reactions": "리액션 수정" "write:reactions": "리액션 수정"
@@ -202,6 +214,7 @@ common:
use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용" use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용"
disable-animated-mfm: "글의 문자 애니메이션을 비활성화" disable-animated-mfm: "글의 문자 애니메이션을 비활성화"
disable-showing-animated-images: "움직이는 이미지를 자동으로 재생하지 않음" disable-showing-animated-images: "움직이는 이미지를 자동으로 재생하지 않음"
enable-quick-notification-view: "알림의 빠른 보기를 사용합니다"
suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시" suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시"
always-show-nsfw: "항상 열람주의 미디어를 표시" always-show-nsfw: "항상 열람주의 미디어를 표시"
always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시" always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
@@ -278,6 +291,8 @@ common:
load-raw-images: "첨부 이미지를 고품질로 표시" load-raw-images: "첨부 이미지를 고품질로 표시"
load-remote-media: "원격 서버의 미디어를 표시" load-remote-media: "원격 서버의 미디어를 표시"
sync: "동기화" sync: "동기화"
save: "저장"
saved: "저장하였습니다"
home-profile: "홈 프로필" home-profile: "홈 프로필"
deck-profile: "덱 프로필" deck-profile: "덱 프로필"
search: "검색" search: "검색"
@@ -529,6 +544,8 @@ common/views/components/note-menu.vue:
unpin: "프로필에서 고정 해제" unpin: "프로필에서 고정 해제"
delete: "삭제" delete: "삭제"
delete-confirm: "이 글을 삭제하시겠습니까?" delete-confirm: "이 글을 삭제하시겠습니까?"
delete-and-edit: "삭제 후 편집"
delete-and-edit-confirm: "이 글을 삭제한 뒤 다시 편집하시겠습니까? 이 글에 대한 리액션, 리노트, 답글 또한 모두 삭제됩니다."
remote: "글 원본 보기" remote: "글 원본 보기"
pin-limit-exceeded: "더 이상 고정할 수 없습니다." pin-limit-exceeded: "더 이상 고정할 수 없습니다."
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
@@ -723,6 +740,9 @@ common/views/components/profile-editor.vue:
saved: "프로필을 저장하였습니다" saved: "프로필을 저장하였습니다"
uploading: "업로드 중" uploading: "업로드 중"
upload-failed: "업로드에 실패하였습니다" upload-failed: "업로드에 실패하였습니다"
unable-to-process: "작업을 완료할 수 없습니다"
avatar-not-an-image: "아바타로 지정한 파일이 이미지 형식이 아닙니다"
banner-not-an-image: "배너로 지정한 파일이 이미지 형식이 아닙니다"
email: "메일 설정" email: "메일 설정"
email-address: "메일 주소" email-address: "메일 주소"
email-verified: "매일 주소가 확인되었습니다" email-verified: "매일 주소가 확인되었습니다"
@@ -742,6 +762,9 @@ common/views/components/profile-editor.vue:
danger-zone: "위험한 설정" danger-zone: "위험한 설정"
delete-account: "계정 삭제" delete-account: "계정 삭제"
account-deleted: "계정이 삭제되었습니다. 데이터가 사라질 때까지 시간이 걸릴 수 있습니다." account-deleted: "계정이 삭제되었습니다. 데이터가 사라질 때까지 시간이 걸릴 수 있습니다."
profile-metadata: "프로필 추가 정보"
metadata-label: "라벨"
metadata-content: "내용"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "사용자" users: "사용자"
rename: "리스트 이름 바꾸기" rename: "리스트 이름 바꾸기"
@@ -851,6 +874,7 @@ desktop:
uploading-avatar: "새로운 아바타를 업로드하고 있습니다" uploading-avatar: "새로운 아바타를 업로드하고 있습니다"
avatar-updated: "아바타가 변경되었습니다" avatar-updated: "아바타가 변경되었습니다"
choose-avatar: "아바타 이미지를 선택" choose-avatar: "아바타 이미지를 선택"
unable-to-process: "작업을 완료할 수 없습니다"
invalid-filetype: "이 형식의 파일은 지원되지 않습니다" invalid-filetype: "이 형식의 파일은 지원되지 않습니다"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "검은색 ... 전체" total: "검은색 ... 전체"
@@ -892,7 +916,7 @@ desktop/views/components/drive.file.vue:
copy-url: "URL 복사" copy-url: "URL 복사"
download: "다운로드" download: "다운로드"
else-files: "기타" else-files: "기타"
set-as-avatar: "아이콘으로 설정" set-as-avatar: "아바타로 설정"
set-as-banner: "배너로 설정" set-as-banner: "배너로 설정"
open-in-app: "앱에서 열기" open-in-app: "앱에서 열기"
add-app: "앱 추가" add-app: "앱 추가"
@@ -1363,6 +1387,8 @@ admin/views/users.vue:
remote-user-updated: "원격 사용자 정보를 갱신하였습니다" remote-user-updated: "원격 사용자 정보를 갱신하였습니다"
delete-all-files: "모든 파일 삭제" delete-all-files: "모든 파일 삭제"
delete-all-files-confirm: "모든 파일을 삭제하시겠습니까?" delete-all-files-confirm: "모든 파일을 삭제하시겠습니까?"
username: "사용자명"
host: "관리자"
users: users:
title: "사용자" title: "사용자"
sort: sort:
@@ -1393,6 +1419,12 @@ admin/views/moderators.vue:
added: "모더레이터를 등록하였습니다" added: "모더레이터를 등록하였습니다"
remove: "해제" remove: "해제"
removed: "모더레이터 등록을 해제했습니다" removed: "모더레이터 등록을 해제했습니다"
logs:
title: "로그"
moderator: "모더레이터"
type: "작업"
at: "일시"
info: "정보"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
title: "이모지 등록" title: "이모지 등록"
@@ -1681,6 +1713,8 @@ mobile/views/pages/search.vue:
not-found: "\"{q}\" 와 일치하는 글을 찾을 수 없습니다." not-found: "\"{q}\" 와 일치하는 글을 찾을 수 없습니다."
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "파일 선택" select-file: "파일 선택"
mobile/views/pages/notifications.vue:
notifications: "알림"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "{}(으)로 로그인" signed-in-as: "{}(으)로 로그인"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
@@ -1861,6 +1895,12 @@ pages:
message: "눌렀을 때 표시할 메시지" message: "눌렀을 때 표시할 메시지"
variable: "보낼 변수" variable: "보낼 변수"
no-variable: "없음" no-variable: "없음"
radioButton: "선택지"
_radioButton:
name: "변수명"
title: "제목"
values: "줄바꿈으로 구분된 선택지"
default: "기본값"
script: script:
categories: categories:
flow: "흐름 제어" flow: "흐름 제어"

View File

@@ -8,6 +8,11 @@ common:
reaction: "Reactie" reaction: "Reactie"
close: "Sluiten" close: "Sluiten"
enter-password: "Voer het wachtwoord in" enter-password: "Voer het wachtwoord in"
notification-types:
all: "Alle"
follow: "Volgend"
reply: "Beantwoorden"
reaction: "Reactie"
time: time:
unknown: "onbekend" unknown: "onbekend"
future: "toekomstig" future: "toekomstig"
@@ -199,6 +204,7 @@ common/views/components/profile-editor.vue:
name: "Naam" name: "Naam"
avatar: "Gebruikersafbeelding" avatar: "Gebruikersafbeelding"
banner: "Omslagfoto" banner: "Omslagfoto"
unable-to-process: "De operatie kan niet worden voltooid."
export-targets: export-targets:
following-list: "Volgend" following-list: "Volgend"
user-lists: "Lijsten" user-lists: "Lijsten"
@@ -226,6 +232,7 @@ common/views/pages/follow.vue:
follow: "Volgend" follow: "Volgend"
desktop: desktop:
banner: "Omslagfoto" banner: "Omslagfoto"
unable-to-process: "De operatie kan niet worden voltooid."
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Zwart ... totaal" total: "Zwart ... totaal"
notes: "Blauw ... notities" notes: "Blauw ... notities"
@@ -425,6 +432,7 @@ admin/views/drive.vue:
local: "Lokaal" local: "Lokaal"
delete: "Verwijderen" delete: "Verwijderen"
admin/views/users.vue: admin/views/users.vue:
username: "Gebruikersnaam"
users: users:
title: "Gebruiker" title: "Gebruiker"
state: state:
@@ -566,6 +574,8 @@ mobile/views/pages/search.vue:
search: "Zoeken" search: "Zoeken"
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "Kies een bestand" select-file: "Kies een bestand"
mobile/views/pages/notifications.vue:
notifications: "Meldingen"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "Ingelogd als {}" signed-in-as: "Ingelogd als {}"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:

View File

@@ -11,6 +11,10 @@ common:
rich-contents: "Innlegg" rich-contents: "Innlegg"
drive: "Disk" drive: "Disk"
close: "Lukk" close: "Lukk"
notification-types:
all: "Alle"
follow: "Følger"
reply: "Svar"
got-it: "Skjønner!" got-it: "Skjønner!"
notification: notification:
file-uploaded: "Filen ble lastet opp!" file-uploaded: "Filen ble lastet opp!"
@@ -78,6 +82,7 @@ common:
_settings: _settings:
notification: "Notifikasjon" notification: "Notifikasjon"
password: "Passord" password: "Passord"
save: "Lagre"
search: "Søk" search: "Søk"
delete: "Slett" delete: "Slett"
loading: "Laster inn..." loading: "Laster inn..."
@@ -345,12 +350,16 @@ admin/views/drive.vue:
local: "Lokalt" local: "Lokalt"
delete: "Slett" delete: "Slett"
admin/views/users.vue: admin/views/users.vue:
username: "Brukernavn"
users: users:
title: "Bruker" title: "Bruker"
state: state:
all: "Alle" all: "Alle"
origin: origin:
local: "Lokalt" local: "Lokalt"
admin/views/moderators.vue:
logs:
info: "Informasjon"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
add: "Legg til" add: "Legg til"
@@ -462,6 +471,8 @@ mobile/views/pages/games/reversi.vue:
reversi: "Reversi" reversi: "Reversi"
mobile/views/pages/search.vue: mobile/views/pages/search.vue:
search: "Søk" search: "Søk"
mobile/views/pages/notifications.vue:
notifications: "Notifikasjon"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
following: "Følger" following: "Følger"
followers: "Følgere" followers: "Følgere"

View File

@@ -28,6 +28,12 @@ common:
signin: "Zaloguj się" signin: "Zaloguj się"
signup: "Rejestracja" signup: "Rejestracja"
signout: "Wyloguj się" signout: "Wyloguj się"
notification-types:
all: "Wszyscy"
follow: "Śledzeni"
reply: "Odpowiedz"
renote: "Udostępnij"
reaction: "Reakcja"
got-it: "Rozumiem!" got-it: "Rozumiem!"
customization-tips: customization-tips:
title: "Wskazówki o dostosowywaniu" title: "Wskazówki o dostosowywaniu"
@@ -178,6 +184,8 @@ common:
version: "Wersja:" version: "Wersja:"
do-update: "Sprawdź dostępność nowych aktualizacji" do-update: "Sprawdź dostępność nowych aktualizacji"
navbar-position-left: "Z lewej" navbar-position-left: "Z lewej"
save: "Zapisz"
saved: "Zapisano"
search: "Szukaj" search: "Szukaj"
delete: "Usuń" delete: "Usuń"
loading: "Ładowanie" loading: "Ładowanie"
@@ -516,6 +524,7 @@ common/views/components/profile-editor.vue:
saved: "Pomyślnie zaktualizowano profil" saved: "Pomyślnie zaktualizowano profil"
uploading: "Wysyłanie" uploading: "Wysyłanie"
upload-failed: "Wysyłanie nie powiodło się" upload-failed: "Wysyłanie nie powiodło się"
unable-to-process: "Nie udało się ukończyć działania."
email: "Ustawienia e-mail" email: "Ustawienia e-mail"
email-address: "Adres e-mail" email-address: "Adres e-mail"
email-verified: "Twój adres e-mail został zweryfikowany." email-verified: "Twój adres e-mail został zweryfikowany."
@@ -610,6 +619,7 @@ desktop:
uploading-avatar: "Wysyłanie awatara" uploading-avatar: "Wysyłanie awatara"
avatar-updated: "Wysłano awatar" avatar-updated: "Wysłano awatar"
choose-avatar: "Wybierz awatar" choose-avatar: "Wybierz awatar"
unable-to-process: "Nie udało się ukończyć działania."
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "Czarny … Łącznie" total: "Czarny … Łącznie"
notes: "Niebieski … Wpisy" notes: "Niebieski … Wpisy"
@@ -908,6 +918,7 @@ admin/views/drive.vue:
unmark-as-sensitive: "Cofnij oznaczenie jako zawartość wrażliwą" unmark-as-sensitive: "Cofnij oznaczenie jako zawartość wrażliwą"
admin/views/users.vue: admin/views/users.vue:
user-not-found: "Nie znaleziono użytkownika" user-not-found: "Nie znaleziono użytkownika"
username: "Nazwa użytkownika"
users: users:
title: "Użytkownicy" title: "Użytkownicy"
sort: sort:
@@ -923,6 +934,9 @@ admin/views/users.vue:
admin/views/moderators.vue: admin/views/moderators.vue:
add-moderator: add-moderator:
add: "Zarejestruj się" add: "Zarejestruj się"
logs:
moderator: "Moderatorzy"
info: "Informacje"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
name: "Nazwa Emoji" name: "Nazwa Emoji"
@@ -1129,6 +1143,8 @@ mobile/views/pages/search.vue:
search: "Szukaj" search: "Szukaj"
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "Wybierz plik" select-file: "Wybierz plik"
mobile/views/pages/notifications.vue:
notifications: "Powiadomienia"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "Zalogowany jako {}" signed-in-as: "Zalogowany jako {}"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
@@ -1212,6 +1228,8 @@ pages:
text: "Tytuł" text: "Tytuł"
_button: _button:
text: "Tytuł" text: "Tytuł"
_radioButton:
title: "Tytuł"
script: script:
categories: categories:
random: "Losowy" random: "Losowy"

View File

@@ -18,6 +18,8 @@ common:
application-authorization: "Aplicativos autorizados" application-authorization: "Aplicativos autorizados"
close: "Fechar" close: "Fechar"
do-not-copy-paste: "Por favor, não digite ou copie o código aqui. A conta pode ser comprometida." do-not-copy-paste: "Por favor, não digite ou copie o código aqui. A conta pode ser comprometida."
notification-types:
follow: "Seguindo"
got-it: "Entendi!" got-it: "Entendi!"
customization-tips: customization-tips:
title: "Dicas de personalização" title: "Dicas de personalização"

View File

@@ -35,6 +35,19 @@ common:
signout: "退出" signout: "退出"
reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?" reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?"
fetching-as-ap-object: "联合查询" fetching-as-ap-object: "联合查询"
unfollow-confirm: "取消对{name}的关注?"
signin-required: "请先登录"
notification-type: "通知类型"
notification-types:
all: "所有"
pollVote: "投票"
follow: "关注中"
receiveFollowRequest: "关注请求"
reply: "回复"
quote: "引用"
renote: "转推"
mention: "提及"
reaction: "回应"
got-it: "知道了" got-it: "知道了"
customization-tips: customization-tips:
title: "自定义提示" title: "自定义提示"
@@ -122,6 +135,7 @@ common:
add-visible-user: "添加用户" add-visible-user: "添加用户"
cw-placeholder: "评论帖子(可选)" cw-placeholder: "评论帖子(可选)"
username-prompt: "输入用户名" username-prompt: "输入用户名"
enter-file-name: "编辑文件名"
weekday-short: weekday-short:
sunday: "日" sunday: "日"
monday: "一" monday: "一"
@@ -188,6 +202,11 @@ common:
remember-note-visibility: "记住帖子可见性" remember-note-visibility: "记住帖子可见性"
web-search-engine: "搜索引擎" web-search-engine: "搜索引擎"
web-search-engine-desc: "例如: https://www.google.com/?#q={{query}}" web-search-engine-desc: "例如: https://www.google.com/?#q={{query}}"
paste: "粘贴"
pasted-file-name: "粘贴的文件名模板"
pasted-file-name-desc: "例: \"yyyy-MM-dd HH-mm-ss [{{number}}]\" → \"2018-03-20 21-30-24 1\""
paste-dialog: "粘贴时编辑文件名"
paste-dialog-desc: "粘贴时显示编辑文件名的对话框"
keep-cw: "保留内容警告" keep-cw: "保留内容警告"
keep-cw-desc: "在回复帖子时,如果原帖设置了内容警告,默认情况下回帖也会设置相同的内容警告。" keep-cw-desc: "在回复帖子时,如果原帖设置了内容警告,默认情况下回帖也会设置相同的内容警告。"
i-like-sushi: "相比于布丁来说, 我更喜欢寿司。" i-like-sushi: "相比于布丁来说, 我更喜欢寿司。"
@@ -195,6 +214,7 @@ common:
use-avatar-reversi-stones: "用头像作为黑白棋的棋子" use-avatar-reversi-stones: "用头像作为黑白棋的棋子"
disable-animated-mfm: "在帖子中禁用动画文本" disable-animated-mfm: "在帖子中禁用动画文本"
disable-showing-animated-images: "不播放动画" disable-showing-animated-images: "不播放动画"
enable-quick-notification-view: "启用通知快速查看"
suggest-recent-hashtags: "在帖子表单上显示最近流行的哈希标签" suggest-recent-hashtags: "在帖子表单上显示最近流行的哈希标签"
always-show-nsfw: "总是显示 NSFW 的内容" always-show-nsfw: "总是显示 NSFW 的内容"
always-mark-nsfw: "总是用 NSFW 来标记附件" always-mark-nsfw: "总是用 NSFW 来标记附件"
@@ -271,6 +291,8 @@ common:
load-raw-images: "以原始质量显示附加图像" load-raw-images: "以原始质量显示附加图像"
load-remote-media: "显示来自远程服务器的媒体" load-remote-media: "显示来自远程服务器的媒体"
sync: "同步" sync: "同步"
save: "保存"
saved: "已保存"
home-profile: "定制首页数据" home-profile: "定制首页数据"
deck-profile: "定制Deck数据" deck-profile: "定制Deck数据"
search: "搜索" search: "搜索"
@@ -522,7 +544,10 @@ common/views/components/note-menu.vue:
unpin: "取消置顶" unpin: "取消置顶"
delete: "删除" delete: "删除"
delete-confirm: "确定删除这个投稿吗?" delete-confirm: "确定删除这个投稿吗?"
delete-and-edit: "删除和编辑"
delete-and-edit-confirm: "要删除此帖并再次编辑吗?对此帖的所有回应,转推和回复也将被删除。"
remote: "显示原始投稿" remote: "显示原始投稿"
pin-limit-exceeded: "无法置顶更多了。"
common/views/components/user-menu.vue: common/views/components/user-menu.vue:
mention: "提到" mention: "提到"
mute: "屏蔽" mute: "屏蔽"
@@ -592,6 +617,12 @@ common/views/components/emoji-picker.vue:
symbols: "符号" symbols: "符号"
flags: "旗帜" flags: "旗帜"
common/views/components/settings/app-type.vue: common/views/components/settings/app-type.vue:
title: "模式"
intro: "您可以指定使用桌面版或移动版。"
choices:
auto: "自动选择"
desktop: "固定为桌面版"
mobile: "固定为移动版"
info: "更改将在刷新页面后生效。" info: "更改将在刷新页面后生效。"
common/views/components/signin.vue: common/views/components/signin.vue:
username: "用户名" username: "用户名"
@@ -603,6 +634,8 @@ common/views/components/signin.vue:
signin-with-github: "用 GitHub 登录" signin-with-github: "用 GitHub 登录"
signin-with-discord: "用 Discord 登录" signin-with-discord: "用 Discord 登录"
login-failed: "登录失败。请检查用户名和密码。" login-failed: "登录失败。请检查用户名和密码。"
tap-key: "点击安全密钥登录"
enter-2fa-code: "输入验证码"
common/views/components/signup.vue: common/views/components/signup.vue:
invitation-code: "邀请码" invitation-code: "邀请码"
invitation-info: "如果您没有邀请码,请联系<a href=\"{}\">管理员</a>。" invitation-info: "如果您没有邀请码,请联系<a href=\"{}\">管理员</a>。"
@@ -707,6 +740,9 @@ common/views/components/profile-editor.vue:
saved: "您的个人资料已保存" saved: "您的个人资料已保存"
uploading: "正在上传" uploading: "正在上传"
upload-failed: "上传失败" upload-failed: "上传失败"
unable-to-process: "无法完成操作"
avatar-not-an-image: "选择的头像文件不是图片格式"
banner-not-an-image: "选择的横幅背景不是图片格式"
email: "邮件设置" email: "邮件设置"
email-address: "电子邮件地址" email-address: "电子邮件地址"
email-verified: "电子邮件地址已验证" email-verified: "电子邮件地址已验证"
@@ -726,6 +762,9 @@ common/views/components/profile-editor.vue:
danger-zone: "危险选项" danger-zone: "危险选项"
delete-account: "删除帐户" delete-account: "删除帐户"
account-deleted: "帐户已被删除。 数据会在一段时间之后清除。" account-deleted: "帐户已被删除。 数据会在一段时间之后清除。"
profile-metadata: "个人资料补充信息"
metadata-label: "标签"
metadata-content: "内容"
common/views/components/user-list-editor.vue: common/views/components/user-list-editor.vue:
users: "用户" users: "用户"
rename: "重命名列表" rename: "重命名列表"
@@ -835,6 +874,7 @@ desktop:
uploading-avatar: "上传一个新的头像" uploading-avatar: "上传一个新的头像"
avatar-updated: "成功上传头像" avatar-updated: "成功上传头像"
choose-avatar: "选择作为头像的图片" choose-avatar: "选择作为头像的图片"
unable-to-process: "无法完成操作"
invalid-filetype: "不接受此文件类型" invalid-filetype: "不接受此文件类型"
desktop/views/components/activity.chart.vue: desktop/views/components/activity.chart.vue:
total: "黑 ... 总计" total: "黑 ... 总计"
@@ -885,9 +925,12 @@ desktop/views/components/drive.file.vue:
copied: "已复制" copied: "已复制"
copied-url-to-clipboard: "已复制链接到剪贴板" copied-url-to-clipboard: "已复制链接到剪贴板"
desktop/views/components/drive.folder.vue: desktop/views/components/drive.folder.vue:
upload-folder: "默认上传文件夹"
unable-to-process: "无法完成操作" unable-to-process: "无法完成操作"
circular-reference-detected: "目标文件夹是您要移动的文件夹的子文件夹。" circular-reference-detected: "目标文件夹是您要移动的文件夹的子文件夹。"
unhandled-error: "未知错误" unhandled-error: "未知错误"
unable-to-delete: "无法删除"
has-child-files-or-folders: "此文件夹不为空,无法删除。"
contextmenu: contextmenu:
move-to-this-folder: "移动到此文件夹" move-to-this-folder: "移动到此文件夹"
show-in-new-window: "在新窗口打开" show-in-new-window: "在新窗口打开"
@@ -895,6 +938,7 @@ desktop/views/components/drive.folder.vue:
rename-folder: "重命名文件夹" rename-folder: "重命名文件夹"
input-new-folder-name: "请输入新文件名" input-new-folder-name: "请输入新文件名"
else-folders: "其他" else-folders: "其他"
set-as-upload-folder: "设置为默认上传文件夹"
desktop/views/components/drive.vue: desktop/views/components/drive.vue:
search: "搜索" search: "搜索"
empty-draghover: "放在这里!因为你知道我很可爱,对吗?" empty-draghover: "放在这里!因为你知道我很可爱,对吗?"
@@ -1000,6 +1044,16 @@ desktop/views/components/settings.2fa.vue:
success: "设置完成" success: "设置完成"
failed: "设置失败, 请确保您的密钥是正确的。" failed: "设置失败, 请确保您的密钥是正确的。"
info: "从下次登录Misskey时您的设备上显示的令牌以及密码也是必需的。" info: "从下次登录Misskey时您的设备上显示的令牌以及密码也是必需的。"
totp-header: "身份验证 App"
security-key-header: "安全密钥"
security-key: "为了增强安全性您可以使用支持FIDO2的硬件安全密钥登录您的帐户。 登录时,您将需要注册安全密钥或身份验证应用。"
last-used: "最后使用:"
activate-key: "单击以激活您的安全密钥"
security-key-name: "密钥名称"
register-security-key: "安全密钥注册完成"
something-went-wrong: "糟糕!安全密钥注册出现问题:"
key-unregistered: "安全密钥已被删除。"
use-password-less-login: "使用免密码登录"
common/views/components/media-image.vue: common/views/components/media-image.vue:
sensitive: "阅读注意" sensitive: "阅读注意"
click-to-show: "点击查看" click-to-show: "点击查看"
@@ -1024,7 +1078,9 @@ common/views/components/drive-settings.vue:
max: "容量" max: "容量"
in-use: "已使用" in-use: "已使用"
stats: "统计" stats: "统计"
default-upload-folder: "默认上传文件夹"
default-upload-folder-name: "文件夹" default-upload-folder-name: "文件夹"
change-default-upload-folder: "更改文件夹"
common/views/components/mute-and-block.vue: common/views/components/mute-and-block.vue:
mute-and-block: "屏蔽/拉黑" mute-and-block: "屏蔽/拉黑"
mute: "屏蔽" mute: "屏蔽"
@@ -1114,6 +1170,9 @@ admin/views/index.vue:
back-to-misskey: "返回 Misskey" back-to-misskey: "返回 Misskey"
admin/views/db.vue: admin/views/db.vue:
tables: "表格" tables: "表格"
vacuum: "VACUUM"
vacuum-info: "清理数据库。 保持数据完整并减少磁盘使用量。 此操作通常会自动定期执行。"
vacuum-exclamation: "运行VACUUM之后数据库上的负载可能会持续一段时间并且可能不响应用户操作。"
admin/views/dashboard.vue: admin/views/dashboard.vue:
dashboard: "Dashboard" dashboard: "Dashboard"
accounts: "账户" accounts: "账户"
@@ -1328,6 +1387,8 @@ admin/views/users.vue:
remote-user-updated: "远程用户信息已更新" remote-user-updated: "远程用户信息已更新"
delete-all-files: "删除所有文件" delete-all-files: "删除所有文件"
delete-all-files-confirm: "删除所有文件吗?" delete-all-files-confirm: "删除所有文件吗?"
username: "用户名"
host: "主机名"
users: users:
title: "用户" title: "用户"
sort: sort:
@@ -1358,6 +1419,12 @@ admin/views/moderators.vue:
added: "已注册版主。" added: "已注册版主。"
remove: "取消" remove: "取消"
removed: "取消注册版主" removed: "取消注册版主"
logs:
title: "登录"
moderator: "版主"
type: "操作"
at: "日期和时间"
info: "信息"
admin/views/emoji.vue: admin/views/emoji.vue:
add-emoji: add-emoji:
title: "添加emoji" title: "添加emoji"
@@ -1646,6 +1713,8 @@ mobile/views/pages/search.vue:
not-found: "没有找到有关于“{q}”的帖子" not-found: "没有找到有关于“{q}”的帖子"
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "选择文件" select-file: "选择文件"
mobile/views/pages/notifications.vue:
notifications: "通知"
mobile/views/pages/settings.vue: mobile/views/pages/settings.vue:
signed-in-as: "以{}登录" signed-in-as: "以{}登录"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
@@ -1704,6 +1773,7 @@ deck/deck.user-column.vue:
activity: "活动" activity: "活动"
timeline: "时间线" timeline: "时间线"
pinned-notes: "置顶帖" pinned-notes: "置顶帖"
pinned-page: "已置顶的页面"
docs: docs:
edit-this-page-on-github: "发现错误或想要为文档做出贡献?" edit-this-page-on-github: "发现错误或想要为文档做出贡献?"
edit-this-page-on-github-link: "在GitHub上编辑这个页面。" edit-this-page-on-github-link: "在GitHub上编辑这个页面。"
@@ -1758,6 +1828,7 @@ pages:
url: "页面URL" url: "页面URL"
summary: "页面摘要" summary: "页面摘要"
align-center: "居中" align-center: "居中"
hide-title-when-pinned: "置顶时隐藏标题"
font: "字体" font: "字体"
fontSerif: "衬线字体" fontSerif: "衬线字体"
fontSansSerif: "无衬线字体" fontSansSerif: "无衬线字体"
@@ -1811,12 +1882,25 @@ pages:
inc: "增加值" inc: "增加值"
_button: _button:
text: "标题" text: "标题"
colored: "彩色"
action: "按下按钮时的行为" action: "按下按钮时的行为"
_action: _action:
dialog: "显示对话框" dialog: "显示对话框"
_dialog: _dialog:
content: "内容" content: "内容"
resetRandom: "随机值重置" resetRandom: "随机值重置"
pushEvent: "发送事件"
_pushEvent:
event: "事件名称"
message: "按下时显示的消息"
variable: "发送的变量"
no-variable: "空"
radioButton: "选择项"
_radioButton:
name: "变量名"
title: "标题"
values: "使用换行区分的选择项"
default: "默认值"
script: script:
categories: categories:
flow: "控制" flow: "控制"

View File

@@ -0,0 +1,17 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class ModerationLog1562869971568 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "moderation_log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "type" character varying(128) NOT NULL, "info" jsonb NOT NULL, CONSTRAINT "PK_d0adca6ecfd068db83e4526cc26" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_a08ad074601d204e0f69da9a95" ON "moderation_log" ("userId") `);
await queryRunner.query(`ALTER TABLE "moderation_log" ADD CONSTRAINT "FK_a08ad074601d204e0f69da9a954" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "moderation_log" DROP CONSTRAINT "FK_a08ad074601d204e0f69da9a954"`);
await queryRunner.query(`DROP INDEX "IDX_a08ad074601d204e0f69da9a95"`);
await queryRunner.query(`DROP TABLE "moderation_log"`);
}
}

View File

@@ -0,0 +1,13 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class UsedUsername1563757595828 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "used_username" ("username" character varying(128) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_78fd79d2d24c6ac2f4cc9a31a5d" PRIMARY KEY ("username"))`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP TABLE "used_username"`);
}
}

View File

@@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "11.25.1", "version": "11.27.1",
"codename": "daybreak", "codename": "daybreak",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -27,7 +27,7 @@
}, },
"resolutions": { "resolutions": {
"gulp-cssnano/cssnano/postcss-svgo/svgo/js-yaml": "^3.13.1", "gulp-cssnano/cssnano/postcss-svgo/svgo/js-yaml": "^3.13.1",
"video-thumbnail-generator/lodash": "^4.17.11" "lodash": "^4.17.13"
}, },
"dependencies": { "dependencies": {
"@elastic/elasticsearch": "7.1.0", "@elastic/elasticsearch": "7.1.0",
@@ -37,7 +37,6 @@
"@fortawesome/free-solid-svg-icons": "5.9.0", "@fortawesome/free-solid-svg-icons": "5.9.0",
"@fortawesome/vue-fontawesome": "0.1.6", "@fortawesome/vue-fontawesome": "0.1.6",
"@koa/cors": "3.0.0", "@koa/cors": "3.0.0",
"@typescript-eslint/parser": "1.11.0",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.5.15", "@types/bull": "3.5.15",
"@types/cbor": "2.0.0", "@types/cbor": "2.0.0",
@@ -50,7 +49,6 @@
"@types/gulp-replace": "0.0.31", "@types/gulp-replace": "0.0.31",
"@types/gulp-uglify": "3.0.6", "@types/gulp-uglify": "3.0.6",
"@types/gulp-util": "3.0.34", "@types/gulp-util": "3.0.34",
"@types/is-root": "2.1.2",
"@types/is-url": "1.2.28", "@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.1", "@types/js-yaml": "3.12.1",
"@types/jsdom": "12.2.4", "@types/jsdom": "12.2.4",
@@ -68,7 +66,6 @@
"@types/koa-views": "2.0.3", "@types/koa-views": "2.0.3",
"@types/koa__cors": "2.2.3", "@types/koa__cors": "2.2.3",
"@types/lolex": "3.1.1", "@types/lolex": "3.1.1",
"@types/minio": "7.0.2",
"@types/mocha": "5.2.7", "@types/mocha": "5.2.7",
"@types/node": "12.0.10", "@types/node": "12.0.10",
"@types/nodemailer": "6.2.0", "@types/nodemailer": "6.2.0",
@@ -83,7 +80,7 @@
"@types/ratelimiter": "2.1.28", "@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.13", "@types/redis": "2.8.13",
"@types/rename": "1.0.1", "@types/rename": "1.0.1",
"@types/request": "2.48.1", "@types/request": "2.48.2",
"@types/request-promise-native": "1.0.16", "@types/request-promise-native": "1.0.16",
"@types/request-stats": "3.0.0", "@types/request-stats": "3.0.0",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
@@ -100,11 +97,14 @@
"@types/webpack-stream": "3.2.10", "@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
"@typescript-eslint/parser": "1.11.0",
"agentkeepalive": "4.0.2",
"animejs": "3.0.1", "animejs": "3.0.1",
"apexcharts": "3.8.1", "apexcharts": "3.8.3",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "4.0.2", "autosize": "4.0.2",
"autwh": "0.1.0", "autwh": "0.1.0",
"aws-sdk": "2.500.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"bootstrap": "4.3.1", "bootstrap": "4.3.1",
"bootstrap-vue": "2.0.0-rc.13", "bootstrap-vue": "2.0.0-rc.13",
@@ -117,18 +117,19 @@
"commander": "2.20.0", "commander": "2.20.0",
"content-disposition": "0.5.3", "content-disposition": "0.5.3",
"crc-32": "1.2.0", "crc-32": "1.2.0",
"css-loader": "3.0.0", "css-loader": "3.1.0",
"cssnano": "4.1.10", "cssnano": "4.1.10",
"dateformat": "3.0.3", "dateformat": "3.0.3",
"deep-equal": "1.0.1", "deep-equal": "1.0.1",
"diskusage": "1.1.2", "diskusage": "1.1.3",
"double-ended-queue": "2.1.0-0", "double-ended-queue": "2.1.0-0",
"emojilib": "2.4.0", "emojilib": "2.4.0",
"eslint": "6.0.1", "eslint": "6.1.0",
"eslint-plugin-vue": "5.2.3", "eslint-plugin-vue": "5.2.3",
"eventemitter3": "4.0.0", "eventemitter3": "4.0.0",
"feed": "3.0.0", "feed": "3.0.0",
"file-type": "12.0.0", "file-type": "12.0.1",
"fluent-ffmpeg": "2.1.2",
"fuckadblock": "3.2.1", "fuckadblock": "3.2.1",
"gulp": "4.0.2", "gulp": "4.0.2",
"gulp-cssnano": "2.1.3", "gulp-cssnano": "2.1.3",
@@ -145,7 +146,8 @@
"hard-source-webpack-plugin": "0.13.1", "hard-source-webpack-plugin": "0.13.1",
"html-minifier": "4.0.0", "html-minifier": "4.0.0",
"http-signature": "1.2.0", "http-signature": "1.2.0",
"insert-text-at-cursor": "0.2.0", "https-proxy-agent": "2.2.2",
"insert-text-at-cursor": "0.3.0",
"is-root": "2.1.0", "is-root": "2.1.0",
"is-svg": "4.2.0", "is-svg": "4.2.0",
"js-yaml": "3.13.1", "js-yaml": "3.13.1",
@@ -159,7 +161,7 @@
"koa-compress": "3.0.0", "koa-compress": "3.0.0",
"koa-favicon": "2.0.1", "koa-favicon": "2.0.1",
"koa-json-body": "5.3.0", "koa-json-body": "5.3.0",
"koa-logger": "3.2.0", "koa-logger": "3.2.1",
"koa-mount": "4.0.0", "koa-mount": "4.0.0",
"koa-multer": "1.0.2", "koa-multer": "1.0.2",
"koa-router": "7.4.0", "koa-router": "7.4.0",
@@ -170,31 +172,28 @@
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"lolex": "4.1.0", "lolex": "4.1.0",
"lookup-dns-cache": "2.1.0", "lookup-dns-cache": "2.1.0",
"minio": "7.0.10", "mocha": "6.2.0",
"mocha": "6.1.4",
"moji": "0.5.1", "moji": "0.5.1",
"moment": "2.24.0",
"ms": "2.1.2", "ms": "2.1.2",
"nested-property": "1.0.1", "nested-property": "1.0.1",
"node-fetch": "2.6.0", "node-fetch": "2.6.0",
"nodemailer": "6.2.1", "nodemailer": "6.3.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"object-assign-deep": "0.4.0", "object-assign-deep": "0.4.0",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"parse5": "5.1.0", "parse5": "5.1.0",
"parsimmon": "1.12.0", "parsimmon": "1.12.1",
"pg": "7.11.0", "pg": "7.11.0",
"portscanner": "2.2.0", "portscanner": "2.2.0",
"postcss-loader": "3.0.0", "postcss-loader": "3.0.0",
"prismjs": "1.16.0", "prismjs": "1.16.0",
"progress-bar-webpack-plugin": "1.12.1", "progress-bar-webpack-plugin": "1.12.1",
"promise-any": "0.2.0",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"promise-sequential": "1.1.1", "promise-sequential": "1.1.1",
"pug": "2.0.4", "pug": "2.0.4",
"punycode": "2.1.1", "punycode": "2.1.1",
"pureimage": "0.1.6", "pureimage": "0.1.6",
"qrcode": "1.3.3", "qrcode": "1.4.1",
"random-seed": "0.3.0", "random-seed": "0.3.0",
"randomcolor": "0.5.4", "randomcolor": "0.5.4",
"ratelimiter": "3.3.0", "ratelimiter": "3.3.0",
@@ -220,7 +219,7 @@
"stylus": "0.54.5", "stylus": "0.54.5",
"stylus-loader": "3.0.2", "stylus-loader": "3.0.2",
"summaly": "2.3.0", "summaly": "2.3.0",
"systeminformation": "4.13.1", "systeminformation": "4.14.4",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.3.0", "terser-webpack-plugin": "1.3.0",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
@@ -231,25 +230,24 @@
"tslint": "5.18.0", "tslint": "5.18.0",
"tslint-sonarts": "1.9.0", "tslint-sonarts": "1.9.0",
"typeorm": "0.2.18", "typeorm": "0.2.18",
"typescript": "3.5.2", "typescript": "3.5.3",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
"ulid": "2.3.0", "ulid": "2.3.0",
"url-loader": "2.0.1", "url-loader": "2.1.0",
"uuid": "3.3.2", "uuid": "3.3.2",
"v-animate-css": "0.0.3", "v-animate-css": "0.0.3",
"v-debounce": "0.1.2", "v-debounce": "0.1.2",
"video-thumbnail-generator": "1.1.3",
"vue": "2.6.10", "vue": "2.6.10",
"vue-color": "2.7.0", "vue-color": "2.7.0",
"vue-content-loading": "1.6.0", "vue-content-loading": "1.6.0",
"vue-cropperjs": "4.0.0", "vue-cropperjs": "4.0.0",
"vue-i18n": "8.11.2", "vue-i18n": "8.12.0",
"vue-js-modal": "1.3.31", "vue-js-modal": "1.3.31",
"vue-json-pretty": "1.6.0", "vue-json-pretty": "1.6.0",
"vue-loader": "15.7.0", "vue-loader": "15.7.1",
"vue-marquee-text-component": "1.1.1", "vue-marquee-text-component": "1.1.1",
"vue-prism-component": "1.1.1", "vue-prism-component": "1.1.1",
"vue-router": "3.0.6", "vue-router": "3.0.7",
"vue-sequential-entrance": "1.1.3", "vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.16", "vue-svg-inline-loader": "1.2.16",
@@ -259,10 +257,13 @@
"vuex": "3.1.1", "vuex": "3.1.1",
"vuex-persistedstate": "2.5.4", "vuex-persistedstate": "2.5.4",
"web-push": "3.3.5", "web-push": "3.3.5",
"webpack": "4.35.2", "webpack": "4.36.1",
"webpack-cli": "3.3.5", "webpack-cli": "3.3.6",
"websocket": "1.0.28", "websocket": "1.0.29",
"ws": "7.0.1", "ws": "7.1.1",
"xev": "2.0.1" "xev": "2.0.1"
},
"devDependencies": {
"@types/fluent-ffmpeg": "2.1.10"
} }
} }

View File

@@ -1,17 +1,9 @@
declare module 'lookup-dns-cache' { declare module 'lookup-dns-cache' {
type IPv4 = 4; import { LookupOneOptions, LookupAllOptions, LookupOptions, LookupAddress } from 'dns'
type IPv6 = 6; function lookup(hostname: string, family: number, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
function lookup(hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
type Family = IPv4 | IPv6 | undefined; function lookup(hostname: string, options: LookupAllOptions, callback: (err: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void): void;
function lookup(hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family: number) => void): void;
interface IRunOptions { function lookup(hostname: string, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
family?: Family;
all?: boolean;
}
type RunCallback = (error: Error | null, address?: string | string[], family?: Family) => void;
export function lookup(hostname: string, options: IRunOptions | Family, callback: RunCallback): {} | undefined;
export function lookup(hostname: string, callback: RunCallback): {} | undefined;
} }

View File

@@ -1,7 +0,0 @@
declare module 'promise-any' {
function promiseAny<T>(iterable: Iterable<T | PromiseLike<T>>): Promise<T>;
namespace promiseAny {} // Hack
export = promiseAny;
}

View File

@@ -12,6 +12,31 @@
</ui-horizon-group> </ui-horizon-group>
</section> </section>
</ui-card> </ui-card>
<ui-card>
<template #title>{{ $t('logs.title') }}</template>
<section class="fit-top">
<sequential-entrance animation="entranceFromTop" delay="25">
<div v-for="log in logs" :key="log.id" class="">
<ui-horizon-group inputs>
<ui-input :value="log.user | acct" type="text" readonly>
<span>{{ $t('logs.moderator') }}</span>
</ui-input>
<ui-input :value="log.type" type="text" readonly>
<span>{{ $t('logs.type') }}</span>
</ui-input>
<ui-input :value="log.createdAt | date" type="text" readonly>
<span>{{ $t('logs.at') }}</span>
</ui-input>
</ui-horizon-group>
<ui-textarea :value="JSON.stringify(log.info, null, 4)" readonly>
<span>{{ $t('logs.info') }}</span>
</ui-textarea>
</div>
</sequential-entrance>
<ui-button v-if="existMoreLogs" @click="fetchLogs">{{ $t('@.load-more') }}</ui-button>
</section>
</ui-card>
</div> </div>
</template> </template>
@@ -26,10 +51,17 @@ export default Vue.extend({
data() { data() {
return { return {
username: '', username: '',
changing: false changing: false,
logs: [],
untilLogId: null,
existMoreLogs: false
}; };
}, },
created() {
this.fetchLogs();
},
methods: { methods: {
async add() { async add() {
this.changing = true; this.changing = true;
@@ -74,6 +106,22 @@ export default Vue.extend({
this.changing = false; this.changing = false;
}, },
fetchLogs() {
this.$root.api('admin/show-moderation-logs', {
untilId: this.untilId,
limit: 10 + 1
}).then(logs => {
if (logs.length == 10 + 1) {
logs.pop();
this.existMoreLogs = true;
} else {
this.existMoreLogs = false;
}
this.logs = this.logs.concat(logs);
this.untilLogId = this.logs[this.logs.length - 1].id;
});
},
} }
}); });
</script> </script>

View File

@@ -5,7 +5,7 @@
<mk-avatar class="avatar" :user="user" :disable-link="true"/> <mk-avatar class="avatar" :user="user" :disable-link="true"/>
</a> </a>
</div> </div>
<div> <div @click="click(user.id)">
<header> <header>
<b><mk-user-name :user="user"/></b> <b><mk-user-name :user="user"/></b>
<span class="username">@{{ user | acct }}</span> <span class="username">@{{ user | acct }}</span>
@@ -32,7 +32,7 @@ import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/users.vue'), i18n: i18n('admin/views/users.vue'),
props: ['user'], props: ['user', 'click'],
data() { data() {
return { return {
faSnowflake, faMicrophoneSlash faSnowflake, faMicrophoneSlash
@@ -44,7 +44,7 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.kofvwchc .kofvwchc
display flex display flex
padding 16px 0 padding 16px
border-top solid 1px var(--faceDivider) border-top solid 1px var(--faceDivider)
> div:first-child > div:first-child
@@ -55,6 +55,7 @@ export default Vue.extend({
> div:last-child > div:last-child
flex 1 flex 1
cursor pointer
padding-left 16px padding-left 16px
@media (max-width 500px) @media (max-width 500px)
@@ -80,4 +81,15 @@ export default Vue.extend({
> .is-suspended > .is-suspended
margin 0 0 0 .5em margin 0 0 0 .5em
color #4dabf7 color #4dabf7
&:hover
color var(--primaryForeground)
background var(--primary)
text-decoration none
border-radius 3px
&:active
color var(--primaryForeground)
background var(--primaryDarken10)
border-radius 3px
</style> </style>

View File

@@ -8,7 +8,7 @@
</ui-input> </ui-input>
<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button> <ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
<div class="user" v-if="user"> <div ref="user" class="user" v-if="user" :key="user.id">
<x-user :user="user"/> <x-user :user="user"/>
<div class="actions"> <div class="actions">
<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button> <ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button>
@@ -54,8 +54,16 @@
<option value="remote">{{ $t('users.origin.remote') }}</option> <option value="remote">{{ $t('users.origin.remote') }}</option>
</ui-select> </ui-select>
</ui-horizon-group> </ui-horizon-group>
<ui-horizon-group searchboxes>
<ui-input v-model="searchUsername" type="text" spellcheck="false" @input="fetchUsers(true)">
<span>{{ $t('username') }}</span>
</ui-input>
<ui-input v-model="searchHost" type="text" spellcheck="false" @input="fetchUsers(true)" :disabled="origin === 'local'">
<span>{{ $t('host') }}</span>
</ui-input>
</ui-horizon-group>
<sequential-entrance animation="entranceFromTop" delay="25"> <sequential-entrance animation="entranceFromTop" delay="25">
<x-user v-for="user in users" :user='user' :key="user.id"/> <x-user v-for="user in users" :key="user.id" :user='user' :click="showUserOnClick"/>
</sequential-entrance> </sequential-entrance>
<ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button> <ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button>
</section> </section>
@@ -85,6 +93,8 @@ export default Vue.extend({
sort: '+createdAt', sort: '+createdAt',
state: 'all', state: 'all',
origin: 'local', origin: 'local',
searchUsername: '',
searchHost: '',
limit: 10, limit: 10,
offset: 0, offset: 0,
users: [], users: [],
@@ -107,6 +117,7 @@ export default Vue.extend({
}, },
origin() { origin() {
if (this.origin === 'local') this.searchHost = '';
this.users = []; this.users = [];
this.offset = 0; this.offset = 0;
this.fetchUsers(); this.fetchUsers();
@@ -157,6 +168,15 @@ export default Vue.extend({
this.target = ''; this.target = '';
}, },
async showUserOnClick(userId: string) {
this.$root.api('admin/show-user', { userId: userId }).then(info => {
this.user = info;
this.$nextTick(() => {
this.$refs.user.scrollIntoView();
});
});
},
/** 処理対象ユーザーの情報を更新する */ /** 処理対象ユーザーの情報を更新する */
async refreshUser() { async refreshUser() {
this.$root.api('admin/show-user', { userId: this.user.id }).then(info => { this.$root.api('admin/show-user', { userId: this.user.id }).then(info => {
@@ -308,13 +328,16 @@ export default Vue.extend({
return !confirm.canceled; return !confirm.canceled;
}, },
fetchUsers() { fetchUsers(truncate?: boolean) {
if (truncate) this.offset = 0;
this.$root.api('admin/show-users', { this.$root.api('admin/show-users', {
state: this.state, state: this.state,
origin: this.origin, origin: this.origin,
sort: this.sort, sort: this.sort,
offset: this.offset, offset: this.offset,
limit: this.limit + 1 limit: this.limit + 1,
username: this.searchUsername,
hostname: this.searchHost
}).then(users => { }).then(users => {
if (users.length == this.limit + 1) { if (users.length == this.limit + 1) {
users.pop(); users.pop();
@@ -322,7 +345,7 @@ export default Vue.extend({
} else { } else {
this.existMore = false; this.existMore = false;
} }
this.users = this.users.concat(users); this.users = truncate ? users : this.users.concat(users);
this.offset += this.limit; this.offset += this.limit;
}); });
} }

View File

@@ -32,6 +32,12 @@ export function collectPageVars(content) {
type: 'number', type: 'number',
value: 0 value: 0
}); });
} else if (x.type === 'radioButton') {
pageVars.push({
name: x.name,
type: 'string',
value: x.default || ''
});
} else if (x.children) { } else if (x.children) {
collect(x.children); collect(x.children);
} }

View File

@@ -4,7 +4,8 @@ const faces = [
'🐡( \'-\' 🐡 )フグパンチ!!!!', '🐡( \'-\' 🐡 )フグパンチ!!!!',
'✌️(´・_・`)✌️', '✌️(´・_・`)✌️',
'(。><。)', '(。><。)',
'(Δ・x・Δ)' '(Δ・x・Δ)',
'(コ`・ヘ・´ケ)'
]; ];
export default () => faces[Math.floor(Math.random() * faces.length)]; export default () => faces[Math.floor(Math.random() * faces.length)];

View File

@@ -3,6 +3,7 @@ import { sum, unique } from '../../../../prelude/array';
import shouldMuteNote from './should-mute-note'; import shouldMuteNote from './should-mute-note';
import MkNoteMenu from '../views/components/note-menu.vue'; import MkNoteMenu from '../views/components/note-menu.vue';
import MkReactionPicker from '../views/components/reaction-picker.vue'; import MkReactionPicker from '../views/components/reaction-picker.vue';
import pleaseLogin from './please-login';
function focus(el, fn) { function focus(el, fn) {
const target = fn(el); const target = fn(el);
@@ -108,6 +109,7 @@ export default (opts: Opts = {}) => ({
methods: { methods: {
reply(viaKeyboard = false) { reply(viaKeyboard = false) {
pleaseLogin(this.$root);
this.$root.$post({ this.$root.$post({
reply: this.appearNote, reply: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
@@ -118,6 +120,7 @@ export default (opts: Opts = {}) => ({
}, },
renote(viaKeyboard = false) { renote(viaKeyboard = false) {
pleaseLogin(this.$root);
this.$root.$post({ this.$root.$post({
renote: this.appearNote, renote: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
@@ -134,6 +137,7 @@ export default (opts: Opts = {}) => ({
}, },
react(viaKeyboard = false) { react(viaKeyboard = false) {
pleaseLogin(this.$root);
this.blur(); this.blur();
this.$root.new(MkReactionPicker, { this.$root.new(MkReactionPicker, {
source: this.$refs.reactButton, source: this.$refs.reactButton,
@@ -159,6 +163,7 @@ export default (opts: Opts = {}) => ({
}, },
favorite() { favorite() {
pleaseLogin(this.$root);
this.$root.api('notes/favorites/create', { this.$root.api('notes/favorites/create', {
noteId: this.appearNote.id noteId: this.appearNote.id
}).then(() => { }).then(() => {

View File

@@ -67,6 +67,7 @@ export default (opts) => ({
async init() { async init() {
this.fetching = true; this.fetching = true;
if (opts.beforeInit) opts.beforeInit(this);
let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params; let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;
if (params && params.then) params = await params; if (params && params.then) params = await params;
await this.$root.api(this.pagination.endpoint, { await this.$root.api(this.pagination.endpoint, {

View File

@@ -0,0 +1,10 @@
export default ($root: any) => {
if ($root.$store.getters.isSignedIn) return;
$root.dialog({
title: $root.$t('@.signin-required'),
text: null
});
throw new Error('signin required');
};

View File

@@ -35,6 +35,10 @@ export default (opts) => ({
type: String, type: String,
required: false required: false
}, },
initialNote: {
type: Object,
required: false
},
instant: { instant: {
type: Boolean, type: Boolean,
required: false, required: false,
@@ -195,6 +199,28 @@ export default (opts) => ({
this.$emit('change-attached-files', this.files); this.$emit('change-attached-files', this.files);
} }
} }
if (this.initialNote) {
// 削除して編集
const init = this.initialNote;
this.text = init.text ? init.text : '';
this.files = init.files;
this.cw = init.cw;
this.useCw = init.cw != null;
if (init.poll) {
this.poll = true;
this.$nextTick(() => {
(this.$refs.poll as any).set({
choices: init.poll.choices.map(c => c.text),
multiple: init.poll.multiple
});
});
}
// hack 位置情報投稿が動くようになったら適用する
this.geo = null;
this.visibility = init.visibility;
this.localOnly = init.localOnly;
this.quoteId = init.renote ? init.renote.id : null;
}
this.$nextTick(() => this.watch()); this.$nextTick(() => this.watch());
}); });
@@ -328,6 +354,7 @@ export default (opts) => ({
this.text = ''; this.text = '';
this.files = []; this.files = [];
this.poll = false; this.poll = false;
this.quoteId = null;
this.$emit('change-attached-files', this.files); this.$emit('change-attached-files', this.files);
}, },
@@ -357,7 +384,7 @@ export default (opts) => ({
const paste = e.clipboardData.getData('text'); const paste = e.clipboardData.getData('text');
if (paste.startsWith(url + '/notes/')) { if (!this.renote && !this.quoteId && paste.startsWith(url + '/notes/')) {
e.preventDefault(); e.preventDefault();
this.$root.dialog({ this.$root.dialog({

View File

@@ -98,7 +98,7 @@ export default Vue.extend({
return { return {
inputValue: this.input && this.input.default ? this.input.default : null, inputValue: this.input && this.input.default ? this.input.default : null,
userInputValue: null, userInputValue: null,
selectedValue: this.select ? this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null, selectedValue: this.select ? this.select.default ? this.select.default : this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
canOk: true, canOk: true,
faTimesCircle, faQuestionCircle faTimesCircle, faQuestionCircle
}; };

View File

@@ -1,11 +1,17 @@
<template> <template>
<router-link class="ldlomzub" :to="`/${ canonical }`" v-user-preview="canonical"> <router-link class="ldlomzub" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<span class="me" v-if="isMe">{{ $t('@.you') }}</span> <span class="me" v-if="isMe">{{ $t('@.you') }}</span>
<span class="main"> <span class="main">
<span class="username">@{{ username }}</span> <span class="username">@{{ username }}</span>
<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span> <span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span>
</span> </span>
</router-link> </router-link>
<a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else>
<span class="main">
<span class="username">@{{ username }}</span>
<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }">@{{ toUnicode(host) }}</span>
</span>
</a>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -32,6 +38,15 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
url(): string {
switch (this.host) {
case 'twitter.com':
case 'github.com':
return `https://${this.host}/${this.username}`;
default:
return `/${this.canonical}`;
}
},
canonical(): string { canonical(): string {
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`; return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
}, },

View File

@@ -30,6 +30,7 @@ export default Vue.extend({
border-radius 4px border-radius 4px
>>> .quote >>> .quote
display block
margin 8px margin 8px
padding 6px 0 6px 12px padding 6px 0 6px 12px
color var(--mfmQuote) color var(--mfmQuote)

View File

@@ -22,6 +22,7 @@ export default Vue.extend({
}, },
computed: { computed: {
items(): any[] { items(): any[] {
if (this.$store.getters.isSignedIn) {
return [{ return [{
icon: 'at', icon: 'at',
text: this.$t('mention'), text: this.$t('mention'),
@@ -74,14 +75,42 @@ export default Vue.extend({
action: () => this.togglePin(true) action: () => this.togglePin(true)
} : undefined, } : undefined,
...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [ ...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [
null, { null,
this.note.userId == this.$store.state.i.id ? {
icon: 'undo-alt',
text: this.$t('delete-and-edit'),
action: this.deleteAndEdit
} : undefined,
{
icon: ['far', 'trash-alt'], icon: ['far', 'trash-alt'],
text: this.$t('delete'), text: this.$t('delete'),
action: this.del action: this.del
}] }]
: [] : []
)] )]
.filter(x => x !== undefined) .filter(x => x !== undefined);
} else {
return [{
icon: 'info-circle',
text: this.$t('detail'),
action: this.detail
}, {
icon: faCopy,
text: this.$t('copy-content'),
action: this.copyContent
}, {
icon: 'link',
text: this.$t('copy-link'),
action: this.copyLink
}, this.note.uri ? {
icon: 'external-link-square-alt',
text: this.$t('remote'),
action: () => {
window.open(this.note.uri, '_blank');
}
} : undefined]
.filter(x => x !== undefined);
}
} }
}, },
@@ -154,6 +183,25 @@ export default Vue.extend({
}); });
}, },
deleteAndEdit() {
this.$root.dialog({
type: 'warning',
text: this.$t('delete-and-edit-confirm'),
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) return;
this.$root.api('notes/delete', {
noteId: this.note.id
}).then(() => {
this.destroyDom();
});
this.$post({
initialNote: this.note,
reply: this.note.reply,
});
});
},
toggleFavorite(favorite: boolean) { toggleFavorite(favorite: boolean) {
this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
noteId: this.note.id noteId: this.note.id

View File

@@ -16,10 +16,11 @@ import XIf from './page.if.vue';
import XTextarea from './page.textarea.vue'; import XTextarea from './page.textarea.vue';
import XPost from './page.post.vue'; import XPost from './page.post.vue';
import XCounter from './page.counter.vue'; import XCounter from './page.counter.vue';
import XRadioButton from './page.radio-button.vue';
export default Vue.extend({ export default Vue.extend({
components: { components: {
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton
}, },
props: { props: {

View File

@@ -0,0 +1,37 @@
<template>
<div>
<div>{{ script.interpolate(value.title) }}</div>
<ui-radio v-for="x in value.values" v-model="v" :value="x" :key="x">{{ x }}</ui-radio>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
value: {
required: true
},
script: {
required: true
}
},
data() {
return {
v: this.value.default,
};
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
}
}
});
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -26,13 +26,19 @@
<option value="after">{{ $t('after') }}</option> <option value="after">{{ $t('after') }}</option>
</ui-select> </ui-select>
<section v-if="expiration === 'at'"> <section v-if="expiration === 'at'">
<ui-input v-model="atDate" type="date">{{ $t('deadline-date') }}</ui-input> <ui-input v-model="atDate" type="date">
<ui-input v-model="atTime" type="time">{{ $t('deadline-time') }}</ui-input> <template #title>{{ $t('deadline-date') }}</template>
</ui-input>
<ui-input v-model="atTime" type="time">
<template #title>{{ $t('deadline-time') }}</template>
</ui-input>
</section> </section>
<section v-if="expiration === 'after'"> <section v-if="expiration === 'after'">
<ui-input v-model="after" type="number">{{ $t('interval') }}</ui-input> <ui-input v-model="after" type="number">
<template #title>{{ $t('interval') }}</template>
</ui-input>
<ui-select v-model="unit"> <ui-select v-model="unit">
<template #label>{{ $t('unit') }}</template> <template #title>{{ $t('unit') }}</template>
<option value="second">{{ $t('second') }}</option> <option value="second">{{ $t('second') }}</option>
<option value="minute">{{ $t('minute') }}</option> <option value="minute">{{ $t('minute') }}</option>
<option value="hour">{{ $t('hour') }}</option> <option value="hour">{{ $t('hour') }}</option>
@@ -46,9 +52,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as moment from 'moment';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import { erase } from '../../../../../prelude/array'; import { erase } from '../../../../../prelude/array';
import { addTimespan } from '../../../../../prelude/time';
import { formatDateTimeString } from '../../../../../misc/format-time-string';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/poll-editor.vue'), i18n: i18n('common/views/components/poll-editor.vue'),
data() { data() {
@@ -56,7 +64,7 @@ export default Vue.extend({
choices: ['', ''], choices: ['', ''],
multiple: false, multiple: false,
expiration: 'infinite', expiration: 'infinite',
atDate: moment().add(1, 'day').toISOString().split('T')[0], atDate: formatDateTimeString(addTimespan(new Date(), 1, 'days'), 'yyyy-MM-dd'),
atTime: '00:00', atTime: '00:00',
after: 0, after: 0,
unit: 'second' unit: 'second'
@@ -89,7 +97,7 @@ export default Vue.extend({
get() { get() {
const at = () => { const at = () => {
return moment(`${this.atDate} ${this.atTime}`).valueOf(); return new Date(`${this.atDate} ${this.atTime}`).getTime();
}; };
const after = () => { const after = () => {

View File

@@ -36,7 +36,7 @@ export default Vue.extend({
required: true required: true
}, },
detachMediaFn: { detachMediaFn: {
type: Object, type: Function,
required: false required: false
} }
}, },

View File

@@ -276,6 +276,7 @@ export default Vue.extend({
font-size 14px font-size 14px
color var(--popupFg) color var(--popupFg)
border-bottom solid var(--lineWidth) var(--faceDivider) border-bottom solid var(--lineWidth) var(--faceDivider)
line-height 20px
> .buttons > .buttons
padding 4px 4px 8px 4px padding 4px 4px 8px 4px

View File

@@ -29,8 +29,25 @@ export default Vue.extend({
computed: { computed: {
appTypeForce: { appTypeForce: {
get() { return this.$store.state.device.appTypeForce; }, get() { return this.$store.state.device.appTypeForce; },
set(value) { this.$store.commit('device/set', { key: 'appTypeForce', value }); } set(value) {
this.$store.commit('device/set', { key: 'appTypeForce', value });
this.reload();
}
}, },
}, },
methods: {
reload() {
this.$root.dialog({
type: 'warning',
text: this.$t('@.reload-to-apply-the-setting'),
showCancelButton: true
}).then(({ canceled }) => {
if (!canceled) {
location.reload();
}
});
},
}
}); });
</script> </script>

View File

@@ -51,6 +51,26 @@
<template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template> <template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
</ui-input> </ui-input>
<div class="fields">
<header>{{ $t('profile-metadata') }}</header>
<ui-horizon-group>
<ui-input v-model="fieldName0">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue0">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
<ui-horizon-group>
<ui-input v-model="fieldName1">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue1">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
<ui-horizon-group>
<ui-input v-model="fieldName2">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue2">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
<ui-horizon-group>
<ui-input v-model="fieldName3">{{ $t('metadata-label') }}</ui-input>
<ui-input v-model="fieldValue3">{{ $t('metadata-content') }}</ui-input>
</ui-horizon-group>
</div>
<ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button> <ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
</ui-form> </ui-form>
</section> </section>
@@ -139,6 +159,14 @@ export default Vue.extend({
username: null, username: null,
location: null, location: null,
description: null, description: null,
fieldName0: null,
fieldValue0: null,
fieldName1: null,
fieldValue1: null,
fieldName2: null,
fieldValue2: null,
fieldName3: null,
fieldValue3: null,
lang: null, lang: null,
birthday: null, birthday: null,
avatarId: null, avatarId: null,
@@ -189,6 +217,15 @@ export default Vue.extend({
this.isLocked = this.$store.state.i.isLocked; this.isLocked = this.$store.state.i.isLocked;
this.carefulBot = this.$store.state.i.carefulBot; this.carefulBot = this.$store.state.i.carefulBot;
this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed; this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed;
this.fieldName0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].name : null;
this.fieldValue0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].value : null;
this.fieldName1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].name : null;
this.fieldValue1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].value : null;
this.fieldName2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].name : null;
this.fieldValue2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].value : null;
this.fieldName3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].name : null;
this.fieldValue3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].value : null;
}, },
methods: { methods: {
@@ -237,6 +274,13 @@ export default Vue.extend({
}, },
save(notify) { save(notify) {
const fields = [
{ name: this.fieldName0, value: this.fieldValue0 },
{ name: this.fieldName1, value: this.fieldValue1 },
{ name: this.fieldName2, value: this.fieldValue2 },
{ name: this.fieldName3, value: this.fieldValue3 },
];
this.saving = true; this.saving = true;
this.$root.api('i/update', { this.$root.api('i/update', {
@@ -247,6 +291,7 @@ export default Vue.extend({
birthday: this.birthday || null, birthday: this.birthday || null,
avatarId: this.avatarId || undefined, avatarId: this.avatarId || undefined,
bannerId: this.bannerId || undefined, bannerId: this.bannerId || undefined,
fields,
isCat: !!this.isCat, isCat: !!this.isCat,
isBot: !!this.isBot, isBot: !!this.isBot,
isLocked: !!this.isLocked, isLocked: !!this.isLocked,
@@ -265,6 +310,29 @@ export default Vue.extend({
text: this.$t('saved') text: this.$t('saved')
}); });
} }
}).catch(err => {
this.saving = false;
switch(err.id) {
case 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191':
this.$root.dialog({
type: 'error',
title: this.$t('unable-to-process'),
text: this.$t('avatar-not-an-image')
});
break;
case '75aedb19-2afd-4e6d-87fc-67941256fa60':
this.$root.dialog({
type: 'error',
title: this.$t('unable-to-process'),
text: this.$t('banner-not-an-image')
});
break;
default:
this.$root.dialog({
type: 'error',
text: this.$t('unable-to-process')
});
}
}); });
}, },
@@ -366,4 +434,9 @@ export default Vue.extend({
height 72px height 72px
margin auto margin auto
.fields
> header
padding 8px 0px
font-weight bold
</style> </style>

View File

@@ -47,6 +47,7 @@
<ui-switch v-model="disableAnimatedMfm">{{ $t('@._settings.disable-animated-mfm') }}</ui-switch> <ui-switch v-model="disableAnimatedMfm">{{ $t('@._settings.disable-animated-mfm') }}</ui-switch>
<ui-switch v-model="disableShowingAnimatedImages">{{ $t('@._settings.disable-showing-animated-images') }}</ui-switch> <ui-switch v-model="disableShowingAnimatedImages">{{ $t('@._settings.disable-showing-animated-images') }}</ui-switch>
<ui-switch v-model="remainDeletedNote">{{ $t('@._settings.remain-deleted-note') }}</ui-switch> <ui-switch v-model="remainDeletedNote">{{ $t('@._settings.remain-deleted-note') }}</ui-switch>
<ui-switch v-model="enableMobileQuickNotificationView">{{ $t('@._settings.enable-quick-notification-view') }}</ui-switch>
</section> </section>
<section> <section>
<header>{{ $t('@._settings.line-width') }}</header> <header>{{ $t('@._settings.line-width') }}</header>
@@ -143,13 +144,17 @@
<ui-input v-model="webSearchEngine">{{ $t('@._settings.web-search-engine') }} <ui-input v-model="webSearchEngine">{{ $t('@._settings.web-search-engine') }}
<template #desc>{{ $t('@._settings.web-search-engine-desc') }}</template> <template #desc>{{ $t('@._settings.web-search-engine-desc') }}</template>
</ui-input> </ui-input>
<ui-button @click="save('webSearchEngine', webSearchEngine)"><fa :icon="faSave"/> {{ $t('@._settings.save') }}</ui-button>
</section> </section>
<section v-if="!$root.isMobile"> <section v-if="!$root.isMobile">
<header>{{ $t('@._settings.paste') }}</header> <header>{{ $t('@._settings.paste') }}</header>
<ui-input v-model="pastedFileName">{{ $t('@._settings.pasted-file-name') }} <ui-input v-model="pastedFileName">{{ $t('@._settings.pasted-file-name') }}
<template #desc>{{ $t('@._settings.pasted-file-name-desc') }}</template> <template v-if="pastedFileName === this.$store.state.settings.pastedFileName" #desc>{{ $t('@._settings.pasted-file-name-desc') }}</template>
<template v-else #desc>{{ pastedFileNamePreview() }}</template>
</ui-input> </ui-input>
<ui-button @click="save('pastedFileName', pastedFileName)"><fa :icon="faSave"/> {{ $t('@._settings.save') }}</ui-button>
<ui-switch v-model="pasteDialog">{{ $t('@._settings.paste-dialog') }} <ui-switch v-model="pasteDialog">{{ $t('@._settings.paste-dialog') }}
<template #desc>{{ $t('@._settings.paste-dialog-desc') }}</template> <template #desc>{{ $t('@._settings.paste-dialog-desc') }}</template>
</ui-switch> </ui-switch>
@@ -289,6 +294,8 @@ import XNotification from './notification.vue';
import { url, version } from '../../../../config'; import { url, version } from '../../../../config';
import checkForUpdate from '../../../scripts/check-for-update'; import checkForUpdate from '../../../scripts/check-for-update';
import { formatTimeString } from '../../../../../../misc/format-time-string';
import { faSave } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({ export default Vue.extend({
i18n: i18n(), i18n: i18n(),
@@ -319,8 +326,11 @@ export default Vue.extend({
return { return {
meta: null, meta: null,
version, version,
webSearchEngine: this.$store.state.settings.webSearchEngine,
pastedFileName : this.$store.state.settings.pastedFileName,
latestVersion: undefined, latestVersion: undefined,
checkingForUpdate: false checkingForUpdate: false,
faSave
}; };
}, },
computed: { computed: {
@@ -419,16 +429,6 @@ export default Vue.extend({
set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); } set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
}, },
webSearchEngine: {
get() { return this.$store.state.settings.webSearchEngine; },
set(value) { this.$store.dispatch('settings/set', { key: 'webSearchEngine', value }); }
},
pastedFileName: {
get() { return this.$store.state.settings.pastedFileName; },
set(value) { this.$store.dispatch('settings/set', { key: 'pastedFileName', value }); }
},
pasteDialog: { pasteDialog: {
get() { return this.$store.state.settings.pasteDialog; }, get() { return this.$store.state.settings.pasteDialog; },
set(value) { this.$store.dispatch('settings/set', { key: 'pasteDialog', value }); } set(value) { this.$store.dispatch('settings/set', { key: 'pasteDialog', value }); }
@@ -533,6 +533,11 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); } set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); }
}, },
enableMobileQuickNotificationView: {
get() { return this.$store.state.device.enableMobileQuickNotificationView; },
set(value) { this.$store.commit('device/set', { key: 'enableMobileQuickNotificationView', value }); }
},
homeProfile: { homeProfile: {
get() { return this.$store.state.device.homeProfile; }, get() { return this.$store.state.device.homeProfile; },
set(value) { this.$store.commit('device/set', { key: 'homeProfile', value }); } set(value) { this.$store.commit('device/set', { key: 'homeProfile', value }); }
@@ -565,6 +570,17 @@ export default Vue.extend({
} }
}); });
}, },
save(key, value) {
this.$store.dispatch('settings/set', {
key,
value
}).then(() => {
this.$root.dialog({
type: 'success',
text: this.$t('@._settings.saved')
})
});
},
customizeHome() { customizeHome() {
location.href = '/?customize'; location.href = '/?customize';
}, },
@@ -600,7 +616,10 @@ export default Vue.extend({
const sound = new Audio(`${url}/assets/message.mp3`); const sound = new Audio(`${url}/assets/message.mp3`);
sound.volume = this.$store.state.device.soundVolume; sound.volume = this.$store.state.device.soundVolume;
sound.play(); sound.play();
} },
pastedFileNamePreview() {
return `${formatTimeString(new Date(), this.pastedFileName).replace(/{{number}}/g, `1`)}.png`
},
} }
}); });
</script> </script>

View File

@@ -43,7 +43,7 @@
</i18n> </i18n>
</ui-switch> </ui-switch>
<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div> <div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>
<ui-button type="submit" :disabled="!(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button> <ui-button type="submit" :disabled=" submitting || !(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button>
</template> </template>
</form> </form>
</template> </template>
@@ -70,6 +70,7 @@ export default Vue.extend({
passwordStrength: '', passwordStrength: '',
passwordRetypeState: null, passwordRetypeState: null,
meta: {}, meta: {},
submitting: false,
ToSAgreement: false ToSAgreement: false
} }
}, },
@@ -145,6 +146,9 @@ export default Vue.extend({
}, },
onSubmit() { onSubmit() {
if (this.submitting) return;
this.submitting = true;
this.$root.api('signup', { this.$root.api('signup', {
username: this.username, username: this.username,
password: this.password, password: this.password,
@@ -159,6 +163,8 @@ export default Vue.extend({
location.href = '/'; location.href = '/';
}); });
}).catch(() => { }).catch(() => {
this.submitting = false;
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('some-error') text: this.$t('some-error')

View File

@@ -66,6 +66,7 @@ export default Vue.extend({
(this.url.substr(local.length) === '/') || (this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') || this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') || this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/') ||
this.url.substr(local.length).startsWith('/pages/'); this.url.substr(local.length).startsWith('/pages/');
return { return {
local, local,

View File

@@ -28,6 +28,7 @@ export default Vue.extend({
(this.url.substr(local.length) === '/') || (this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') || this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') || this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/') ||
this.url.substr(local.length).startsWith('/pages/')); this.url.substr(local.length).startsWith('/pages/'));
return { return {
local, local,

View File

@@ -1,8 +1,8 @@
<template> <template>
<x-column :name="name" :column="column" :is-stacked="isStacked"> <x-column :name="name" :column="column" :is-stacked="isStacked" :menu="menu">
<template #header><fa :icon="['far', 'bell']"/>{{ name }}</template> <template #header><fa :icon="['far', 'bell']"/>{{ name }}</template>
<x-notifications/> <x-notifications :type="column.notificationType === 'all' ? null : column.notificationType"/>
</x-column> </x-column>
</template> </template>
@@ -30,11 +30,46 @@ export default Vue.extend({
} }
}, },
data() {
return {
menu: null,
}
},
computed: { computed: {
name(): string { name(): string {
if (this.column.name) return this.column.name; if (this.column.name) return this.column.name;
return this.$t('@deck.notifications'); return this.$t('@deck.notifications');
} }
}, },
created() {
if (this.column.notificationType == null) {
this.column.notificationType = 'all';
this.$store.commit('updateDeckColumn', this.column);
}
this.menu = [{
icon: 'cog',
text: this.$t('@.notification-type'),
action: () => {
this.$root.dialog({
title: this.$t('@.notification-type'),
type: null,
select: {
items: ['all', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'].map(x => ({
value: x, text: this.$t('@.notification-types.' + x)
}))
default: this.column.notificationType,
},
showCancelButton: true
}).then(({ canceled, result: type }) => {
if (canceled) return;
this.column.notificationType = type;
this.$store.commit('updateDeckColumn', this.column);
});
}
}];
},
}); });
</script> </script>

View File

@@ -47,12 +47,22 @@ export default Vue.extend({
}), }),
], ],
props: {
type: {
type: String,
required: false
}
},
data() { data() {
return { return {
connection: null, connection: null,
pagination: { pagination: {
endpoint: 'i/notifications', endpoint: 'i/notifications',
limit: 20, limit: 20,
params: () => ({
includeTypes: this.type ? [this.type] : undefined
})
} }
}; };
}, },
@@ -69,6 +79,12 @@ export default Vue.extend({
} }
}, },
watch: {
type() {
this.reload();
}
},
mounted() { mounted() {
this.connection = this.$root.stream.useSharedConnection('main'); this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);

View File

@@ -0,0 +1,53 @@
<template>
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faBolt"/> {{ $t('blocks.radioButton') }}</template>
<section style="padding: 0 16px 16px 16px;">
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._radioButton.name') }}</span></ui-input>
<ui-input v-model="value.title"><span>{{ $t('blocks._radioButton.title') }}</span></ui-input>
<ui-textarea v-model="values"><span>{{ $t('blocks._radioButton.values') }}</span></ui-textarea>
<ui-input v-model="value.default"><span>{{ $t('blocks._radioButton.default') }}</span></ui-input>
</section>
</x-container>
</template>
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../../../../i18n';
import XContainer from '../page-editor.container.vue';
export default Vue.extend({
i18n: i18n('pages'),
components: {
XContainer
},
props: {
value: {
required: true
},
},
data() {
return {
values: '',
faBolt, faMagic
};
},
watch: {
values() {
Vue.set(this.value, 'values', this.values.split('\n'));
}
},
created() {
if (this.value.name == null) Vue.set(this.value, 'name', '');
if (this.value.title == null) Vue.set(this.value, 'title', '');
if (this.value.values == null) Vue.set(this.value, 'values', []);
this.values = this.value.values.join('\n');
},
});
</script>

View File

@@ -19,10 +19,11 @@ import XSwitch from './els/page-editor.el.switch.vue';
import XIf from './els/page-editor.el.if.vue'; import XIf from './els/page-editor.el.if.vue';
import XPost from './els/page-editor.el.post.vue'; import XPost from './els/page-editor.el.post.vue';
import XCounter from './els/page-editor.el.counter.vue'; import XCounter from './els/page-editor.el.counter.vue';
import XRadioButton from './els/page-editor.el.radio-button.vue';
export default Vue.extend({ export default Vue.extend({
components: { components: {
XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton
}, },
props: { props: {

View File

@@ -342,6 +342,7 @@ export default Vue.extend({
label: this.$t('input-blocks'), label: this.$t('input-blocks'),
items: [ items: [
{ value: 'button', text: this.$t('blocks.button') }, { value: 'button', text: this.$t('blocks.button') },
{ value: 'radioButton', text: this.$t('blocks.radioButton') },
{ value: 'textInput', text: this.$t('blocks.textInput') }, { value: 'textInput', text: this.$t('blocks.textInput') },
{ value: 'textareaInput', text: this.$t('blocks.textareaInput') }, { value: 'textareaInput', text: this.$t('blocks.textareaInput') },
{ value: 'numberInput', text: this.$t('blocks.numberInput') }, { value: 'numberInput', text: this.$t('blocks.numberInput') },

View File

@@ -83,6 +83,21 @@ export default ($root: any) => {
}); });
return i; return i;
}).catch(err => {
switch (err.id) {
case 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191':
$root.dialog({
type: 'error',
title: locale['desktop']['unable-to-process'],
text: locale['desktop']['invalid-filetype']
});
break;
default:
$root.dialog({
type: 'error',
text: locale['desktop']['unable-to-process']
});
}
}); });
}; };

View File

@@ -83,6 +83,21 @@ export default ($root: any) => {
}); });
return i; return i;
}).catch(err => {
switch (err.id) {
case '75aedb19-2afd-4e6d-87fc-67941256fa60':
$root.dialog({
type: 'error',
title: locale['desktop']['unable-to-process'],
text: locale['desktop']['invalid-filetype']
});
break;
default:
$root.dialog({
type: 'error',
text: locale['desktop']['unable-to-process']
});
}
}); });
}; };

View File

@@ -63,7 +63,10 @@ init(async (launch, os) => {
this.$root.newAsync(() => import('./views/components/post-form-window.vue').then(m => m.default), { this.$root.newAsync(() => import('./views/components/post-form-window.vue').then(m => m.default), {
reply: o.reply, reply: o.reply,
mention: o.mention, mention: o.mention,
animation: o.animation == null ? true : o.animation animation: o.animation == null ? true : o.animation,
initialText: o.initialText,
instant: o.instant,
initialNote: o.initialNote,
}).then(vm => { }).then(vm => {
if (o.cb) vm.$once('closed', o.cb); if (o.cb) vm.$once('closed', o.cb);
}); });

View File

@@ -5,7 +5,7 @@
:x="record.x" :y="record.date.weekday" :x="record.x" :y="record.date.weekday"
rx="1" ry="1" rx="1" ry="1"
fill="transparent"> fill="transparent">
<title>{{ record.date.year }}/{{ record.date.month }}/{{ record.date.day }}</title> <title>{{ record.date.year }}/{{ record.date.month + 1 }}/{{ record.date.day }}</title>
</rect> </rect>
<rect v-for="record in data" class="day" <rect v-for="record in data" class="day"
:width="record.v" :height="record.v" :width="record.v" :height="record.v"
@@ -39,17 +39,17 @@ export default Vue.extend({
const month = now.getMonth(); const month = now.getMonth();
const day = now.getDate(); const day = now.getDate();
let x = 0; let x = 20;
this.data.slice().reverse().forEach((d, i) => { this.data.slice().forEach((d, i) => {
d.x = x; d.x = x;
const date = new Date(year, month, day - i); const date = new Date(year, month, day - i);
d.date = { d.date = {
year: date.getFullYear(), year: date.getFullYear(),
month: date.getMonth(), month: date.getMonth(),
day: date.getDate() day: date.getDate(),
weekday: date.getDay()
}; };
d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
d.v = peak == 0 ? 0 : d.total / (peak / 2); d.v = peak == 0 ? 0 : d.total / (peak / 2);
if (d.v > 1) d.v = 1; if (d.v > 1) d.v = 1;
@@ -58,7 +58,7 @@ export default Vue.extend({
const cl = 15 + ((1 - d.v) * 80); const cl = 15 + ((1 - d.v) * 80);
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`; d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
if (d.date.weekday == 6) x++; if (d.date.weekday == 0) x--;
}); });
} }
}); });

View File

@@ -46,7 +46,7 @@ export default Vue.extend({
props: ['data'], props: ['data'],
data() { data() {
return { return {
viewBoxX: 140, viewBoxX: 147,
viewBoxY: 60, viewBoxY: 60,
zoom: 1, zoom: 1,
pos: 0, pos: 0,

View File

@@ -48,7 +48,7 @@ export default Vue.extend({
this.$root.api('charts/user/notes', { this.$root.api('charts/user/notes', {
userId: this.user.id, userId: this.user.id,
span: 'day', span: 'day',
limit: 7 * 20 limit: 7 * 21
}).then(activity => { }).then(activity => {
this.activity = activity.diffs.normal.map((_, i) => ({ this.activity = activity.diffs.normal.map((_, i) => ({
total: activity.diffs.normal[i] + activity.diffs.reply[i] + activity.diffs.renote[i], total: activity.diffs.normal[i] + activity.diffs.reply[i] + activity.diffs.renote[i],

View File

@@ -22,19 +22,19 @@
> >
<div class="selection" ref="selection"></div> <div class="selection" ref="selection"></div>
<div class="contents" ref="contents"> <div class="contents" ref="contents">
<div class="folders" ref="foldersContainer" v-if="folders.length > 0"> <div class="folders" ref="foldersContainer" v-if="folders.length > 0 || moreFolders">
<x-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/> <x-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="n in 16"></div> <div class="padding" v-for="n in 16"></div>
<ui-button v-if="moreFolders">{{ $t('@.load-more') }}</ui-button> <ui-button v-if="moreFolders">{{ $t('@.load-more') }}</ui-button>
</div> </div>
<div class="files" ref="filesContainer" v-if="files.length > 0"> <div class="files" ref="filesContainer" v-if="files.length > 0 || moreFiles">
<x-file v-for="file in files" :key="file.id" class="file" :file="file"/> <x-file v-for="file in files" :key="file.id" class="file" :file="file"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="n in 16"></div> <div class="padding" v-for="n in 16"></div>
<ui-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('@.load-more') }}</ui-button> <ui-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('@.load-more') }}</ui-button>
</div> </div>
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching"> <div class="empty" v-if="files.length == 0 && !moreFiles && folders.length == 0 && !moreFolders && !fetching">
<p v-if="draghover">{{ $t('empty-draghover') }}</p> <p v-if="draghover">{{ $t('empty-draghover') }}</p>
<p v-if="!draghover && folder == null"><strong>{{ $t('empty-drive') }}</strong><br/>{{ $t('empty-drive-description') }}</p> <p v-if="!draghover && folder == null"><strong>{{ $t('empty-drive') }}</strong><br/>{{ $t('empty-drive-description') }}</p>
<p v-if="!draghover && folder != null">{{ $t('empty-folder') }}</p> <p v-if="!draghover && folder != null">{{ $t('empty-folder') }}</p>

View File

@@ -75,7 +75,7 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.gcafiosrssbtbnbzqupfmglvzgiaipyv .gcafiosrssbtbnbzqupfmglvzgiaipyv
position fixed position absolute
top 0 top 0
left 0 left 0
z-index 3000 z-index 3000

View File

@@ -153,6 +153,13 @@ export default Vue.extend({
paging({}), paging({}),
], ],
props: {
type: {
type: String,
required: false
}
},
data() { data() {
return { return {
connection: null, connection: null,
@@ -160,6 +167,9 @@ export default Vue.extend({
pagination: { pagination: {
endpoint: 'i/notifications', endpoint: 'i/notifications',
limit: 10, limit: 10,
params: () => ({
includeTypes: this.type ? [this.type] : undefined
})
} }
}; };
}, },
@@ -176,6 +186,12 @@ export default Vue.extend({
} }
}, },
watch: {
type() {
this.reload();
}
},
mounted() { mounted() {
this.connection = this.$root.stream.useSharedConnection('main'); this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);

View File

@@ -5,16 +5,20 @@
<span class="icon" v-if="geo"><fa icon="map-marker-alt"/></span> <span class="icon" v-if="geo"><fa icon="map-marker-alt"/></span>
<span v-if="!reply">{{ $t('note') }}</span> <span v-if="!reply">{{ $t('note') }}</span>
<span v-if="reply">{{ $t('reply') }}</span> <span v-if="reply">{{ $t('reply') }}</span>
<span class="count" v-if="files.length != 0">{{ this.$t('attaches').replace('{}', files.length) }}</span> <span class="count" v-if="files.length != 0">{{ $t('attaches').replace('{}', files.length) }}</span>
<span class="count" v-if="uploadings.length != 0">{{ this.$t('uploading-media').replace('{}', uploadings.length) }}<mk-ellipsis/></span> <span class="count" v-if="uploadings.length != 0">{{ $t('uploading-media').replace('{}', uploadings.length) }}<mk-ellipsis/></span>
</span> </span>
</template> </template>
<div class="mk-post-form-window--body"> <div class="mk-post-form-window--body" :style="{ maxHeight: `${maxHeight}px` }">
<mk-note-preview v-if="reply" class="notePreview" :note="reply"/> <mk-note-preview v-if="reply" class="notePreview" :note="reply"/>
<x-post-form ref="form" <x-post-form ref="form"
:reply="reply" :reply="reply"
:mention="mention" :mention="mention"
:initial-text="initialText"
:initial-note="initialNote"
:instant="instant"
@posted="onPosted" @posted="onPosted"
@change-uploadings="onChangeUploadings" @change-uploadings="onChangeUploadings"
@change-attached-files="onChangeFiles" @change-attached-files="onChangeFiles"
@@ -50,7 +54,23 @@ export default Vue.extend({
type: Boolean, type: Boolean,
required: false, required: false,
default: true default: true
} },
initialText: {
type: String,
required: false
},
initialNote: {
type: Object,
required: false
},
instant: {
type: Boolean,
required: false,
default: false
},
}, },
data() { data() {
@@ -61,6 +81,12 @@ export default Vue.extend({
}; };
}, },
computed: {
maxHeight() {
return window.innerHeight - 50;
},
},
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
(this.$refs.form as any).focus(); (this.$refs.form as any).focus();

View File

@@ -6,18 +6,18 @@
@drop.stop="onDrop" @drop.stop="onDrop"
> >
<div class="content"> <div class="content">
<div v-if="visibility == 'specified'" class="visibleUsers"> <div class="hashtags" v-if="recentHashtags.length > 0 && $store.state.settings.suggestRecentHashtags">
<b>{{ $t('@.post-form.recent-tags') }}:</b>
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('@.post-form.click-to-tagging')">#{{ tag }}</a>
</div>
<div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }} <a @click="quoteId = null">[x]</a></div>
<div v-if="visibility === 'specified'" class="visibleUsers">
<span v-for="u in visibleUsers"> <span v-for="u in visibleUsers">
<mk-user-name :user="u"/><a @click="removeVisibleUser(u)">[x]</a> <mk-user-name :user="u"/><a @click="removeVisibleUser(u)">[x]</a>
</span> </span>
<a @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</a> <a @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</a>
</div> </div>
<div class="hashtags" v-if="recentHashtags.length > 0 && $store.state.settings.suggestRecentHashtags"> <div class="local-only" v-if="localOnly === true">{{ $t('@.post-form.local-only-message') }}</div>
<b>{{ $t('@.post-form.recent-tags') }}:</b>
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('@.post-form.click-to-tagging')">#{{ tag }}</a>
</div>
<div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }}</div>
<div class="local-only" v-if="localOnly == true">{{ $t('@.post-form.local-only-message') }}</div>
<input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }"> <input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }">
<div class="textarea"> <div class="textarea">
<textarea :class="{ with: (files.length != 0 || poll) }" <textarea :class="{ with: (files.length != 0 || poll) }"
@@ -190,14 +190,6 @@ export default Vue.extend({
border-radius 0 0 4px 4px border-radius 0 0 4px 4px
transition border-color .3s ease transition border-color .3s ease
> .visibleUsers
margin-bottom 8px
font-size 14px
> span
margin-right 16px
color var(--primary)
> .hashtags > .hashtags
margin 0 0 8px 0 margin 0 0 8px 0
overflow hidden overflow hidden
@@ -211,6 +203,18 @@ export default Vue.extend({
margin-right 8px margin-right 8px
white-space nowrap white-space nowrap
> .with-quote
margin 0 0 8px 0
color var(--primary)
> .visibleUsers
margin-bottom 8px
font-size 14px
> span
margin-right 16px
color var(--primary)
> .local-only > .local-only
margin 0 0 8px 0 margin 0 0 8px 0
color var(--primary) color var(--primary)

View File

@@ -260,14 +260,14 @@ export default Vue.extend({
let moveLeft = me.clientX - moveBaseX; let moveLeft = me.clientX - moveBaseX;
let moveTop = me.clientY - moveBaseY; let moveTop = me.clientY - moveBaseY;
// はみ出し // はみ出し
if (moveTop < 0) moveTop = 0; if (moveTop + windowHeight > browserHeight) moveTop = browserHeight - windowHeight;
// 左はみ出し // 左はみ出し
if (moveLeft < 0) moveLeft = 0; if (moveLeft < 0) moveLeft = 0;
// はみ出し // はみ出し
if (moveTop + windowHeight > browserHeight) moveTop = browserHeight - windowHeight; if (moveTop < 0) moveTop = 0;
// 右はみ出し // 右はみ出し
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
@@ -442,10 +442,10 @@ export default Vue.extend({
const browserHeight = window.innerHeight; const browserHeight = window.innerHeight;
const windowWidth = main.offsetWidth; const windowWidth = main.offsetWidth;
const windowHeight = main.offsetHeight; const windowHeight = main.offsetHeight;
if (position.left < 0) main.style.left = 0; if (position.left < 0) main.style.left = 0; // 左はみ出し
if (position.top < 0) main.style.top = 0; if (position.top + windowHeight > browserHeight) main.style.top = browserHeight - windowHeight + 'px'; // 下はみ出し
if (position.left + windowWidth > browserWidth) main.style.left = browserWidth - windowWidth + 'px'; if (position.left + windowWidth > browserWidth) main.style.left = browserWidth - windowWidth + 'px'; // 右はみ出し
if (position.top + windowHeight > browserHeight) main.style.top = browserHeight - windowHeight + 'px'; if (position.top < 0) main.style.top = 0; // 上はみ出し
} }
} }
}); });

View File

@@ -224,6 +224,8 @@ export default Vue.extend({
}, },
addWidget() { addWidget() {
if(this.widgetAdderSelected == null) return;
this.$store.commit('addHomeWidget', { this.$store.commit('addHomeWidget', {
name: this.widgetAdderSelected, name: this.widgetAdderSelected,
id: uuid(), id: uuid(),

View File

@@ -1,10 +1,10 @@
<template> <template>
<div class="mkw-notifications"> <div class="mkw-notifications">
<ui-container :show-header="!props.compact"> <ui-container :show-header="!props.compact">
<template #header><fa :icon="['far', 'bell']"/>{{ $t('title') }}</template> <template #header><fa :icon="['far', 'bell']"/>{{ props.type === 'all' ? $t('title') : $t('@.notification-types.' + props.type) }}</template>
<!-- <button #func :title="$t('title')" @click="settings"><fa icon="cog"/></button> --> <template #func><button :title="$t('@.notification-type')" @click="settings"><fa icon="cog"/></button></template>
<mk-notifications :class="$style.notifications"/> <mk-notifications :class="$style.notifications" :type="props.type === 'all' ? null : props.type"/>
</ui-container> </ui-container>
</div> </div>
</template> </template>
@@ -16,13 +16,28 @@ import i18n from '../../../i18n';
export default define({ export default define({
name: 'notifications', name: 'notifications',
props: () => ({ props: () => ({
compact: false compact: false,
type: 'all'
}) })
}).extend({ }).extend({
i18n: i18n('desktop/views/widgets/notifications.vue'), i18n: i18n('desktop/views/widgets/notifications.vue'),
methods: { methods: {
settings() { settings() {
alert('not implemented yet'); this.$root.dialog({
title: this.$t('@.notification-type'),
type: null,
select: {
items: ['all', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'].map(x => ({
value: x, text: this.$t('@.notification-types.' + x)
}))
default: this.props.type,
},
showCancelButton: true
}).then(({ canceled, result: type }) => {
if (canceled) return;
this.props.type = type;
this.save();
});
}, },
func() { func() {
this.props.compact = !this.props.compact; this.props.compact = !this.props.compact;

View File

@@ -125,7 +125,8 @@ import {
faMapMarker, faMapMarker,
faRobot, faRobot,
faHourglassHalf, faHourglassHalf,
faGavel faGavel,
faUndoAlt,
} from '@fortawesome/free-solid-svg-icons'; } from '@fortawesome/free-solid-svg-icons';
import { import {
@@ -258,6 +259,7 @@ library.add(
faRobot, faRobot,
faHourglassHalf, faHourglassHalf,
faGavel, faGavel,
faUndoAlt,
farBell, farBell,
farEnvelope, farEnvelope,

View File

@@ -174,7 +174,7 @@ export default class MiOS extends EventEmitter {
// Init service worker // Init service worker
if (this.shouldRegisterSw) { if (this.shouldRegisterSw) {
this.getMeta().then(data => { this.getMeta().then(data => {
this.registerSw(data.swPublickey); if (data.swPublickey) this.registerSw(data.swPublickey);
}); });
} }
}; };
@@ -291,7 +291,7 @@ export default class MiOS extends EventEmitter {
* Register service worker * Register service worker
*/ */
@autobind @autobind
private registerSw(swPublickey) { private registerSw(swPublickey: string) {
// Check whether service worker and push manager supported // Check whether service worker and push manager supported
const isSwSupported = const isSwSupported =
('serviceWorker' in navigator) && ('PushManager' in window); ('serviceWorker' in navigator) && ('PushManager' in window);

View File

@@ -14,6 +14,7 @@ import MkIndex from './views/pages/index.vue';
import MkSignup from './views/pages/signup.vue'; import MkSignup from './views/pages/signup.vue';
import MkSelectDrive from './views/pages/selectdrive.vue'; import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue'; import MkDrive from './views/pages/drive.vue';
import MkNotifications from './views/pages/notifications.vue';
import MkMessaging from './views/pages/messaging.vue'; import MkMessaging from './views/pages/messaging.vue';
import MkMessagingRoom from './views/pages/messaging-room.vue'; import MkMessagingRoom from './views/pages/messaging-room.vue';
import MkNote from './views/pages/note.vue'; import MkNote from './views/pages/note.vue';
@@ -54,7 +55,10 @@ init((launch, os) => {
const vm = this.$root.new(PostFormDialog, { const vm = this.$root.new(PostFormDialog, {
reply: o.reply, reply: o.reply,
mention: o.mention, mention: o.mention,
renote: o.renote renote: o.renote,
initialText: o.initialText,
instant: o.instant,
initialNote: o.initialNote,
}); });
vm.$once('cancel', recover); vm.$once('cancel', recover);
vm.$once('posted', recover); vm.$once('posted', recover);
@@ -144,6 +148,7 @@ init((launch, os) => {
{ path: '/i/groups/:group', component: UI, props: route => ({ component: () => import('../common/views/pages/user-group-editor.vue').then(m => m.default), groupId: route.params.group }) }, { path: '/i/groups/:group', component: UI, props: route => ({ component: () => import('../common/views/pages/user-group-editor.vue').then(m => m.default), groupId: route.params.group }) },
{ path: '/i/follow-requests', name: 'follow-requests', component: UI, props: route => ({ component: () => import('../common/views/pages/follow-requests.vue').then(m => m.default) }) }, { path: '/i/follow-requests', name: 'follow-requests', component: UI, props: route => ({ component: () => import('../common/views/pages/follow-requests.vue').then(m => m.default) }) },
{ path: '/i/widgets', name: 'widgets', component: () => import('./views/pages/widgets.vue').then(m => m.default) }, { path: '/i/widgets', name: 'widgets', component: () => import('./views/pages/widgets.vue').then(m => m.default) },
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
{ path: '/i/messaging', name: 'messaging', component: MkMessaging }, { path: '/i/messaging', name: 'messaging', component: MkMessaging },
{ path: '/i/messaging/group/:group', component: MkMessagingRoom }, { path: '/i/messaging/group/:group', component: MkMessagingRoom },
{ path: '/i/messaging/:user', component: MkMessagingRoom }, { path: '/i/messaging/:user', component: MkMessagingRoom },

View File

@@ -25,17 +25,17 @@
<template v-if="folder.filesCount > 0">{{ folder.filesCount }} {{ $t('file-count') }}</template> <template v-if="folder.filesCount > 0">{{ folder.filesCount }} {{ $t('file-count') }}</template>
</p> </p>
</div> </div>
<div class="folders" v-if="folders.length > 0"> <div class="folders" v-if="folders.length > 0 || moreFolders">
<x-folder class="folder" v-for="folder in folders" :key="folder.id" :folder="folder"/> <x-folder class="folder" v-for="folder in folders" :key="folder.id" :folder="folder"/>
<p v-if="moreFolders">{{ $t('@.load-more') }}</p> <p v-if="moreFolders">{{ $t('@.load-more') }}</p>
</div> </div>
<div class="files" v-if="files.length > 0"> <div class="files" v-if="files.length > 0 || moreFiles">
<x-file class="file" v-for="file in files" :key="file.id" :file="file"/> <x-file class="file" v-for="file in files" :key="file.id" :file="file"/>
<button class="more" v-if="moreFiles" @click="fetchMoreFiles"> <button class="more" v-if="moreFiles" @click="fetchMoreFiles">
{{ fetchingMoreFiles ? this.$t('@.loading') : this.$t('@.load-more') }} {{ fetchingMoreFiles ? this.$t('@.loading') : this.$t('@.load-more') }}
</button> </button>
</div> </div>
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching"> <div class="empty" v-if="files.length == 0 && !moreFiles && folders.length == 0 && !moreFolders && !fetching">
<p v-if="folder == null">{{ $t('nothing-in-drive') }}</p> <p v-if="folder == null">{{ $t('nothing-in-drive') }}</p>
<p v-if="folder != null">{{ $t('folder-is-empty') }}</p> <p v-if="folder != null">{{ $t('folder-is-empty') }}</p>
</div> </div>

View File

@@ -100,6 +100,27 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-notification .mk-notification
&.wide
> .notification
@media (min-width 350px)
font-size 14px
@media (min-width 500px)
font-size 16px
@media (min-width 600px)
padding 24px 32px
> .avatar
@media (min-width 500px)
width 42px
height 42px
> div
@media (min-width 500px)
width calc(100% - 42px)
> .notification > .notification
padding 16px padding 16px
font-size 12px font-size 12px

View File

@@ -9,7 +9,7 @@
<!-- トランジションを有効にするとなぜかメモリリークする --> <!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div"> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div">
<template v-for="(notification, i) in _notifications"> <template v-for="(notification, i) in _notifications">
<mk-notification :notification="notification" :key="notification.id"/> <mk-notification :notification="notification" :key="notification.id" :class="{ wide: wide }"/>
<p class="date" :key="notification.id + '_date'" v-if="i != items.length - 1 && notification._date != _notifications[i + 1]._date"> <p class="date" :key="notification.id + '_date'" v-if="i != items.length - 1 && notification._date != _notifications[i + 1]._date">
<span><fa icon="angle-up"/>{{ notification._datetext }}</span> <span><fa icon="angle-up"/>{{ notification._datetext }}</span>
<span><fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}</span> <span><fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}</span>
@@ -37,15 +37,37 @@ export default Vue.extend({
i18n: i18n('mobile/views/components/notifications.vue'), i18n: i18n('mobile/views/components/notifications.vue'),
mixins: [ mixins: [
paging({}), paging({
beforeInit: (self) => {
self.$emit('beforeInit');
},
onInited: (self) => {
self.$emit('inited');
}
}),
], ],
props: {
type: {
type: String,
required: false
},
wide: {
type: Boolean,
required: false,
default: false
}
},
data() { data() {
return { return {
connection: null, connection: null,
pagination: { pagination: {
endpoint: 'i/notifications', endpoint: 'i/notifications',
limit: 15, limit: 15,
params: () => ({
includeTypes: this.type ? [this.type] : undefined
})
} }
}; };
}, },
@@ -62,6 +84,12 @@ export default Vue.extend({
} }
}, },
watch: {
type() {
this.reload();
}
},
mounted() { mounted() {
this.connection = this.$root.stream.useSharedConnection('main'); this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);

View File

@@ -7,6 +7,7 @@
:renote="renote" :renote="renote"
:mention="mention" :mention="mention"
:initial-text="initialText" :initial-text="initialText"
:initial-note="initialNote"
:instant="instant" :instant="instant"
@posted="onPosted" @posted="onPosted"
@cancel="onCanceled"/> @cancel="onCanceled"/>
@@ -41,6 +42,10 @@ export default Vue.extend({
type: String, type: String,
required: false required: false
}, },
initialNote: {
type: Object,
required: false
},
instant: { instant: {
type: Boolean, type: Boolean,
required: false, required: false,

View File

@@ -17,16 +17,17 @@
<div class="form"> <div class="form">
<mk-note-preview class="preview" v-if="reply" :note="reply"/> <mk-note-preview class="preview" v-if="reply" :note="reply"/>
<mk-note-preview class="preview" v-if="renote" :note="renote"/> <mk-note-preview class="preview" v-if="renote" :note="renote"/>
<div v-if="visibility == 'specified'" class="visibleUsers"> <div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }} <a @click="quoteId = null">[x]</a></div>
<div v-if="visibility === 'specified'" class="visibleUsers">
<span v-for="u in visibleUsers"> <span v-for="u in visibleUsers">
<mk-user-name :user="u"/> <mk-user-name :user="u"/>
<a @click="removeVisibleUser(u)">[x]</a> <a @click="removeVisibleUser(u)">[x]</a>
</span> </span>
<a @click="addVisibleUser">+{{ $t('@.post-form.add-visible-user') }}</a> <a @click="addVisibleUser">+{{ $t('@.post-form.add-visible-user') }}</a>
</div> </div>
<div class="local-only" v-if="localOnly === true">{{ $t('@.post-form.local-only-message') }}</div>
<input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }"> <input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }">
<textarea v-model="text" ref="text" :disabled="posting" :placeholder="placeholder" v-autocomplete="{ model: 'text' }" @paste="onPaste"></textarea> <textarea v-model="text" ref="text" :disabled="posting" :placeholder="placeholder" v-autocomplete="{ model: 'text' }" @paste="onPaste"></textarea>
<div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }}</div>
<x-post-form-attaches class="attaches" :files="files"/> <x-post-form-attaches class="attaches" :files="files"/>
<x-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="onPollUpdate()"/> <x-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="onPollUpdate()"/>
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/> <mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
@@ -140,6 +141,10 @@ export default Vue.extend({
> .preview > .preview
padding 16px padding 16px
> .with-quote
margin 0 0 8px 0
color var(--primary)
> .visibleUsers > .visibleUsers
margin 5px margin 5px
font-size 14px font-size 14px
@@ -148,6 +153,10 @@ export default Vue.extend({
margin-right 16px margin-right 16px
color var(--text) color var(--text)
> .local-only
margin 0 0 8px 0
color var(--primary)
> input > input
z-index 1 z-index 1

View File

@@ -17,7 +17,8 @@
<div class="links"> <div class="links">
<ul> <ul>
<li><router-link to="/" :data-active="$route.name == 'index'"><i><fa icon="home" fixed-width/></i>{{ $t('timeline') }}<i><fa icon="angle-right"/></i></router-link></li> <li><router-link to="/" :data-active="$route.name == 'index'"><i><fa icon="home" fixed-width/></i>{{ $t('timeline') }}<i><fa icon="angle-right"/></i></router-link></li>
<li><p @click="showNotifications = true"><i><fa :icon="['far', 'bell']" fixed-width/></i>{{ $t('notifications') }}<i v-if="hasUnreadNotification" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></p></li> <li v-if="$store.state.device.enableMobileQuickNotificationView"><p @click="showNotifications = true"><i><fa :icon="faBell" fixed-width/></i>{{ $t('notifications') }}<i v-if="hasUnreadNotification" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></p></li>
<li v-else><router-link to="/i/notifications" :data-active="$route.name == 'notifications'"><i><fa :icon="faBell" fixed-width/></i>{{ $t('notifications') }}<i v-if="hasUnreadNotification" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></router-link></li>
<li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'"><i><fa :icon="['far', 'comments']" fixed-width/></i>{{ $t('@.messaging') }}<i v-if="hasUnreadMessagingMessage" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></router-link></li> <li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'"><i><fa :icon="['far', 'comments']" fixed-width/></i>{{ $t('@.messaging') }}<i v-if="hasUnreadMessagingMessage" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></router-link></li>
<li v-if="$store.getters.isSignedIn && ($store.state.i.isLocked || $store.state.i.carefulBot)"><router-link to="/i/follow-requests" :data-active="$route.name == 'follow-requests'"><i><fa :icon="['far', 'envelope']" fixed-width/></i>{{ $t('follow-requests') }}<i v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></router-link></li> <li v-if="$store.getters.isSignedIn && ($store.state.i.isLocked || $store.state.i.carefulBot)"><router-link to="/i/follow-requests" :data-active="$route.name == 'follow-requests'"><i><fa :icon="['far', 'envelope']" fixed-width/></i>{{ $t('follow-requests') }}<i v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount" class="circle"><fa icon="circle"/></i><i><fa icon="angle-right"/></i></router-link></li>
<li><router-link to="/featured" :data-active="$route.name == 'featured'"><i><fa :icon="faNewspaper" fixed-width/></i>{{ $t('@.featured-notes') }}<i><fa icon="angle-right"/></i></router-link></li> <li><router-link to="/featured" :data-active="$route.name == 'featured'"><i><fa :icon="faNewspaper" fixed-width/></i>{{ $t('@.featured-notes') }}<i><fa icon="angle-right"/></i></router-link></li>
@@ -68,7 +69,7 @@ import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import { lang } from '../../../config'; import { lang } from '../../../config';
import { faNewspaper, faHashtag, faHome, faColumns, faUsers } from '@fortawesome/free-solid-svg-icons'; import { faNewspaper, faHashtag, faHome, faColumns, faUsers } from '@fortawesome/free-solid-svg-icons';
import { faMoon, faSun, faStickyNote } from '@fortawesome/free-regular-svg-icons'; import { faMoon, faSun, faStickyNote, faBell } from '@fortawesome/free-regular-svg-icons';
import { search } from '../../../common/scripts/search'; import { search } from '../../../common/scripts/search';
export default Vue.extend({ export default Vue.extend({
@@ -88,7 +89,7 @@ export default Vue.extend({
announcements: [], announcements: [],
searching: false, searching: false,
showNotifications: false, showNotifications: false,
faNewspaper, faHashtag, faMoon, faSun, faHome, faColumns, faStickyNote, faUsers faNewspaper, faHashtag, faMoon, faSun, faHome, faColumns, faStickyNote, faUsers, faBell,
}; };
}, },

View File

@@ -0,0 +1,68 @@
<template>
<mk-ui>
<template #header><fa :icon="faBell"/> {{ $t('notifications') }}</template>
<template #func>
<button @click="filter()"><fa icon="cog"/></button>
</template>
<main>
<mk-notifications @before-init="beforeInit()" @inited="inited()" :type="type === 'all' ? null : type" :wide="true" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }"/>
</main>
</mk-ui>
</template>
<script lang="ts">
import Vue from 'vue';
import { faBell } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../../i18n';
import Progress from '../../../common/scripts/loading';
export default Vue.extend({
i18n: i18n('mobile/views/pages/notifications.vue'),
data() {
return {
type: 'all',
faBell,
};
},
methods: {
beforeInit() {
Progress.start();
},
inited() {
Progress.done();
},
filter() {
this.$root.dialog({
title: this.$t('@.notification-type'),
type: null,
select: {
items: ['all', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'].map(x => ({
value: x, text: this.$t('@.notification-types.' + x)
}))
default: this.type,
},
showCancelButton: true
}).then(({ canceled, result: type }) => {
if (canceled) return;
this.type = type;
});
}
}
});
</script>
<style lang="stylus" scoped>
main > *
overflow hidden
background var(--face)
&.round
border-radius 8px
&.shadow
box-shadow 0 4px 16px rgba(#000, 0.1)
@media (min-width 500px)
box-shadow 0 8px 32px rgba(#000, 0.1)
</style>

View File

@@ -98,12 +98,6 @@ export default Vue.extend({
id: 'g', data: {} id: 'g', data: {}
}]); }]);
} }
this.$watch('$store.getters.mobileHome', () => {
this.$store.dispatch('settings/updateMobileHomeProfile');
}, {
deep: true
});
}, },
mounted() { mounted() {
@@ -128,6 +122,8 @@ export default Vue.extend({
}, },
addWidget() { addWidget() {
if(this.widgetAdderSelected == null) return;
this.$store.commit('addMobileHomeWidget', { this.$store.commit('addMobileHomeWidget', {
name: this.widgetAdderSelected, name: this.widgetAdderSelected,
id: uuid(), id: uuid(),

View File

@@ -75,6 +75,7 @@ const defaultDeviceSettings = {
disableShowingAnimatedImages: false, disableShowingAnimatedImages: false,
expandUsersPhotos: true, expandUsersPhotos: true,
expandUsersActivity: true, expandUsersActivity: true,
enableMobileQuickNotificationView: false,
}; };
export default (os: MiOS) => new Vuex.Store({ export default (os: MiOS) => new Vuex.Store({

View File

@@ -47,6 +47,8 @@ import { UserSecurityKey } from '../models/entities/user-security-key';
import { AttestationChallenge } from '../models/entities/attestation-challenge'; import { AttestationChallenge } from '../models/entities/attestation-challenge';
import { Page } from '../models/entities/page'; import { Page } from '../models/entities/page';
import { PageLike } from '../models/entities/page-like'; import { PageLike } from '../models/entities/page-like';
import { ModerationLog } from '../models/entities/moderation-log';
import { UsedUsername } from '../models/entities/used-username';
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
@@ -99,6 +101,7 @@ export const entities = [
UserGroupInvite, UserGroupInvite,
UserNotePining, UserNotePining,
UserSecurityKey, UserSecurityKey,
UsedUsername,
AttestationChallenge, AttestationChallenge,
Following, Following,
FollowRequest, FollowRequest,
@@ -124,6 +127,7 @@ export const entities = [
RegistrationTicket, RegistrationTicket,
MessagingMessage, MessagingMessage,
Signin, Signin,
ModerationLog,
ReversiGame, ReversiGame,
ReversiMatching, ReversiMatching,
...charts as any ...charts as any

View File

@@ -29,6 +29,7 @@ export async function downloadUrl(url: string, path: string) {
url: new URL(url).href, // https://github.com/syuilo/misskey/issues/2637 url: new URL(url).href, // https://github.com/syuilo/misskey/issues/2637
proxy: config.proxy, proxy: config.proxy,
timeout: 10 * 1000, timeout: 10 * 1000,
forever: true,
headers: { headers: {
'User-Agent': config.userAgent 'User-Agent': config.userAgent
} }

View File

@@ -20,7 +20,7 @@ function formatLocaleString(date: Date, format: string): string {
}); });
} }
function formatDateTimeString(date: Date, format: string): string { export function formatDateTimeString(date: Date, format: string): string {
return format return format
.replace(/yyyy/g, date.getFullYear().toString()) .replace(/yyyy/g, date.getFullYear().toString())
.replace(/yy/g, date.getFullYear().toString().slice(-2)) .replace(/yy/g, date.getFullYear().toString().slice(-2))

View File

@@ -0,0 +1,32 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user';
import { id } from '../id';
@Entity()
export class ModerationLog {
@PrimaryColumn(id())
public id: string;
@Column('timestamp with time zone', {
comment: 'The created date of the ModerationLog.'
})
public createdAt: Date;
@Index()
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE'
})
@JoinColumn()
public user: User | null;
@Column('varchar', {
length: 128,
})
public type: string;
@Column('jsonb')
public info: Record<string, any>;
}

View File

@@ -0,0 +1,20 @@
import { PrimaryColumn, Entity, Column } from 'typeorm';
@Entity()
export class UsedUsername {
@PrimaryColumn('varchar', {
length: 128,
})
public username: string;
@Column('timestamp with time zone')
public createdAt: Date;
constructor(data: Partial<UsedUsername>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View File

@@ -42,6 +42,8 @@ import { UserSecurityKey } from './entities/user-security-key';
import { HashtagRepository } from './repositories/hashtag'; import { HashtagRepository } from './repositories/hashtag';
import { PageRepository } from './repositories/page'; import { PageRepository } from './repositories/page';
import { PageLikeRepository } from './repositories/page-like'; import { PageLikeRepository } from './repositories/page-like';
import { ModerationLogRepository } from './repositories/moderation-logs';
import { UsedUsername } from './entities/used-username';
export const Apps = getCustomRepository(AppRepository); export const Apps = getCustomRepository(AppRepository);
export const Notes = getCustomRepository(NoteRepository); export const Notes = getCustomRepository(NoteRepository);
@@ -63,6 +65,7 @@ export const UserGroups = getCustomRepository(UserGroupRepository);
export const UserGroupJoinings = getRepository(UserGroupJoining); export const UserGroupJoinings = getRepository(UserGroupJoining);
export const UserGroupInvites = getCustomRepository(UserGroupInviteRepository); export const UserGroupInvites = getCustomRepository(UserGroupInviteRepository);
export const UserNotePinings = getRepository(UserNotePining); export const UserNotePinings = getRepository(UserNotePining);
export const UsedUsernames = getRepository(UsedUsername);
export const Followings = getCustomRepository(FollowingRepository); export const Followings = getCustomRepository(FollowingRepository);
export const FollowRequests = getCustomRepository(FollowRequestRepository); export const FollowRequests = getCustomRepository(FollowRequestRepository);
export const Instances = getRepository(Instance); export const Instances = getRepository(Instance);
@@ -86,3 +89,4 @@ export const ReversiMatchings = getCustomRepository(ReversiMatchingRepository);
export const Logs = getRepository(Log); export const Logs = getRepository(Log);
export const Pages = getCustomRepository(PageRepository); export const Pages = getCustomRepository(PageRepository);
export const PageLikes = getCustomRepository(PageLikeRepository); export const PageLikes = getCustomRepository(PageLikeRepository);
export const ModerationLogs = getCustomRepository(ModerationLogRepository);

View File

@@ -14,6 +14,7 @@ export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
return await awaitAll({ return await awaitAll({
id: report.id, id: report.id,
createdAt: report.createdAt, createdAt: report.createdAt,
comment: report.comment,
reporterId: report.reporterId, reporterId: report.reporterId,
userId: report.userId, userId: report.userId,
reporter: Users.pack(report.reporter || report.reporterId, null, { reporter: Users.pack(report.reporter || report.reporterId, null, {

View File

@@ -0,0 +1,31 @@
import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..';
import { ModerationLog } from '../entities/moderation-log';
import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
@EntityRepository(ModerationLog)
export class ModerationLogRepository extends Repository<ModerationLog> {
public async pack(
src: ModerationLog['id'] | ModerationLog,
) {
const log = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await awaitAll({
id: log.id,
createdAt: log.createdAt,
type: log.type,
info: log.info,
userId: log.userId,
user: Users.pack(log.user || log.userId, null, {
detail: true
}),
});
}
public packMany(
reports: any[],
) {
return Promise.all(reports.map(x => this.pack(x)));
}
}

View File

@@ -148,6 +148,7 @@ export class UserRepository extends Repository<User> {
description: profile!.description, description: profile!.description,
location: profile!.location, location: profile!.location,
birthday: profile!.birthday, birthday: profile!.birthday,
fields: profile!.fields,
followersCount: user.followersCount, followersCount: user.followersCount,
followingCount: user.followingCount, followingCount: user.followingCount,
notesCount: user.notesCount, notesCount: user.notesCount,

31
src/prelude/time.ts Normal file
View File

@@ -0,0 +1,31 @@
const dateTimeIntervals = {
'days': 86400000,
'hours': 3600000,
};
export function DateUTC(time: number[]): Date {
const r = new Date(0);
r.setUTCFullYear(time[0], time[1], time[2]);
if (time[3]) r.setUTCHours(time[3], ...time.slice(4));
return r;
}
export function isTimeSame(a: Date, b: Date): boolean {
return (a.getTime() - b.getTime()) === 0;
}
export function isTimeBefore(a: Date, b: Date): boolean {
return (a.getTime() - b.getTime()) < 0;
}
export function isTimeAfter(a: Date, b: Date): boolean {
return (a.getTime() - b.getTime()) > 0;
}
export function addTimespan(x: Date, value: number, span: keyof typeof dateTimeIntervals): Date {
return new Date(x.getTime() + (value * dateTimeIntervals[span]));
}
export function subtractTimespan(x: Date, value: number, span: keyof typeof dateTimeIntervals): Date {
return new Date(x.getTime() - (value * dateTimeIntervals[span]));
}

View File

@@ -215,8 +215,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
const apEmojis = emojis.map(emoji => emoji.name); const apEmojis = emojis.map(emoji => emoji.name);
const questionUri = note._misskey_question; const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined);
const poll = await extractPollFromQuestion(note._misskey_question || note, resolver).catch(() => undefined);
// ユーザーの情報が古かったらついでに更新しておく // ユーザーの情報が古かったらついでに更新しておく
if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
@@ -239,7 +238,6 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
apMentions, apMentions,
apHashtags, apHashtags,
apEmojis, apEmojis,
questionUri,
poll, poll,
uri: note.id uri: note.id
}, silent); }, silent);
@@ -304,6 +302,7 @@ export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]
if ((tag.updated != null && exists.updatedAt == null) if ((tag.updated != null && exists.updatedAt == null)
|| (tag.id != null && exists.uri == null) || (tag.id != null && exists.uri == null)
|| (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt)
|| (tag.icon!.url !== exists.url)
) { ) {
await Emojis.update({ await Emojis.update({
host, host,
@@ -311,7 +310,7 @@ export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]
}, { }, {
uri: tag.id, uri: tag.id,
url: tag.icon!.url, url: tag.icon!.url,
updatedAt: new Date(tag.updated!), updatedAt: new Date(),
}); });
return await Emojis.findOne({ return await Emojis.findOne({
@@ -331,7 +330,7 @@ export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]
name, name,
uri: tag.id, uri: tag.id,
url: tag.icon!.url, url: tag.icon!.url,
updatedAt: tag.updated ? new Date(tag.updated) : undefined, updatedAt: new Date(),
aliases: [] aliases: []
} as Partial<Emoji>); } as Partial<Emoji>);
})); }));

View File

@@ -15,7 +15,7 @@ export async function extractPollFromQuestion(source: string | IObject, resolver
} }
const multiple = !question.oneOf; const multiple = !question.oneOf;
const expiresAt = question.endTime ? new Date(question.endTime) : null; const expiresAt = question.endTime ? new Date(question.endTime) : question.closed ? new Date(question.closed) : null;
if (multiple && !question.anyOf) { if (multiple && !question.anyOf) {
throw new Error('invalid question'); throw new Error('invalid question');

View File

@@ -90,14 +90,11 @@ export default async function renderNote(note: Note, dive = true): Promise<any>
poll = await Polls.findOne({ noteId: note.id }); poll = await Polls.findOne({ noteId: note.id });
} }
let question: string | undefined;
if (poll) { if (poll) {
if (text == null) text = ''; if (text == null) text = '';
const url = `${config.url}/notes/${note.id}`; const url = `${config.url}/notes/${note.id}`;
// TODO: i18n // TODO: i18n
text += `\n[リモートで結果を表示](${url})`; text += `\n[リモートで結果を表示](${url})`;
question = `${config.url}/questions/${note.id}`;
} }
let apText = text; let apText = text;
@@ -156,7 +153,6 @@ export default async function renderNote(note: Note, dive = true): Promise<any>
content, content,
_misskey_content: text, _misskey_content: text,
_misskey_quote: quote, _misskey_quote: quote,
_misskey_question: question,
published: note.createdAt.toISOString(), published: note.createdAt.toISOString(),
to, to,
cc, cc,

Some files were not shown because too many files have changed in this diff Show More