Compare commits
	
		
			185 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					95ce8dce3d | ||
| 
						 | 
					0b5eec4ca8 | ||
| 
						 | 
					6d9716f90e | ||
| 
						 | 
					aa31061d90 | ||
| 
						 | 
					acc7797dff | ||
| 
						 | 
					7959196dc7 | ||
| 
						 | 
					c6ff6939a5 | ||
| 
						 | 
					769960f29e | ||
| 
						 | 
					d92e9759f3 | ||
| 
						 | 
					bf7e19b288 | ||
| 
						 | 
					98954cd6d4 | ||
| 
						 | 
					538bb978ed | ||
| 
						 | 
					10232c5866 | ||
| 
						 | 
					5cd6a0db16 | ||
| 
						 | 
					ff0a05a2d6 | ||
| 
						 | 
					e34b264af2 | ||
| 
						 | 
					00d79487cd | ||
| 
						 | 
					3cace734c7 | ||
| 
						 | 
					f428372869 | ||
| 
						 | 
					5dd2feba9b | ||
| 
						 | 
					a1b026239e | ||
| 
						 | 
					40735ce76b | ||
| 
						 | 
					4a00c13b33 | ||
| 
						 | 
					8e359d54bd | ||
| 
						 | 
					2448bf4e4e | ||
| 
						 | 
					91e0fc8c62 | ||
| 
						 | 
					b4f86feddb | ||
| 
						 | 
					ccf8e44acc | ||
| 
						 | 
					451acb77df | ||
| 
						 | 
					e2c6227f47 | ||
| 
						 | 
					ebd1c877ad | ||
| 
						 | 
					498094b3c7 | ||
| 
						 | 
					1cc183ecdb | ||
| 
						 | 
					e8948452fd | ||
| 
						 | 
					ade7e62836 | ||
| 
						 | 
					395cfa6108 | ||
| 
						 | 
					b5ff2abdb9 | ||
| 
						 | 
					229e85b2c5 | ||
| 
						 | 
					37058e3480 | ||
| 
						 | 
					a1b82e9723 | ||
| 
						 | 
					db943df0c8 | ||
| 
						 | 
					ff8d300ea8 | ||
| 
						 | 
					8b490b9b94 | ||
| 
						 | 
					f83f8631ac | ||
| 
						 | 
					1915ccabdd | ||
| 
						 | 
					6fea2f52f1 | ||
| 
						 | 
					f77eaaa08a | ||
| 
						 | 
					7c5bc03492 | ||
| 
						 | 
					72a1af6cd4 | ||
| 
						 | 
					4bce6f14f3 | ||
| 
						 | 
					a38ce86f87 | ||
| 
						 | 
					f539491502 | ||
| 
						 | 
					d279f8e9ff | ||
| 
						 | 
					eaec936fa6 | ||
| 
						 | 
					a0735b0e7a | ||
| 
						 | 
					5b039a1bee | ||
| 
						 | 
					921609cab1 | ||
| 
						 | 
					199573ccee | ||
| 
						 | 
					977200b7cd | ||
| 
						 | 
					6abff253ea | ||
| 
						 | 
					ba64de334a | ||
| 
						 | 
					dc1d7fa9d7 | ||
| 
						 | 
					f42665d4bc | ||
| 
						 | 
					a5eb19c878 | ||
| 
						 | 
					60fa8e13d6 | ||
| 
						 | 
					ecbaea463b | ||
| 
						 | 
					814ddeb436 | ||
| 
						 | 
					d6466106e8 | ||
| 
						 | 
					633f5384f9 | ||
| 
						 | 
					fa7989772c | ||
| 
						 | 
					0e395612a6 | ||
| 
						 | 
					fb3f52f3ad | ||
| 
						 | 
					ba11c71d65 | ||
| 
						 | 
					bdc3081167 | ||
| 
						 | 
					430efcf1b9 | ||
| 
						 | 
					996450dd7c | ||
| 
						 | 
					fa779f0417 | ||
| 
						 | 
					25cec6d28a | ||
| 
						 | 
					c5f8403cea | ||
| 
						 | 
					a9ae9a65c8 | ||
| 
						 | 
					3698c679e2 | ||
| 
						 | 
					881df20f1b | ||
| 
						 | 
					7d269c0441 | ||
| 
						 | 
					ba38f64353 | ||
| 
						 | 
					db3ae303cb | ||
| 
						 | 
					66f3a155e6 | ||
| 
						 | 
					639b483e6c | ||
| 
						 | 
					09843a409b | ||
| 
						 | 
					e894ed5a8b | ||
| 
						 | 
					d7808299fd | ||
| 
						 | 
					f92e0c16d2 | ||
| 
						 | 
					d94b3757be | ||
| 
						 | 
					13e822cba6 | ||
| 
						 | 
					c57bf87f52 | ||
| 
						 | 
					99fbd60265 | ||
| 
						 | 
					ea9b48db3c | ||
| 
						 | 
					c145c994a9 | ||
| 
						 | 
					d033998b56 | ||
| 
						 | 
					3136c714bf | ||
| 
						 | 
					c0ee134f19 | ||
| 
						 | 
					d15ebe5732 | ||
| 
						 | 
					ef630195fa | ||
| 
						 | 
					e31921151e | ||
| 
						 | 
					f94992abbe | ||
| 
						 | 
					b00060c09c | ||
| 
						 | 
					f6217d96d2 | ||
| 
						 | 
					3a6947c7ed | ||
| 
						 | 
					0fb528ddf8 | ||
| 
						 | 
					14c03f226d | ||
| 
						 | 
					4f0d844b43 | ||
| 
						 | 
					b93395fc4c | ||
| 
						 | 
					34eacb7e2d | ||
| 
						 | 
					0177023ead | ||
| 
						 | 
					a4678e45de | ||
| 
						 | 
					f24869625e | ||
| 
						 | 
					01beb705a2 | ||
| 
						 | 
					ce28c70c35 | ||
| 
						 | 
					5e0f5c31e7 | ||
| 
						 | 
					b28dd4be52 | ||
| 
						 | 
					291beb45fc | ||
| 
						 | 
					ffb345ccb5 | ||
| 
						 | 
					d2abe2cd81 | ||
| 
						 | 
					acffc3e522 | ||
| 
						 | 
					0962e62b8c | ||
| 
						 | 
					91ebd310b7 | ||
| 
						 | 
					2974c74b4e | ||
| 
						 | 
					3d24112d2d | ||
| 
						 | 
					4a977cd523 | ||
| 
						 | 
					4b1886990f | ||
| 
						 | 
					f3499b787c | ||
| 
						 | 
					5209a584a2 | ||
| 
						 | 
					57a63d38aa | ||
| 
						 | 
					3efffbcf22 | ||
| 
						 | 
					15eaebe522 | ||
| 
						 | 
					eee98358ac | ||
| 
						 | 
					795fc5e7bc | ||
| 
						 | 
					70ac668474 | ||
| 
						 | 
					1004e0d6e8 | ||
| 
						 | 
					52aa64fcb6 | ||
| 
						 | 
					7860d97a10 | ||
| 
						 | 
					409b37b271 | ||
| 
						 | 
					ad9b9964fa | ||
| 
						 | 
					d2b5276f43 | ||
| 
						 | 
					7204e2a84c | ||
| 
						 | 
					1377fa3332 | ||
| 
						 | 
					bf087bfccf | ||
| 
						 | 
					e846e3d571 | ||
| 
						 | 
					d646e62888 | ||
| 
						 | 
					c008154d18 | ||
| 
						 | 
					29bd4de26a | ||
| 
						 | 
					7559b8da6c | ||
| 
						 | 
					35218e84fc | ||
| 
						 | 
					2c135fa2f6 | ||
| 
						 | 
					21da6bd047 | ||
| 
						 | 
					f1d65a66b4 | ||
| 
						 | 
					63e2dbbb0d | ||
| 
						 | 
					a228c522f1 | ||
| 
						 | 
					a951c337b8 | ||
| 
						 | 
					db3efb3791 | ||
| 
						 | 
					5f9a9867eb | ||
| 
						 | 
					059a8e07d2 | ||
| 
						 | 
					cf82f56e66 | ||
| 
						 | 
					2778bd14d4 | ||
| 
						 | 
					5b0446739c | ||
| 
						 | 
					55f235d0ac | ||
| 
						 | 
					4ec44c68e9 | ||
| 
						 | 
					e6952d499a | ||
| 
						 | 
					e0b82f827b | ||
| 
						 | 
					0bccb17e82 | ||
| 
						 | 
					b251b8c6a9 | ||
| 
						 | 
					c2a62f632b | ||
| 
						 | 
					37b5afa1a3 | ||
| 
						 | 
					b80d0a3b12 | ||
| 
						 | 
					9f60688d37 | ||
| 
						 | 
					ca0ea9e57c | ||
| 
						 | 
					a77a7e8112 | ||
| 
						 | 
					b26ea2edc0 | ||
| 
						 | 
					02b99dfd76 | ||
| 
						 | 
					af02b0f115 | ||
| 
						 | 
					9b3c379678 | ||
| 
						 | 
					a423fd7695 | ||
| 
						 | 
					de6e1d8c9b | ||
| 
						 | 
					d9db3e8629 | ||
| 
						 | 
					ac1c81b7d6 | ||
| 
						 | 
					49b2eec534 | 
@@ -1,18 +1,19 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
# BEARER_TOKEN=
 | 
			
		||||
# CAMPAIGN_ID=
 | 
			
		||||
# GITHUB_TOKEN=
 | 
			
		||||
# HEAD='acid-chicken:patch-autogen'
 | 
			
		||||
# REPO='syuilo/misskey'
 | 
			
		||||
test "$(curl -LSs -w '\n' -- "https://api.github.com/repos/$REPO/pulls?access_token=$GITHUB_TOKEN" | jq -r '.[].head.label' | grep $HEAD)" && exit 1
 | 
			
		||||
# __MISSKEY_BEARER_TOKEN=
 | 
			
		||||
# __MISSKEY_CAMPAIGN_ID=
 | 
			
		||||
# __MISSKEY_GITHUB_TOKEN=
 | 
			
		||||
# __MISSKEY_HEAD=acid-chicken:patch-autogen
 | 
			
		||||
# __MISSKEY_REPO=syuilo/misskey
 | 
			
		||||
# __MISSKEY_BRANCH=develop
 | 
			
		||||
test "$(curl -LSs -w '\n' -- "https://api.github.com/repos/$REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" | jq -r '.[].head.label' | grep $__MISSKEY_HEAD)" && exit 1
 | 
			
		||||
cd "$(dirname $0)/.." && \
 | 
			
		||||
touch null.cache && \
 | 
			
		||||
rm *.cache && \
 | 
			
		||||
git checkout master && \
 | 
			
		||||
git pull origin master && \
 | 
			
		||||
git pull upstream master && \
 | 
			
		||||
git checkout $__MISSKEY_BRANCH && \
 | 
			
		||||
git pull origin $__MISSKEY_BRANCH && \
 | 
			
		||||
git pull upstream $__MISSKEY_BRANCH && \
 | 
			
		||||
git stash && \
 | 
			
		||||
git rebase -f upstream/master && \
 | 
			
		||||
git rebase -f upstream/$__MISSKEY_BRANCH && \
 | 
			
		||||
git branch patch-autogen && \
 | 
			
		||||
git checkout patch-autogen && \
 | 
			
		||||
git reset --hard HEAD || \
 | 
			
		||||
@@ -20,12 +21,12 @@ exit 1
 | 
			
		||||
touch patreon.md.cache && \
 | 
			
		||||
rm patreon.md.cache && \
 | 
			
		||||
echo '<!-- PATREON_START -->' > patreon.md.cache && \
 | 
			
		||||
URL="https://www.patreon.com/api/oauth2/v2/campaigns/$CAMPAIGN_ID/members?include=currently_entitled_tiers,user&fields%5Btier%5D=title&fields%5Buser%5D=full_name,thumb_url,url,hide_pledges"
 | 
			
		||||
url="https://www.patreon.com/api/oauth2/v2/campaigns/$__MISSKEY_CAMPAIGN_ID/members?include=currently_entitled_tiers,user&fields%5Btier%5D=title&fields%5Buser%5D=full_name,thumb_url,url,hide_pledges"
 | 
			
		||||
while :
 | 
			
		||||
 do
 | 
			
		||||
  touch patreon.raw.cache && \
 | 
			
		||||
  rm patreon.raw.cache && \
 | 
			
		||||
  curl -LSs -w '\n' -H "Authorization: Bearer $BEARER_TOKEN" -- $URL > patreon.raw.cache && \
 | 
			
		||||
  curl -LSs -w '\n' -H "Authorization: Bearer $__MISSKEY_BEARER_TOKEN" -- $url > patreon.raw.cache && \
 | 
			
		||||
  touch patreon.cache && \
 | 
			
		||||
  rm patreon.cache && \
 | 
			
		||||
  cat patreon.raw.cache | \
 | 
			
		||||
@@ -42,31 +43,31 @@ while :
 | 
			
		||||
  xargs -I% echo '<td><a href="%</a></td>' >> patreon.md.cache && \
 | 
			
		||||
  echo '</tr></table>' >> patreon.md.cache || \
 | 
			
		||||
  exit 1
 | 
			
		||||
  NEW_URL="$(cat patreon.raw.cache | jq -r '.links.next')"
 | 
			
		||||
  test "$NEW_URL" = 'null' && \
 | 
			
		||||
  new_url="$(cat patreon.raw.cache | jq -r '.links.next')"
 | 
			
		||||
  test "$new_url" = 'null' && \
 | 
			
		||||
  break || \
 | 
			
		||||
  URL="$NEW_URL"
 | 
			
		||||
  URL="$url"
 | 
			
		||||
done
 | 
			
		||||
IGNORE= && \
 | 
			
		||||
ignore= && \
 | 
			
		||||
echo -e "\n**Last updated:** $(date -uR | sed 's/\+0000/UTC/')\n<!-- PATREON_END -->" >> patreon.md.cache && \
 | 
			
		||||
touch README.md && \
 | 
			
		||||
touch .autogen/README.md && \
 | 
			
		||||
rm .autogen/README.md && \
 | 
			
		||||
mv README.md .autogen/README.md && \
 | 
			
		||||
cat .autogen/README.md | while IFS= read LINE;
 | 
			
		||||
cat .autogen/README.md | while IFS= read line;
 | 
			
		||||
 do
 | 
			
		||||
  if [[ -z "$IGNORE" ]]
 | 
			
		||||
  if [[ -z "$ignore" ]]
 | 
			
		||||
   then
 | 
			
		||||
    if [[ "$LINE" = '<!-- PATREON_START -->' ]]
 | 
			
		||||
    if [[ "$line" = '<!-- PATREON_START -->' ]]
 | 
			
		||||
     then
 | 
			
		||||
      IGNORE='PATREON_INSIDE'
 | 
			
		||||
      ignore='PATREON_INSIDE'
 | 
			
		||||
     else
 | 
			
		||||
      echo "$LINE" >> README.md
 | 
			
		||||
      echo "$line" >> README.md
 | 
			
		||||
    fi
 | 
			
		||||
   else
 | 
			
		||||
    if [[ "$LINE" = '<!-- PATREON_END -->' ]]
 | 
			
		||||
     then
 | 
			
		||||
      IGNORE=
 | 
			
		||||
      ignore=
 | 
			
		||||
      cat patreon.md.cache >> README.md
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
@@ -80,7 +81,7 @@ test 4 -lt $(cat diff.cache | wc -l) && \
 | 
			
		||||
git add README.md && \
 | 
			
		||||
git commit -m 'Update README.md [AUTOGEN]' && \
 | 
			
		||||
git push -f origin patch-autogen && \
 | 
			
		||||
curl -LSs -w '\n' -X POST -d '{"title":"[AUTOMATED] Update README.md","body":"*This pull request was created by a tool.*","head":"'$HEAD'","base":"master"}' -- "https://api.github.com/repos/$REPO/pulls?access_token=$GITHUB_TOKEN"
 | 
			
		||||
curl -LSs -w '\n' -X POST -d '{"title":"[AUTOMATED] Update README.md","body":"*This pull request was created by a tool.*","head":"'$__MISSKEY_HEAD'","base":"'$__MISSKEY_BRANCH'"}' -- "https://api.github.com/repos/$__MISSKEY_REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN"
 | 
			
		||||
git stash
 | 
			
		||||
git checkout master
 | 
			
		||||
git checkout $__MISSKEY_BRANCH
 | 
			
		||||
git branch -D patch-autogen
 | 
			
		||||
 
 | 
			
		||||
@@ -47,13 +47,13 @@ Please run `node cli/migration/5.0.0` before launch.
 | 
			
		||||
 | 
			
		||||
オセロがリバーシに変更されました。
 | 
			
		||||
 | 
			
		||||
Othello is now Reversi.
 | 
			
		||||
Othello is rename to Reversi.
 | 
			
		||||
 | 
			
		||||
### Migration
 | 
			
		||||
 | 
			
		||||
MongoDBの、`othelloGames`と`othelloMatchings`コレクションをそれぞれ`reversiGames`と`reversiMatchings`にリネームしてください。
 | 
			
		||||
 | 
			
		||||
You need to rename `othelloGames` and `othelloMatchings` MongoDB collections to `reversiGames` and `reversiMatchings`.
 | 
			
		||||
Please rename `othelloGames` and `othelloMatchings` MongoDB collections to `reversiGames` and `reversiMatchings` respectively.
 | 
			
		||||
 | 
			
		||||
3.0.0
 | 
			
		||||
-----
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,27 @@
 | 
			
		||||
# Contribution guide
 | 
			
		||||
:v: Misskeyへの貢献ありがとうございます。 :v:
 | 
			
		||||
:v: Thanks for your contributions :v:
 | 
			
		||||
 | 
			
		||||
## Issueの報告
 | 
			
		||||
新機能の提案や不具合の報告は https://github.com/syuilo/misskey/issues で管理しています。
 | 
			
		||||
Issueを作成する前に、既に同じIssueが作成されていないかご確認ください。
 | 
			
		||||
もし既にIssueが作成されている場合は、既存のIssueにコメントをしたりリアクションをするようお願いします。
 | 
			
		||||
## Issues
 | 
			
		||||
Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues .
 | 
			
		||||
Before creating a new issue, please search existing issues to avoid duplication.
 | 
			
		||||
If you find the existing issue, please add your reaction or comment to the issue.
 | 
			
		||||
 | 
			
		||||
## Issueの解決
 | 
			
		||||
[pr-welcomeのラベルがついているIssue](https://github.com/syuilo/misskey/labels/pr-welcome)
 | 
			
		||||
の解決を目的としたPull Requestを作成してくださると非常にありがたいです。
 | 
			
		||||
## Internationalization (i18n)
 | 
			
		||||
Please see [Translation guide](./docs/translate.en.md).
 | 
			
		||||
 | 
			
		||||
## 翻訳の改善
 | 
			
		||||
ソースコード中の `%i18n:id%` という形の文字列は、言語ファイルの対応するテキストに置換されます。
 | 
			
		||||
言語ファイルは /locales ディレクトリに存在します。
 | 
			
		||||
## Localization (l10n)
 | 
			
		||||
Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
 | 
			
		||||
 | 
			
		||||
## ドキュメントの編集
 | 
			
		||||
現在Misskeyはドキュメントが大きく不足しています。
 | 
			
		||||
ドキュメントは /docs ディレクトリに存在します。
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## テストの追加
 | 
			
		||||
現在Misskeyはテストが大きく不足しています。
 | 
			
		||||
テストコードは /test ディレクトリに存在します。
 | 
			
		||||
## Documentation
 | 
			
		||||
* Documents for contributors are located in `/docs`.
 | 
			
		||||
* Documents for instance admins are located in `/docs`.
 | 
			
		||||
* Documents for end users are located in `src/docs`.
 | 
			
		||||
 | 
			
		||||
## 自動テスト及び自動リリース
 | 
			
		||||
Travis CIで行っています。
 | 
			
		||||
設定ファイルは /.travis に存在します。
 | 
			
		||||
## Test
 | 
			
		||||
* Test codes are located in `/test`.
 | 
			
		||||
 | 
			
		||||
## Continuous integration
 | 
			
		||||
Misskey uses Travis for automated test.
 | 
			
		||||
Configuration files are located in `/.travis`.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								README.md
									
									
									
									
									
								
							@@ -24,7 +24,7 @@ Why don't you take a short break from the hustle and bustle of the city, and div
 | 
			
		||||
* Reactions
 | 
			
		||||
* User lists
 | 
			
		||||
* Customizable column view (called MisskeyDeck)
 | 
			
		||||
  * and widgets!
 | 
			
		||||
* Customizable widgets
 | 
			
		||||
* Private messages
 | 
			
		||||
* ActivityPub support
 | 
			
		||||
 | 
			
		||||
@@ -32,38 +32,25 @@ and more! You can see it with your own eyes at [misskey.xyz](https://misskey.xyz
 | 
			
		||||
 | 
			
		||||
:package: Create your own instance
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
If you want to run your own instance of Misskey,
 | 
			
		||||
please see [Setup and installation guide](./docs/setup.en.md).
 | 
			
		||||
Please see [Setup and installation guide](./docs/setup.en.md).
 | 
			
		||||
 | 
			
		||||
:wrench: Contribute
 | 
			
		||||
:wrench: Contribution
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
**[PR](https://github.com/syuilo/misskey/pulls)s welcome!**
 | 
			
		||||
 | 
			
		||||
### i18n
 | 
			
		||||
 | 
			
		||||
Please see [Translation guide](./docs/translate.en.md).
 | 
			
		||||
 | 
			
		||||
### l10n
 | 
			
		||||
 | 
			
		||||
Misskey is using Crowdin for l10n.
 | 
			
		||||
 | 
			
		||||
[](https://crowdin.com/project/misskey)
 | 
			
		||||
Please see [Contribution guide](./CONTRIBUTING.md).
 | 
			
		||||
 | 
			
		||||
:heart: Backers & Sponsors
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
<!-- PATREON_START -->
 | 
			
		||||
<table><tr>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12378075/0156f769e20f412594fa6b87d85fe228/1?token-time=2145916800&token-hash=IsIJRUXszzoD6-7pDnRY8I05T9nSznc4GTaxj7C9SwU%3D" alt="39ff"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1?token-time=2145916800&token-hash=d6P5MWHHsCMxUuBAEPAoVc5wLUR19mIhqAq7Ma9h9rI%3D" alt="ne_moni"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/1?token-time=2145916800&token-hash=f03BFb4S2FUx9YEt87TnEmifb4h33OywGBW2akQVtQY%3D" alt="Melilot"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/1?token-time=2145916800&token-hash=DVrSdOqQq2dufgNgWZ3XMnEtl_ZAktr8Lhf2tbHKtoA%3D" alt="Axella"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/2?token-time=2145916800&token-hash=rwZ8qvbm_kpA4ib3kc07tVKupXeySpY5ATQFGxfL9v0%3D" alt="Axella"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/3384329/8b713330cb27404ea6e9fac50ff96efe/1?token-time=2145916800&token-hash=0eu4-m1gTWA9PhptVZt6rdKcusqcD7RB87rJT23VVFI%3D" alt="べすれい"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D" alt="gutfuckllc"></td>
 | 
			
		||||
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
 | 
			
		||||
</tr><tr>
 | 
			
		||||
<td><a href="https://www.patreon.com/user?u=12378075">39ff</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/user?u=12731202">negao</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>
 | 
			
		||||
@@ -78,20 +65,16 @@ Misskey is using Crowdin for l10n.
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12931605/ead494101f364dffa90efe49e36fb494/1?token-time=2145916800&token-hash=NzSFPjIlodXyv41rwK61aZWVZWfI4surJaNj8vWKvqM%3D" alt="Reiju"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=UERBN4OyP7Nh5XwwdDg0N0IE5cD6_qUQMO81Z5Wizso%3D" alt="Hiratake"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D" alt="dansup"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4950409/28e7d016209243759d9316be2e21381d/2?token-time=2145916800&token-hash=LuEaDkchH3GQWUcTOhBQ8xfKQYF0s5FjlZRd7Yduia8%3D" alt="mikan54951"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D" alt="Takashi Shibuya"></td>
 | 
			
		||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12959468/c249e15aebec4424b5c0f427173671b6/1?token-time=2145916800&token-hash=lubpCEdxAkxPlpR2O6bvZ7BIh8Q4nGf-U_mE1qpjVAQ%3D" alt="fujishan"></td>
 | 
			
		||||
</tr><tr>
 | 
			
		||||
<td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/user?u=12931605">Reiju</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/user?u=4950409">mikan54951</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
 | 
			
		||||
<td><a href="https://www.patreon.com/fujishan">fujishan</a></td>
 | 
			
		||||
</tr></table>
 | 
			
		||||
 | 
			
		||||
**Last updated:** Sun, 26 Aug 2018 08:55:06 UTC
 | 
			
		||||
**Last updated:** Sun, 02 Sep 2018 05:30:06 UTC
 | 
			
		||||
<!-- PATREON_END -->
 | 
			
		||||
 | 
			
		||||
:four_leaf_clover: Copyright
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ Please visit https://www.google.com/recaptcha/intro/ and generate keys.
 | 
			
		||||
 | 
			
		||||
*(optional)* Generating VAPID keys
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
If you want to enable ServiceWroker, you need to generate VAPID keys:
 | 
			
		||||
If you want to enable ServiceWorker, you need to generate VAPID keys:
 | 
			
		||||
Unless you have set your global node_modules location elsewhere, you need to run this in root.
 | 
			
		||||
 | 
			
		||||
``` shell
 | 
			
		||||
@@ -131,6 +131,7 @@ You can check if the service is running with `systemctl status misskey`.
 | 
			
		||||
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
 | 
			
		||||
3. `npm install`
 | 
			
		||||
4. `npm run build`
 | 
			
		||||
5. Check [ChangeLog](../CHANGELOG.md) for migration information
 | 
			
		||||
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -120,6 +120,7 @@ WantedBy=multi-user.target
 | 
			
		||||
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
 | 
			
		||||
3. `npm install`
 | 
			
		||||
4. `npm run build`
 | 
			
		||||
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
 | 
			
		||||
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
# **Please DO NOT edit these files** except `ja-JP.yml`.
 | 
			
		||||
# **DO NOT edit locale files** except `ja-JP.yml`.
 | 
			
		||||
 | 
			
		||||
If you want to...
 | 
			
		||||
* i18n ... please see [Translation guide](../docs/translate.en.md).
 | 
			
		||||
* l10n ... please visit https://crowdin.com/project/misskey
 | 
			
		||||
Please see [Contribution guide](../CONTRIBUTING.md) for more information.
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
    my-turn: "あなたのターンです"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "削除"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
    my-turn: "あなたのターンです"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "Entwickler"
 | 
			
		||||
  feedback: "Feedback"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "Diese Anmerkung favorisieren"
 | 
			
		||||
  pin: "An die Profilseite pinnen"
 | 
			
		||||
  delete: "Löschen"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "Direkt"
 | 
			
		||||
  specified-desc: "Poste nur für bestimmte Benutzer"
 | 
			
		||||
  private: "Privat"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "Laden"
 | 
			
		||||
  no-broadcasts: "Keine Broadcasts"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "Serverinformationen"
 | 
			
		||||
  toggle: "Sicht umschalten"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -84,9 +84,10 @@ common:
 | 
			
		||||
  my-token-regenerated: "Your token has been regenerated, so you will be signed out."
 | 
			
		||||
  i-like-sushi: "I prefer sushi rather than pudding"
 | 
			
		||||
  show-reversi-board-labels: "Show row and column labels in Reversi"
 | 
			
		||||
  use-contrast-reversi-stones: "Make the stone color clear"
 | 
			
		||||
  use-contrast-reversi-stones: "Make the stone color clear in reversi"
 | 
			
		||||
  verified-user: "Verified account"
 | 
			
		||||
  disable-animated-mfm: "Disable animated texts in a post"
 | 
			
		||||
  do-not-use-in-production: 'As this is for development, do not use this in production.'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Draw"
 | 
			
		||||
    my-turn: "Your turn"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "Developers"
 | 
			
		||||
  feedback: "Feedback"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "Details"
 | 
			
		||||
  copy-link: "Copy link"
 | 
			
		||||
  favorite: "Favorite this note"
 | 
			
		||||
  pin: "Pin to your profile"
 | 
			
		||||
  delete: "Delete"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "Direct"
 | 
			
		||||
  specified-desc: "Post to specified users only"
 | 
			
		||||
  private: "Private"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{} users mentioned"
 | 
			
		||||
  empty: "No popular hashtag trends"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "Fetching"
 | 
			
		||||
  no-broadcasts: "No announcements"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "Toggle views"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "Hashtags"
 | 
			
		||||
  count: "{} users mentioned"
 | 
			
		||||
  empty: "No popular hashtag trends"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "Server info"
 | 
			
		||||
  toggle: "Toggle views"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "Logging in..."
 | 
			
		||||
  signup-button: "Sign up"
 | 
			
		||||
  timeline: "Timeline"
 | 
			
		||||
  announcements: "Announcements"
 | 
			
		||||
  photos: "Recent uploaded"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey storage"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "Post design"
 | 
			
		||||
  post-style-standard: "Standard"
 | 
			
		||||
  post-style-smart: "Smart"
 | 
			
		||||
  notification-position: "Notification style"
 | 
			
		||||
  notification-position-bottom: "Bottom"
 | 
			
		||||
  notification-position-top: "Top"
 | 
			
		||||
  behavior: "Behavior"
 | 
			
		||||
  fetch-on-scroll: "Endless loading on scroll"
 | 
			
		||||
  disable-via-mobile: "Don't mark the post as 'from mobile'"
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ common:
 | 
			
		||||
    warning: "<strong>Misskey no tiene anuncios publicitarios.</strong> Sin embargo, algunas características podrían no estar disponibles si el bloqueador de publicidad está habilitado."
 | 
			
		||||
  application-authorization: "Autorizaciones de la aplicación."
 | 
			
		||||
  close: "Cerrar"
 | 
			
		||||
  do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
 | 
			
		||||
  do-not-copy-paste: "Por favor no copies código aquí. Tu cuenta puede resultar comprometida."
 | 
			
		||||
  got-it: "¡Listo!"
 | 
			
		||||
  customization-tips:
 | 
			
		||||
    title: "Consejos de personalización"
 | 
			
		||||
@@ -58,7 +58,7 @@ common:
 | 
			
		||||
    friday: "Viernes"
 | 
			
		||||
    saturday: "Sábado"
 | 
			
		||||
  reactions:
 | 
			
		||||
    like: "いいね"
 | 
			
		||||
    like: "Me gusta"
 | 
			
		||||
    love: "amor"
 | 
			
		||||
    laugh: "risa"
 | 
			
		||||
    hmm: "hmm"
 | 
			
		||||
@@ -84,9 +84,10 @@ common:
 | 
			
		||||
  my-token-regenerated: "Tu token se ha regenerado vas a ser desconectado."
 | 
			
		||||
  i-like-sushi: "Prefiero sushi a pudín"
 | 
			
		||||
  show-reversi-board-labels: "Mostrar etiquetas de filas y columnas en Reversi"
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  use-contrast-reversi-stones: "Hacer el color de la piedra claro en Reversi"
 | 
			
		||||
  verified-user: "Cuenta verificada"
 | 
			
		||||
  disable-animated-mfm: "Desactivar texto animado en una publicación"
 | 
			
		||||
  do-not-use-in-production: 'Esto está en desarrollo, no usarlo para producción.'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Empatado"
 | 
			
		||||
    my-turn: "Mi turno"
 | 
			
		||||
@@ -170,9 +171,9 @@ common/views/components/games/reversi/reversi.vue:
 | 
			
		||||
common/views/components/games/reversi/reversi.game.vue:
 | 
			
		||||
  surrender: "Rendirse"
 | 
			
		||||
  surrendered: "Por rendirse"
 | 
			
		||||
  is-llotheo: "石の少ない方が勝ち(ロセオ)"
 | 
			
		||||
  looped-map: "ループマップ"
 | 
			
		||||
  can-put-everywhere: "どこでも置けるモード"
 | 
			
		||||
  is-llotheo: "El último gana (Llotheo)"
 | 
			
		||||
  looped-map: "Mapa en bucle"
 | 
			
		||||
  can-put-everywhere: "Puedes colocar donde quieras"
 | 
			
		||||
common/views/components/games/reversi/reversi.index.vue:
 | 
			
		||||
  title: "Misskey Reversi"
 | 
			
		||||
  sub-title: "¡Juega Reversi con tus amigos!"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "Desarrolladores"
 | 
			
		||||
  feedback: "Opiniones"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "Detalles"
 | 
			
		||||
  copy-link: "Copiar enlace"
 | 
			
		||||
  favorite: "Me gusta esta nota"
 | 
			
		||||
  pin: "Fijar en el perfil"
 | 
			
		||||
  delete: "Borrar"
 | 
			
		||||
@@ -288,10 +291,10 @@ common/views/components/signin.vue:
 | 
			
		||||
  signin: "Entra"
 | 
			
		||||
  or: "O"
 | 
			
		||||
  signin-with-twitter: "Ingresar con Twitter"
 | 
			
		||||
  login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
 | 
			
		||||
  login-failed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario y contraseña correctos."
 | 
			
		||||
common/views/components/signup.vue:
 | 
			
		||||
  invitation-code: "招待コード"
 | 
			
		||||
  invitation-info: "招待コードをお持ちでない方は、<a href=\"{}\">管理者</a>までご連絡ください。"
 | 
			
		||||
  invitation-code: "Código de invitación"
 | 
			
		||||
  invitation-info: "Si no tienes un código de invitación, por favor contacta un <a href=\"{}\">administrador</a>."
 | 
			
		||||
  username: "Usuario"
 | 
			
		||||
  checking: "Comprobando..."
 | 
			
		||||
  available: "Disponible"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "Directo"
 | 
			
		||||
  specified-desc: "Publica solo para los seguidores que quieras"
 | 
			
		||||
  private: "Privada"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "Recuperando"
 | 
			
		||||
  no-broadcasts: "Sin emisión"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "Alternar vistas"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "Etiquetas"
 | 
			
		||||
  count: "{} usuarios mencionados"
 | 
			
		||||
  empty: "Ninguna tendencia popular ahora"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "Información del servidor"
 | 
			
		||||
  toggle: "Alternar vistas"
 | 
			
		||||
@@ -411,7 +415,7 @@ desktop:
 | 
			
		||||
  uploading-avatar: "Cargando un nuevo avatar"
 | 
			
		||||
  avatar-updated: "Avatar actualizado"
 | 
			
		||||
  choose-avatar: "Escoge una imagen de avatar"
 | 
			
		||||
  invalid-filetype: "この形式のファイルはサポートされていません"
 | 
			
		||||
  invalid-filetype: "Este tipo de archivo no es compatible aquí"
 | 
			
		||||
desktop/views/components/activity.chart.vue:
 | 
			
		||||
  total: "Negro ... Total"
 | 
			
		||||
  notes: "Azul ... Notas"
 | 
			
		||||
@@ -426,23 +430,23 @@ desktop/views/components/calendar.vue:
 | 
			
		||||
  next: "Próximo mes"
 | 
			
		||||
  go: "Click para navegar"
 | 
			
		||||
desktop/views/components/charts.vue:
 | 
			
		||||
  title: "チャート"
 | 
			
		||||
  per-day: "1日ごと"
 | 
			
		||||
  per-hour: "1時間ごと"
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  title: "Gráficos"
 | 
			
		||||
  per-day: "por día"
 | 
			
		||||
  per-hour: "por hora"
 | 
			
		||||
  notes: "Publicaciones"
 | 
			
		||||
  users: "Usuarios"
 | 
			
		||||
  drive: "Unidad"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
    remote-notes: "投稿の増減 (リモート)"
 | 
			
		||||
    notes-total: "投稿の累計"
 | 
			
		||||
    users: "ユーザーの増減"
 | 
			
		||||
    users-total: "ユーザーの累計"
 | 
			
		||||
    drive: "ドライブ使用量の増減"
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    notes: "Número de publicaciones: aumentar/disminuir (Combinado)"
 | 
			
		||||
    local-notes: "Número de publicaciones: aumentar/disminuir (Local)"
 | 
			
		||||
    remote-notes: "Número de publicaciones: aumentar/disminuir (Remoto)"
 | 
			
		||||
    notes-total: "Número de publicaciones: Acumulativo total"
 | 
			
		||||
    users: "Número de usuarios: aumentar/disminuir"
 | 
			
		||||
    users-total: "Número de usuarios: Acumulativo total"
 | 
			
		||||
    drive: "Capacidad de almacenamiento usada: aumentar/disminuir"
 | 
			
		||||
    drive-total: "Capacidad de almacenamiento usada: Acumulativa total"
 | 
			
		||||
    drive-files: "Número de archivos almacenados: aumentar/disminuir"
 | 
			
		||||
    drive-files-total: "Número de archivos almacenados: Acumulativo total"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Escoger archivos"
 | 
			
		||||
  upload: "Cargar archivos de tu dispositivo"
 | 
			
		||||
@@ -463,7 +467,7 @@ desktop/views/components/drive-window.vue:
 | 
			
		||||
desktop/views/components/drive.file.vue:
 | 
			
		||||
  avatar: "Avatar"
 | 
			
		||||
  banner: "Banner"
 | 
			
		||||
  nsfw: "閲覧注意"
 | 
			
		||||
  nsfw: "Ver más"
 | 
			
		||||
  contextmenu:
 | 
			
		||||
    rename: "Renombrar"
 | 
			
		||||
    mark-as-sensitive: "Marcar como 'sensible'"
 | 
			
		||||
@@ -515,31 +519,31 @@ desktop/views/components/media-image.vue:
 | 
			
		||||
  sensitive: "El contenido es NSFW (no seguro para ver en el trabajo, 'not safe for work')"
 | 
			
		||||
  click-to-show: "Click para mostrar"
 | 
			
		||||
desktop/views/components/media-video.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
  sensitive: "Este contenido no es apropiado para ver en el trabajo"
 | 
			
		||||
  click-to-show: "Click para mostrar"
 | 
			
		||||
desktop/views/components/follow-button.vue:
 | 
			
		||||
  following: "Siguiendo"
 | 
			
		||||
  follow: "Sigue"
 | 
			
		||||
  request-pending: "Pendiente de aprobación"
 | 
			
		||||
  follow-request: "フォロー申請"
 | 
			
		||||
  follow-request: "Solicitud de seguir"
 | 
			
		||||
desktop/views/components/followers-window.vue:
 | 
			
		||||
  followers: "{} のフォロワー"
 | 
			
		||||
  followers: "{} seguidores"
 | 
			
		||||
desktop/views/components/followers.vue:
 | 
			
		||||
  empty: "フォロワーはいないようです。"
 | 
			
		||||
  empty: "Parece que no tienes seguidores aún."
 | 
			
		||||
desktop/views/components/following-window.vue:
 | 
			
		||||
  following: "{} のフォロー"
 | 
			
		||||
  following: "Siguiendo {}"
 | 
			
		||||
desktop/views/components/following.vue:
 | 
			
		||||
  empty: "フォロー中のユーザーはいないようです。"
 | 
			
		||||
  empty: "Parece que aún no sigues a nadie."
 | 
			
		||||
desktop/views/components/friends-maker.vue:
 | 
			
		||||
  title: "気になるユーザーをフォロー:"
 | 
			
		||||
  empty: "おすすめのユーザーは見つかりませんでした。"
 | 
			
		||||
  fetching: "読み込んでいます"
 | 
			
		||||
  refresh: "もっと見る"
 | 
			
		||||
  close: "閉じる"
 | 
			
		||||
  title: "Usuarios recomendados:"
 | 
			
		||||
  empty: "No se pudieron encontrar usuarios para recomendar"
 | 
			
		||||
  fetching: "Cargando"
 | 
			
		||||
  refresh: "Más"
 | 
			
		||||
  close: "Cerrar"
 | 
			
		||||
desktop/views/components/game-window.vue:
 | 
			
		||||
  game: "リバーシ"
 | 
			
		||||
  game: "Reversi"
 | 
			
		||||
desktop/views/components/home.vue:
 | 
			
		||||
  done: "完了"
 | 
			
		||||
  done: "Listo"
 | 
			
		||||
  add-widget: "Agregar accesorio:"
 | 
			
		||||
  add: "Agregar"
 | 
			
		||||
desktop/views/input-dialog.vue:
 | 
			
		||||
@@ -565,8 +569,8 @@ desktop/views/components/notes.note.vue:
 | 
			
		||||
  detail: "Mostrar detalles"
 | 
			
		||||
  private: "Esta publicación es privada"
 | 
			
		||||
  deleted: "Esta publicación ha sido borrada"
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  see-more: "もっと見る"
 | 
			
		||||
  hide: "Esconder"
 | 
			
		||||
  see-more: "Ver más"
 | 
			
		||||
desktop/views/components/notes.vue:
 | 
			
		||||
  error: "Error al cargar."
 | 
			
		||||
  retry: "Reintentar"
 | 
			
		||||
@@ -602,7 +606,7 @@ desktop/views/components/post-form.vue:
 | 
			
		||||
  geolocation-alert: "Tu dispositivo no tiene soporte de geolocalización."
 | 
			
		||||
  error: "Error"
 | 
			
		||||
  enter-username: "Por favor escribe un nombre de usuario..."
 | 
			
		||||
  annotations: "内容への注釈 (オプション)"
 | 
			
		||||
  annotations: "Anotaciones a la publicación (opcional)"
 | 
			
		||||
desktop/views/components/post-form-window.vue:
 | 
			
		||||
  note: "Nota nueva"
 | 
			
		||||
  reply: "Responder"
 | 
			
		||||
@@ -766,40 +770,40 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
  welcome-back: "Bienvenido/a de vuelta,"
 | 
			
		||||
  adjective: "-san"
 | 
			
		||||
desktop/views/components/ui.header.account.vue:
 | 
			
		||||
  profile: "プロフィール"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  favorites: "お気に入り"
 | 
			
		||||
  lists: "リスト"
 | 
			
		||||
  follow-requests: "フォロー申請"
 | 
			
		||||
  customize: "ホームのカスタマイズ"
 | 
			
		||||
  admin: "管理"
 | 
			
		||||
  settings: "設定"
 | 
			
		||||
  signout: "サインアウト"
 | 
			
		||||
  dark: "闇に飲まれる"
 | 
			
		||||
  profile: "Tu perfil"
 | 
			
		||||
  drive: "Unidad"
 | 
			
		||||
  favorites: "Favoritos"
 | 
			
		||||
  lists: "Listas"
 | 
			
		||||
  follow-requests: "Solicitudes de seguimiento"
 | 
			
		||||
  customize: "Personalizar la página de inicio"
 | 
			
		||||
  admin: "Admin"
 | 
			
		||||
  settings: "Configuraciones"
 | 
			
		||||
  signout: "Desconectarse"
 | 
			
		||||
  dark: "Sumergirse en la oscuridad"
 | 
			
		||||
desktop/views/components/ui.header.nav.vue:
 | 
			
		||||
  home: "ホーム"
 | 
			
		||||
  deck: "デッキ"
 | 
			
		||||
  messaging: "メッセージ"
 | 
			
		||||
  game: "ゲーム"
 | 
			
		||||
  home: "Inicio"
 | 
			
		||||
  deck: "Cubierta"
 | 
			
		||||
  messaging: "Mensajes"
 | 
			
		||||
  game: "Juegos"
 | 
			
		||||
desktop/views/components/ui.header.notifications.vue:
 | 
			
		||||
  title: "通知"
 | 
			
		||||
  title: "Notificaciones"
 | 
			
		||||
desktop/views/components/ui.header.post.vue:
 | 
			
		||||
  post: "新規投稿"
 | 
			
		||||
  post: "Crear una publicación"
 | 
			
		||||
desktop/views/components/ui.header.search.vue:
 | 
			
		||||
  placeholder: "検索"
 | 
			
		||||
  placeholder: "Buscar"
 | 
			
		||||
desktop/views/components/received-follow-requests-window.vue:
 | 
			
		||||
  title: "フォロー申請"
 | 
			
		||||
  accept: "承認"
 | 
			
		||||
  reject: "拒否"
 | 
			
		||||
  title: "Solicitudes de seguidores"
 | 
			
		||||
  accept: "Aceptar"
 | 
			
		||||
  reject: "Rechazar"
 | 
			
		||||
desktop/views/components/user-lists-window.vue:
 | 
			
		||||
  title: "リスト"
 | 
			
		||||
  create-list: "リストを作成"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
  title: "Listas de usuario"
 | 
			
		||||
  create-list: "Crear lista"
 | 
			
		||||
  list-name: "Nombre de lista"
 | 
			
		||||
desktop/views/components/user-preview.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  notes: "Publicaciones"
 | 
			
		||||
  following: "フォロー"
 | 
			
		||||
  followers: "フォロワー"
 | 
			
		||||
desktop/views/components/users-list.vue:
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "Compte vérifié"
 | 
			
		||||
  disable-animated-mfm: "Désactiver les textes animés dans les publications"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Partie nulle"
 | 
			
		||||
    my-turn: "C’est votre tour"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "Développeur·se·s"
 | 
			
		||||
  feedback: "Remarques"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "Mettre cette note en favoris"
 | 
			
		||||
  pin: "Épingler sur votre profil"
 | 
			
		||||
  delete: "Supprimer"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "Direct"
 | 
			
		||||
  specified-desc: "Publier aux utilisateur·rice·s mentionné·e·s"
 | 
			
		||||
  private: "Privé"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "Récupération"
 | 
			
		||||
  no-broadcasts: "Aucune annonce"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "Basculer entre les vues"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "Étiquettes"
 | 
			
		||||
  count: "{} utilisateur·rice·s mentionné·e·s"
 | 
			
		||||
  empty: "Aucune tendance"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "Informations sur le serveur"
 | 
			
		||||
  toggle: "Afficher les vues"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "Se connecter"
 | 
			
		||||
  signup-button: "S'inscrire"
 | 
			
		||||
  timeline: "Fil d'actualité"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Propulsé par <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Lecteur de Misskey"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "Style de la publication"
 | 
			
		||||
  post-style-standard: "Standard"
 | 
			
		||||
  post-style-smart: "Intelligent"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "Comportement"
 | 
			
		||||
  fetch-on-scroll: "Chargement lors du défilement"
 | 
			
		||||
  disable-via-mobile: "Ne pas mentionner que ma publication provient d'un 'périphérique mobile'"
 | 
			
		||||
 
 | 
			
		||||
@@ -5,24 +5,9 @@
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const yaml = require('js-yaml');
 | 
			
		||||
 | 
			
		||||
const loadLang = lang => yaml.safeLoad(
 | 
			
		||||
	fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8'));
 | 
			
		||||
const langs = ['de-DE', 'en-US', 'fr-FR', 'ja-JP', 'ja-KS', 'pl-PL', 'es-ES', 'nl-NL'];
 | 
			
		||||
 | 
			
		||||
const native = loadLang('ja-JP');
 | 
			
		||||
const loadLocale = lang => yaml.safeLoad(fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8'));
 | 
			
		||||
const locales = langs.map(lang => ({ [lang]: loadLocale(lang) }));
 | 
			
		||||
 | 
			
		||||
const langs = {
 | 
			
		||||
	'de-DE': loadLang('de-DE'),
 | 
			
		||||
	'en-US': loadLang('en-US'),
 | 
			
		||||
	'fr-FR': loadLang('fr-FR'),
 | 
			
		||||
	'ja-JP': native,
 | 
			
		||||
	'ja-KS': loadLang('ja-KS'),
 | 
			
		||||
	'pl-PL': loadLang('pl-PL'),
 | 
			
		||||
	'es-ES': loadLang('es-ES')
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Object.values(langs).forEach(locale => {
 | 
			
		||||
	// Extend native language (Japanese)
 | 
			
		||||
	locale = Object.assign({}, native, locale);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
module.exports = langs;
 | 
			
		||||
module.exports = locales.reduce((a, b) => ({ ...a, ...b }));
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
    my-turn: "あなたのターンです"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "削除"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -375,6 +375,10 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -403,8 +407,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
@@ -988,6 +990,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
@@ -1353,6 +1357,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストをつけんで!"
 | 
			
		||||
  verified-user: "アメちゃん付きアカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内のちょろちょろ動いてんのを止める"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "おあいこ"
 | 
			
		||||
    my-turn: "あんさんのターンや"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "ほかす"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはあらへんで"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "流行は自分で作るんや"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "サインイン中…"
 | 
			
		||||
  signup-button: "サインアップ"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "ドライブ"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "べっぴんさん"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "게시물의 문자 애니메이션을 비활성화 할"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "무승부"
 | 
			
		||||
    my-turn: "당신의 차례입니다"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "削除"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1246
									
								
								locales/nl-NL.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1246
									
								
								locales/nl-NL.yml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1246
									
								
								locales/no-NO.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1246
									
								
								locales/no-NO.yml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "Wyłącz animowany tekst we wpisach"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Remis"
 | 
			
		||||
    my-turn: "Twoja kolej"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "Autorzy"
 | 
			
		||||
  feedback: "Podziel się opinią"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "Dodaj do ulubionych"
 | 
			
		||||
  pin: "Przypnij do profilu"
 | 
			
		||||
  delete: "Usuń"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "Bezpośredni"
 | 
			
		||||
  specified-desc: "Tylko dla określonych użytkowników"
 | 
			
		||||
  private: "Prywatny"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "Sprawdzanie"
 | 
			
		||||
  no-broadcasts: "Brak transmisji"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "Przełącz widok"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "Hashtagi"
 | 
			
		||||
  count: "Wspomniany przez {} użytkowników"
 | 
			
		||||
  empty: "Brak popularnych hashtagów"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "Informacje o serwerze"
 | 
			
		||||
  toggle: "Przełącz widok"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "Zaloguj się"
 | 
			
		||||
  signup-button: "Zarejestruj się"
 | 
			
		||||
  timeline: "Oś czasu"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Oparto o <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Dysk Misskey"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "Styl wpisów"
 | 
			
		||||
  post-style-standard: "Standardowy"
 | 
			
		||||
  post-style-smart: "Inteligentny"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "Zachowanie"
 | 
			
		||||
  fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół"
 | 
			
		||||
  disable-via-mobile: "Nie oznaczaj wpisów jako „wysłane z telefonu”"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "Conta verificada"
 | 
			
		||||
  disable-animated-mfm: "Desativar texto animado nas publicações"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Empatado"
 | 
			
		||||
    my-turn: "Seu turno"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "削除"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "Timeline"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Desenvolvido por <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Drive Misskey"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
    my-turn: "あなたのターンです"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "削除"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
    my-turn: "あなたのターンです"
 | 
			
		||||
@@ -260,6 +261,8 @@ common/views/components/nav.vue:
 | 
			
		||||
  develop: "開発者"
 | 
			
		||||
  feedback: "フィードバック"
 | 
			
		||||
common/views/components/note-menu.vue:
 | 
			
		||||
  detail: "詳細"
 | 
			
		||||
  copy-link: "リンクをコピー"
 | 
			
		||||
  favorite: "お気に入り"
 | 
			
		||||
  pin: "ピン留め"
 | 
			
		||||
  delete: "削除"
 | 
			
		||||
@@ -337,6 +340,9 @@ common/views/components/visibility-chooser.vue:
 | 
			
		||||
  specified: "ダイレクト"
 | 
			
		||||
  specified-desc: "指定したユーザーにのみ公開"
 | 
			
		||||
  private: "非公開"
 | 
			
		||||
common/views/components/trends.vue:
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/broadcast.vue:
 | 
			
		||||
  fetching: "確認中"
 | 
			
		||||
  no-broadcasts: "お知らせはありません"
 | 
			
		||||
@@ -360,8 +366,6 @@ common/views/widgets/posts-monitor.vue:
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
common/views/widgets/hashtags.vue:
 | 
			
		||||
  title: "ハッシュタグ"
 | 
			
		||||
  count: "{}人が投稿"
 | 
			
		||||
  empty: "トレンドなし"
 | 
			
		||||
common/views/widgets/server.vue:
 | 
			
		||||
  title: "サーバー情報"
 | 
			
		||||
  toggle: "表示を切り替え"
 | 
			
		||||
@@ -861,6 +865,8 @@ desktop/views/pages/welcome.vue:
 | 
			
		||||
  signin-button: "やってる"
 | 
			
		||||
  signup-button: "やる"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  photos: "最近の画像"
 | 
			
		||||
  powered-by-misskey: "Powered by <b>Misskey</b>."
 | 
			
		||||
desktop/views/pages/drive.vue:
 | 
			
		||||
  title: "Misskey Drive"
 | 
			
		||||
@@ -1157,6 +1163,9 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  post-style: "投稿の表示スタイル"
 | 
			
		||||
  post-style-standard: "標準"
 | 
			
		||||
  post-style-smart: "スマート"
 | 
			
		||||
  notification-position: "通知の表示"
 | 
			
		||||
  notification-position-bottom: "下"
 | 
			
		||||
  notification-position-top: "上"
 | 
			
		||||
  behavior: "動作"
 | 
			
		||||
  fetch-on-scroll: "スクロールで自動読み込み"
 | 
			
		||||
  disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								package.json
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "misskey",
 | 
			
		||||
	"author": "syuilo <i@syuilo.com>",
 | 
			
		||||
	"version": "8.19.0",
 | 
			
		||||
	"clientVersion": "1.0.9217",
 | 
			
		||||
	"version": "8.28.1",
 | 
			
		||||
	"clientVersion": "1.0.9400",
 | 
			
		||||
	"codename": "nighthike",
 | 
			
		||||
	"main": "./built/index.js",
 | 
			
		||||
	"private": true,
 | 
			
		||||
@@ -55,7 +55,7 @@
 | 
			
		||||
		"@types/koa-send": "4.1.1",
 | 
			
		||||
		"@types/koa-views": "2.0.3",
 | 
			
		||||
		"@types/koa__cors": "2.2.3",
 | 
			
		||||
		"@types/minio": "6.0.2",
 | 
			
		||||
		"@types/minio": "7.0.0",
 | 
			
		||||
		"@types/mkdirp": "0.5.2",
 | 
			
		||||
		"@types/mocha": "5.2.3",
 | 
			
		||||
		"@types/mongodb": "3.1.4",
 | 
			
		||||
@@ -80,7 +80,7 @@
 | 
			
		||||
		"@types/webpack": "4.4.11",
 | 
			
		||||
		"@types/webpack-stream": "3.2.10",
 | 
			
		||||
		"@types/websocket": "0.0.40",
 | 
			
		||||
		"@types/ws": "6.0.0",
 | 
			
		||||
		"@types/ws": "6.0.1",
 | 
			
		||||
		"animejs": "2.2.0",
 | 
			
		||||
		"autosize": "4.0.2",
 | 
			
		||||
		"autwh": "0.1.0",
 | 
			
		||||
@@ -161,7 +161,7 @@
 | 
			
		||||
		"nan": "2.11.0",
 | 
			
		||||
		"nested-property": "0.0.7",
 | 
			
		||||
		"node-sass": "4.9.3",
 | 
			
		||||
		"node-sass-json-importer": "3.3.1",
 | 
			
		||||
		"node-sass-json-importer": "4.0.1",
 | 
			
		||||
		"nprogress": "0.2.0",
 | 
			
		||||
		"object-assign-deep": "0.4.0",
 | 
			
		||||
		"on-build-webpack": "0.1.0",
 | 
			
		||||
@@ -194,7 +194,7 @@
 | 
			
		||||
		"stylus": "0.54.5",
 | 
			
		||||
		"stylus-loader": "3.0.2",
 | 
			
		||||
		"summaly": "2.2.0",
 | 
			
		||||
		"systeminformation": "3.44.2",
 | 
			
		||||
		"systeminformation": "3.45.1",
 | 
			
		||||
		"syuilo-password-strength": "0.0.1",
 | 
			
		||||
		"textarea-caret": "3.1.0",
 | 
			
		||||
		"tmp": "0.0.33",
 | 
			
		||||
@@ -210,19 +210,18 @@
 | 
			
		||||
		"vue": "2.5.17",
 | 
			
		||||
		"vue-chartjs": "3.4.0",
 | 
			
		||||
		"vue-cropperjs": "2.2.1",
 | 
			
		||||
		"vue-js-modal": "1.3.24",
 | 
			
		||||
		"vue-js-modal": "1.3.26",
 | 
			
		||||
		"vue-json-tree-view": "2.1.4",
 | 
			
		||||
		"vue-loader": "15.4.1",
 | 
			
		||||
		"vue-router": "3.0.1",
 | 
			
		||||
		"vue-style-loader": "4.1.2",
 | 
			
		||||
		"vue-template-compiler": "2.5.17",
 | 
			
		||||
		"vue-thin-modal": "1.1.1",
 | 
			
		||||
		"vuedraggable": "2.16.0",
 | 
			
		||||
		"vuex": "3.0.1",
 | 
			
		||||
		"vuex-persistedstate": "2.5.4",
 | 
			
		||||
		"web-push": "3.3.2",
 | 
			
		||||
		"webfinger.js": "2.6.6",
 | 
			
		||||
		"webpack": "4.17.1",
 | 
			
		||||
		"webpack": "4.17.2",
 | 
			
		||||
		"webpack-cli": "3.1.0",
 | 
			
		||||
		"websocket": "1.0.26",
 | 
			
		||||
		"ws": "6.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,10 @@ html
 | 
			
		||||
		&, *
 | 
			
		||||
			cursor progress !important
 | 
			
		||||
 | 
			
		||||
html
 | 
			
		||||
	// iOSのため
 | 
			
		||||
	overflow auto
 | 
			
		||||
 | 
			
		||||
body
 | 
			
		||||
	overflow-wrap break-word
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
	<router-view id="app"></router-view>
 | 
			
		||||
	<modal-portal/>
 | 
			
		||||
</div>
 | 
			
		||||
<router-view id="app"></router-view>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ export default Vue.extend({
 | 
			
		||||
		accepted() {
 | 
			
		||||
			this.state = 'accepted';
 | 
			
		||||
			if (this.session.app.callbackUrl) {
 | 
			
		||||
				location.href = this.session.app.callbackUrl + '?token=' + this.session.token;
 | 
			
		||||
				location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@
 | 
			
		||||
 | 
			
		||||
	// Get salt query
 | 
			
		||||
	const salt = localStorage.getItem('salt')
 | 
			
		||||
		? '?salt=' + localStorage.getItem('salt')
 | 
			
		||||
		? `?salt=${localStorage.getItem('salt')}`
 | 
			
		||||
		: '';
 | 
			
		||||
 | 
			
		||||
	// Load an app script
 | 
			
		||||
@@ -140,7 +140,7 @@
 | 
			
		||||
		// Random
 | 
			
		||||
		localStorage.setItem('salt', Math.random().toString());
 | 
			
		||||
 | 
			
		||||
		// Clear cache (serive worker)
 | 
			
		||||
		// Clear cache (service worker)
 | 
			
		||||
		try {
 | 
			
		||||
			navigator.serviceWorker.controller.postMessage('clear');
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ export default async function(mios: MiOS, force = false, silent = false) {
 | 
			
		||||
		localStorage.setItem('should-refresh', 'true');
 | 
			
		||||
		localStorage.setItem('v', newer);
 | 
			
		||||
 | 
			
		||||
		// Clear cache (serive worker)
 | 
			
		||||
		// Clear cache (service worker)
 | 
			
		||||
		try {
 | 
			
		||||
			if (navigator.serviceWorker.controller) {
 | 
			
		||||
				navigator.serviceWorker.controller.postMessage('clear');
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
const gcd = (a, b) => !b ? a : gcd(b, a % b);
 | 
			
		||||
export default gcd;
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
export default function(qs: string) {
 | 
			
		||||
	const q = {
 | 
			
		||||
		text: ''
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	qs.split(' ').forEach(x => {
 | 
			
		||||
		if (/^([a-z_]+?):(.+?)$/.test(x)) {
 | 
			
		||||
			const [key, value] = x.split(':');
 | 
			
		||||
			switch (key) {
 | 
			
		||||
				case 'user':
 | 
			
		||||
					q['includeUserUsernames'] = value.split(',');
 | 
			
		||||
					break;
 | 
			
		||||
				case 'exclude_user':
 | 
			
		||||
					q['excludeUserUsernames'] = value.split(',');
 | 
			
		||||
					break;
 | 
			
		||||
				case 'follow':
 | 
			
		||||
					q['following'] = value == 'null' ? null : value == 'true';
 | 
			
		||||
					break;
 | 
			
		||||
				case 'reply':
 | 
			
		||||
					q['reply'] = value == 'null' ? null : value == 'true';
 | 
			
		||||
					break;
 | 
			
		||||
				case 'renote':
 | 
			
		||||
					q['renote'] = value == 'null' ? null : value == 'true';
 | 
			
		||||
					break;
 | 
			
		||||
				case 'media':
 | 
			
		||||
					q['media'] = value == 'null' ? null : value == 'true';
 | 
			
		||||
					break;
 | 
			
		||||
				case 'poll':
 | 
			
		||||
					q['poll'] = value == 'null' ? null : value == 'true';
 | 
			
		||||
					break;
 | 
			
		||||
				case 'until':
 | 
			
		||||
				case 'since':
 | 
			
		||||
					// YYYY-MM-DD
 | 
			
		||||
					if (/^[0-9]+\-[0-9]+\-[0-9]+$/) {
 | 
			
		||||
						const [yyyy, mm, dd] = value.split('-');
 | 
			
		||||
						q[`${key}_date`] = (new Date(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10))).getTime();
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					q[key] = value;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			q.text += x + ' ';
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (q.text) {
 | 
			
		||||
		q.text = q.text.trim();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return q;
 | 
			
		||||
}
 | 
			
		||||
@@ -3,8 +3,10 @@ import MiOS from '../../../../../mios';
 | 
			
		||||
 | 
			
		||||
export class ReversiGameStream extends Stream {
 | 
			
		||||
	constructor(os: MiOS, me, game) {
 | 
			
		||||
		super(os, 'games/reversi-game', {
 | 
			
		||||
			i: me ? me.token : null,
 | 
			
		||||
		super(os, 'games/reversi-game', me ? {
 | 
			
		||||
			i: me.token,
 | 
			
		||||
			game: game.id
 | 
			
		||||
		} : {
 | 
			
		||||
			game: game.id
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,9 @@ import MiOS from '../../../mios';
 | 
			
		||||
 */
 | 
			
		||||
export class LocalTimelineStream extends Stream {
 | 
			
		||||
	constructor(os: MiOS, me) {
 | 
			
		||||
		super(os, 'local-timeline', {
 | 
			
		||||
		super(os, 'local-timeline', me ? {
 | 
			
		||||
			i: me.token
 | 
			
		||||
		});
 | 
			
		||||
		} : {});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { EventEmitter } from 'eventemitter3';
 | 
			
		||||
import * as uuid from 'uuid';
 | 
			
		||||
import Connection from './stream';
 | 
			
		||||
import { erase } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ストリーム接続を管理するクラス
 | 
			
		||||
@@ -89,7 +90,7 @@ export default abstract class StreamManager<T extends Connection> extends EventE
 | 
			
		||||
	 * @param userId use で発行したユーザーID
 | 
			
		||||
	 */
 | 
			
		||||
	public dispose(userId) {
 | 
			
		||||
		this.users = this.users.filter(id => id != userId);
 | 
			
		||||
		this.users = erase(userId, this.users);
 | 
			
		||||
 | 
			
		||||
		this._connection.user = `Managed (${ this.users.length })`;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,11 +44,11 @@ export default class Connection extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
		const query = params
 | 
			
		||||
			? Object.keys(params)
 | 
			
		||||
				.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
 | 
			
		||||
				.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
 | 
			
		||||
				.join('&')
 | 
			
		||||
			: null;
 | 
			
		||||
 | 
			
		||||
		this.socket = new ReconnectingWebsocket(`${wsUrl}/${endpoint}${query ? '?' + query : ''}`);
 | 
			
		||||
		this.socket = new ReconnectingWebsocket(`${wsUrl}/${endpoint}${query ? `?${query}` : ''}`);
 | 
			
		||||
		this.socket.addEventListener('open', this.onOpen);
 | 
			
		||||
		this.socket.addEventListener('close', this.onClose);
 | 
			
		||||
		this.socket.addEventListener('message', this.onMessage);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,20 @@
 | 
			
		||||
<template>
 | 
			
		||||
<span class="mk-acct">
 | 
			
		||||
	<span class="name">@{{ user.username }}</span>
 | 
			
		||||
	<span class="host" v-if="user.host">@{{ user.host }}</span>
 | 
			
		||||
	<span class="host" v-if="user.host || detail">@{{ user.host || host }}</span>
 | 
			
		||||
</span>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { host } from '../../../config';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['user']
 | 
			
		||||
	props: ['user', 'detail'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			host
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ export default Vue.extend({
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (this.type == 'user') {
 | 
			
		||||
				const cacheKey = 'autocomplete:user:' + this.q;
 | 
			
		||||
				const cacheKey = `autocomplete:user:${this.q}`;
 | 
			
		||||
				const cache = sessionStorage.getItem(cacheKey);
 | 
			
		||||
				if (cache) {
 | 
			
		||||
					const users = JSON.parse(cache);
 | 
			
		||||
@@ -148,7 +148,7 @@ export default Vue.extend({
 | 
			
		||||
					this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]');
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
				} else {
 | 
			
		||||
					const cacheKey = 'autocomplete:hashtag:' + this.q;
 | 
			
		||||
					const cacheKey = `autocomplete:hashtag:${this.q}`;
 | 
			
		||||
					const cache = sessionStorage.getItem(cacheKey);
 | 
			
		||||
					if (cache) {
 | 
			
		||||
						const hashtags = JSON.parse(cache);
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ export default Vue.extend({
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check internet connection
 | 
			
		||||
		fetch('https://google.com?rand=' + Math.random(), {
 | 
			
		||||
		fetch(`https://google.com?rand=${Math.random()}`, {
 | 
			
		||||
			mode: 'no-cors'
 | 
			
		||||
		}).then(() => {
 | 
			
		||||
			this.internet = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -159,11 +159,9 @@ export default Vue.extend({
 | 
			
		||||
				canPutEverywhere: this.game.settings.canPutEverywhere,
 | 
			
		||||
				loopedBoard: this.game.settings.loopedBoard
 | 
			
		||||
			});
 | 
			
		||||
			this.logs.forEach((log, i) => {
 | 
			
		||||
				if (i < v) {
 | 
			
		||||
					this.o.put(log.color, log.pos);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			for (const log of this.logs.slice(0, v)) {
 | 
			
		||||
				this.o.put(log.color, log.pos);
 | 
			
		||||
			}
 | 
			
		||||
			this.$forceUpdate();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
import trends from './trends.vue';
 | 
			
		||||
import analogClock from './analog-clock.vue';
 | 
			
		||||
import menu from './menu.vue';
 | 
			
		||||
import noteHeader from './note-header.vue';
 | 
			
		||||
@@ -40,6 +41,7 @@ import uiSelect from './ui/select.vue';
 | 
			
		||||
import formButton from './ui/form/button.vue';
 | 
			
		||||
import formRadio from './ui/form/radio.vue';
 | 
			
		||||
 | 
			
		||||
Vue.component('mk-trends', trends);
 | 
			
		||||
Vue.component('mk-analog-clock', analogClock);
 | 
			
		||||
Vue.component('mk-menu', menu);
 | 
			
		||||
Vue.component('mk-note-header', noteHeader);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-menu">
 | 
			
		||||
<div class="onchrpzrvnoruiaenfcqvccjfuupzzwv">
 | 
			
		||||
	<div class="backdrop" ref="backdrop" @click="close"></div>
 | 
			
		||||
	<div class="popover" :class="{ hukidasi }" ref="popover">
 | 
			
		||||
		<template v-for="item in items">
 | 
			
		||||
@@ -119,9 +119,10 @@ export default Vue.extend({
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
@import '~const.styl'
 | 
			
		||||
 | 
			
		||||
$border-color = rgba(27, 31, 35, 0.15)
 | 
			
		||||
root(isDark)
 | 
			
		||||
	$bg-color = isDark ? #2c303c : #fff
 | 
			
		||||
	$border-color = rgba(27, 31, 35, 0.15)
 | 
			
		||||
 | 
			
		||||
.mk-menu
 | 
			
		||||
	position initial
 | 
			
		||||
 | 
			
		||||
	> .backdrop
 | 
			
		||||
@@ -131,14 +132,14 @@ $border-color = rgba(27, 31, 35, 0.15)
 | 
			
		||||
		z-index 10000
 | 
			
		||||
		width 100%
 | 
			
		||||
		height 100%
 | 
			
		||||
		background rgba(#000, 0.1)
 | 
			
		||||
		background rgba(#000, isDark ? 0.5 : 0.1)
 | 
			
		||||
		opacity 0
 | 
			
		||||
 | 
			
		||||
	> .popover
 | 
			
		||||
		position absolute
 | 
			
		||||
		z-index 10001
 | 
			
		||||
		padding 8px 0
 | 
			
		||||
		background #fff
 | 
			
		||||
		background $bg-color
 | 
			
		||||
		border 1px solid $border-color
 | 
			
		||||
		border-radius 4px
 | 
			
		||||
		box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
 | 
			
		||||
@@ -172,12 +173,13 @@ $border-color = rgba(27, 31, 35, 0.15)
 | 
			
		||||
				border-top solid $balloon-size transparent
 | 
			
		||||
				border-left solid $balloon-size transparent
 | 
			
		||||
				border-right solid $balloon-size transparent
 | 
			
		||||
				border-bottom solid $balloon-size #fff
 | 
			
		||||
				border-bottom solid $balloon-size $bg-color
 | 
			
		||||
 | 
			
		||||
		> button
 | 
			
		||||
			display block
 | 
			
		||||
			padding 8px 16px
 | 
			
		||||
			width 100%
 | 
			
		||||
			color isDark ? #d6dce2 : #111
 | 
			
		||||
 | 
			
		||||
			&:hover
 | 
			
		||||
				color $theme-color-foreground
 | 
			
		||||
@@ -191,6 +193,12 @@ $border-color = rgba(27, 31, 35, 0.15)
 | 
			
		||||
		> div
 | 
			
		||||
			margin 8px 0
 | 
			
		||||
			height 1px
 | 
			
		||||
			background #eee
 | 
			
		||||
			background isDark ? #1c2023 : #eee
 | 
			
		||||
 | 
			
		||||
.onchrpzrvnoruiaenfcqvccjfuupzzwv[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.onchrpzrvnoruiaenfcqvccjfuupzzwv:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -205,17 +205,8 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
			
		||||
			}
 | 
			
		||||
		}));
 | 
			
		||||
 | 
			
		||||
		const _els = [];
 | 
			
		||||
		els.forEach((el, i) => {
 | 
			
		||||
			if (el.tag == 'br') {
 | 
			
		||||
				if (!['div', 'pre'].includes(els[i - 1].tag)) {
 | 
			
		||||
					_els.push(el);
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				_els.push(el);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// el.tag === 'br' のとき i !== 0 が保証されるため、短絡評価により els[i - 1] は配列外参照しない
 | 
			
		||||
		const _els = els.filter((el, i) => !(el.tag === 'br' && ['div', 'pre'].includes(els[i - 1].tag)));
 | 
			
		||||
		return createElement('span', _els);
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { erase } from '../../../../../prelude/array';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
@@ -53,7 +54,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		get() {
 | 
			
		||||
			return {
 | 
			
		||||
				choices: this.choices.filter(choice => choice != '')
 | 
			
		||||
				choices: erase('', this.choices)
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { sum } from '../../../../../prelude/array';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['note'],
 | 
			
		||||
	data() {
 | 
			
		||||
@@ -33,7 +34,7 @@ export default Vue.extend({
 | 
			
		||||
			return this.note.poll;
 | 
			
		||||
		},
 | 
			
		||||
		total(): number {
 | 
			
		||||
			return this.poll.choices.reduce((a, b) => a + b.votes, 0);
 | 
			
		||||
			return sum(this.poll.choices.map(x => x.votes));
 | 
			
		||||
		},
 | 
			
		||||
		isVoted(): boolean {
 | 
			
		||||
			return this.poll.choices.some(c => c.isVoted);
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ export default Vue.extend({
 | 
			
		||||
			cursor wait !important
 | 
			
		||||
 | 
			
		||||
	> .avatar
 | 
			
		||||
		margin 16px auto 0 auto
 | 
			
		||||
		margin 0 auto 0 auto
 | 
			
		||||
		width 64px
 | 
			
		||||
		height 64px
 | 
			
		||||
		background #ddd
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								src/client/app/common/views/components/trends.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/client/app/common/views/components/trends.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<!-- <transition-group v-else tag="div" name="chart"> -->
 | 
			
		||||
	<div>
 | 
			
		||||
		<div v-for="stat in stats" :key="stat.tag">
 | 
			
		||||
			<div class="tag">
 | 
			
		||||
				<router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
 | 
			
		||||
				<p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p>
 | 
			
		||||
			</div>
 | 
			
		||||
			<x-chart class="chart" :src="stat.chart"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<!-- </transition-group> -->
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XChart from './trends.chart.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XChart
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			stats: [],
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			clock: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.fetch();
 | 
			
		||||
		this.clock = setInterval(this.fetch, 1000 * 60);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		clearInterval(this.clock);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch() {
 | 
			
		||||
			(this as any).api('hashtags/trend').then(stats => {
 | 
			
		||||
				this.stats = stats;
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
root(isDark)
 | 
			
		||||
	> .fetching
 | 
			
		||||
	> .empty
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 16px
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		.chart-move
 | 
			
		||||
			transition transform 1s ease
 | 
			
		||||
 | 
			
		||||
		> div
 | 
			
		||||
			display flex
 | 
			
		||||
			align-items center
 | 
			
		||||
			padding 14px 16px
 | 
			
		||||
 | 
			
		||||
			&:not(:last-child)
 | 
			
		||||
				border-bottom solid 1px isDark ? #393f4f : #eee
 | 
			
		||||
 | 
			
		||||
			> .tag
 | 
			
		||||
				flex 1
 | 
			
		||||
				overflow hidden
 | 
			
		||||
				font-size 14px
 | 
			
		||||
				color isDark ? #9baec8 : #65727b
 | 
			
		||||
 | 
			
		||||
				> a
 | 
			
		||||
					display block
 | 
			
		||||
					width 100%
 | 
			
		||||
					white-space nowrap
 | 
			
		||||
					overflow hidden
 | 
			
		||||
					text-overflow ellipsis
 | 
			
		||||
					color inherit
 | 
			
		||||
 | 
			
		||||
				> p
 | 
			
		||||
					margin 0
 | 
			
		||||
					font-size 75%
 | 
			
		||||
					opacity 0.7
 | 
			
		||||
 | 
			
		||||
			> .chart
 | 
			
		||||
				height 30px
 | 
			
		||||
 | 
			
		||||
.csqvmxybqbycalfhkxvyfrgbrdalkaoc[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.csqvmxybqbycalfhkxvyfrgbrdalkaoc:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -24,19 +24,34 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
root(isDark)
 | 
			
		||||
	margin 16px
 | 
			
		||||
	padding 16px
 | 
			
		||||
	color isDark ? #fff : #000
 | 
			
		||||
	background isDark ? #282C37 : #fff
 | 
			
		||||
	box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
 | 
			
		||||
 | 
			
		||||
	@media (min-width 500px)
 | 
			
		||||
		padding 32px
 | 
			
		||||
 | 
			
		||||
	> header
 | 
			
		||||
		font-weight normal
 | 
			
		||||
		font-size 24px
 | 
			
		||||
		padding 16px
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		font-size 20px
 | 
			
		||||
		color isDark ? #fff : #444
 | 
			
		||||
 | 
			
		||||
		@media (min-width 500px)
 | 
			
		||||
			padding 24px 32px
 | 
			
		||||
 | 
			
		||||
	> section
 | 
			
		||||
		padding 20px 16px
 | 
			
		||||
		border-top solid 1px isDark ? rgba(#000, 0.3) : rgba(#000, 0.1)
 | 
			
		||||
 | 
			
		||||
		@media (min-width 500px)
 | 
			
		||||
			padding 32px
 | 
			
		||||
 | 
			
		||||
		&.fit-top
 | 
			
		||||
			padding-top 0
 | 
			
		||||
 | 
			
		||||
		> header
 | 
			
		||||
			margin-bottom 16px
 | 
			
		||||
			font-weight bold
 | 
			
		||||
			color isDark ? #fff : #444
 | 
			
		||||
 | 
			
		||||
.ui-card[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
root(isDark)
 | 
			
		||||
	display inline-block
 | 
			
		||||
	margin 32px 32px 32px 0
 | 
			
		||||
	margin 0 32px 0 0
 | 
			
		||||
	cursor pointer
 | 
			
		||||
	transition all 0.3s
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,12 @@ root(isDark)
 | 
			
		||||
	cursor pointer
 | 
			
		||||
	transition all 0.3s
 | 
			
		||||
 | 
			
		||||
	&:first-child
 | 
			
		||||
		margin-top 0
 | 
			
		||||
 | 
			
		||||
	&:last-child
 | 
			
		||||
		margin-bottom 0
 | 
			
		||||
 | 
			
		||||
	> *
 | 
			
		||||
		user-select none
 | 
			
		||||
 | 
			
		||||
@@ -89,6 +95,7 @@ root(isDark)
 | 
			
		||||
 | 
			
		||||
	> .button
 | 
			
		||||
		display inline-block
 | 
			
		||||
		flex-shrink 0
 | 
			
		||||
		margin 3px 0 0 0
 | 
			
		||||
		width 34px
 | 
			
		||||
		height 14px
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,7 @@ export default Vue.extend({
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
 | 
			
		||||
		fetch(`/url?url=${encodeURIComponent(this.url)}`).then(res => {
 | 
			
		||||
			res.json().then(info => {
 | 
			
		||||
				if (info.url == null) return;
 | 
			
		||||
				this.title = info.title;
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { toUnicode as decodePunycode } from 'punycode';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['url', 'target'],
 | 
			
		||||
	data() {
 | 
			
		||||
@@ -27,11 +28,11 @@ export default Vue.extend({
 | 
			
		||||
	created() {
 | 
			
		||||
		const url = new URL(this.url);
 | 
			
		||||
		this.schema = url.protocol;
 | 
			
		||||
		this.hostname = url.hostname;
 | 
			
		||||
		this.hostname = decodePunycode(url.hostname);
 | 
			
		||||
		this.port = url.port;
 | 
			
		||||
		this.pathname = url.pathname;
 | 
			
		||||
		this.query = url.search;
 | 
			
		||||
		this.hash = url.hash;
 | 
			
		||||
		this.pathname = decodeURIComponent(url.pathname);
 | 
			
		||||
		this.query = decodeURIComponent(url.search);
 | 
			
		||||
		this.hash = decodeURIComponent(url.hash);
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -31,15 +31,30 @@ export default Vue.extend({
 | 
			
		||||
			default: undefined
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			notes: []
 | 
			
		||||
			notes: [],
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.fetch();
 | 
			
		||||
 | 
			
		||||
		this.connection = (this as any).os.streams.localTimelineStream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.streams.localTimelineStream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('note', this.onNote);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('note', this.onNote);
 | 
			
		||||
		(this as any).os.streams.localTimelineStream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch(cb?) {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
@@ -48,14 +63,21 @@ export default Vue.extend({
 | 
			
		||||
				local: true,
 | 
			
		||||
				reply: false,
 | 
			
		||||
				renote: false,
 | 
			
		||||
				media: false,
 | 
			
		||||
				poll: false,
 | 
			
		||||
				bot: false
 | 
			
		||||
				file: false,
 | 
			
		||||
				poll: false
 | 
			
		||||
			}).then(notes => {
 | 
			
		||||
				this.notes = notes;
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			if (note.replyId != null) return;
 | 
			
		||||
			if (note.renoteId != null) return;
 | 
			
		||||
			if (note.poll != null) return;
 | 
			
		||||
 | 
			
		||||
			this.notes.unshift(note);
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -191,7 +191,7 @@ class Autocomplete {
 | 
			
		||||
			const acct = renderAcct(value);
 | 
			
		||||
 | 
			
		||||
			// 挿入
 | 
			
		||||
			this.text = trimmedBefore + '@' + acct + ' ' + after;
 | 
			
		||||
			this.text = `${trimmedBefore}@${acct} ${after}`;
 | 
			
		||||
 | 
			
		||||
			// キャレットを戻す
 | 
			
		||||
			this.vm.$nextTick(() => {
 | 
			
		||||
@@ -207,7 +207,7 @@ class Autocomplete {
 | 
			
		||||
			const after = source.substr(caret);
 | 
			
		||||
 | 
			
		||||
			// 挿入
 | 
			
		||||
			this.text = trimmedBefore + '#' + value + ' ' + after;
 | 
			
		||||
			this.text = `${trimmedBefore}#${value} ${after}`;
 | 
			
		||||
 | 
			
		||||
			// キャレットを戻す
 | 
			
		||||
			this.vm.$nextTick(() => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
Vue.filter('notePage', note => {
 | 
			
		||||
	return '/notes/' + note.id;
 | 
			
		||||
	return `/notes/${note.id}`;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -11,5 +11,5 @@ Vue.filter('userName', user => {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Vue.filter('userPage', (user, path?) => {
 | 
			
		||||
	return '/@' + Vue.filter('acct')(user) + (path ? '/' + path : '');
 | 
			
		||||
	return `/@${Vue.filter('acct')(user)}${(path ? `/${path}` : '')}`;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="syxhndwprovvuqhmyvveewmbqayniwkv" v-if="!fetching" :data-darkmode="$store.state.device.darkmode">
 | 
			
		||||
	<div class="signed-in-as" v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + myName + '</b>')"></div>
 | 
			
		||||
	<div class="signed-in-as" v-html="'%i18n:@signed-in-as%'.replace('{}', `<b>${myName}`)"></div>
 | 
			
		||||
 | 
			
		||||
	<main>
 | 
			
		||||
		<div class="banner" :style="bannerStyle"></div>
 | 
			
		||||
@@ -83,7 +83,7 @@ export default Vue.extend({
 | 
			
		||||
						userId: this.user.id
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					if (this.user.isLocked && this.user.hasPendingFollowRequestFromYou) {
 | 
			
		||||
					if (this.user.hasPendingFollowRequestFromYou) {
 | 
			
		||||
						this.user = await (this as any).api('following/requests/cancel', {
 | 
			
		||||
							userId: this.user.id
 | 
			
		||||
						});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-analog-clock">
 | 
			
		||||
	<mk-widget-container :naked="!(props.design % 2)" :show-header="false">
 | 
			
		||||
	<mk-widget-container :naked="props.style % 2 === 0" :show-header="false">
 | 
			
		||||
		<div class="mkw-analog-clock--body">
 | 
			
		||||
			<mk-analog-clock :dark="$store.state.device.darkmode" :smooth="!(props.design && ~props.design)"/>
 | 
			
		||||
			<mk-analog-clock :dark="$store.state.device.darkmode" :smooth="props.style < 2"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -13,13 +13,12 @@ import define from '../../../common/define-widget';
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'analog-clock',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		design: -1
 | 
			
		||||
		style: 0
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		func() {
 | 
			
		||||
			if (++this.props.design > 2)
 | 
			
		||||
				this.props.design = -1;
 | 
			
		||||
			this.props.style = (this.props.style + 1) % 4;
 | 
			
		||||
			this.save();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-broadcast"
 | 
			
		||||
	:data-found="broadcasts.length != 0"
 | 
			
		||||
<div class="anltbovirfeutcigvwgmgxipejaeozxi"
 | 
			
		||||
	:data-found="announcements && announcements.length != 0"
 | 
			
		||||
	:data-melt="props.design == 1"
 | 
			
		||||
	:data-mobile="platform == 'mobile'"
 | 
			
		||||
>
 | 
			
		||||
@@ -14,18 +14,17 @@
 | 
			
		||||
		</svg>
 | 
			
		||||
	</div>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%i18n:@fetching%<mk-ellipsis/></p>
 | 
			
		||||
	<h1 v-if="!fetching">{{ broadcasts.length == 0 ? '%i18n:@no-broadcasts%' : broadcasts[i].title }}</h1>
 | 
			
		||||
	<h1 v-if="!fetching">{{ announcements.length == 0 ? '%i18n:@no-broadcasts%' : announcements[i].title }}</h1>
 | 
			
		||||
	<p v-if="!fetching">
 | 
			
		||||
		<span v-if="broadcasts.length != 0" v-html="broadcasts[i].text"></span>
 | 
			
		||||
		<template v-if="broadcasts.length == 0">%i18n:@have-a-nice-day%</template>
 | 
			
		||||
		<span v-if="announcements.length != 0" v-html="announcements[i].text"></span>
 | 
			
		||||
		<template v-if="announcements.length == 0">%i18n:@have-a-nice-day%</template>
 | 
			
		||||
	</p>
 | 
			
		||||
	<a v-if="broadcasts.length > 1" @click="next">%i18n:@next% >></a>
 | 
			
		||||
	<a v-if="announcements.length > 1" @click="next">%i18n:@next% >></a>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../common/define-widget';
 | 
			
		||||
import { lang } from '../../../config';
 | 
			
		||||
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'broadcast',
 | 
			
		||||
@@ -37,26 +36,18 @@ export default define({
 | 
			
		||||
		return {
 | 
			
		||||
			i: 0,
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			broadcasts: []
 | 
			
		||||
			announcements: []
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
			let broadcasts = [];
 | 
			
		||||
			if (meta.broadcasts) {
 | 
			
		||||
				meta.broadcasts.forEach(broadcast => {
 | 
			
		||||
					if (broadcast[lang]) {
 | 
			
		||||
						broadcasts.push(broadcast[lang]);
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
			this.broadcasts = broadcasts;
 | 
			
		||||
			this.announcements = meta.broadcasts;
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		next() {
 | 
			
		||||
			if (this.i == this.broadcasts.length - 1) {
 | 
			
		||||
			if (this.i == this.announcements.length - 1) {
 | 
			
		||||
				this.i = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.i++;
 | 
			
		||||
@@ -75,7 +66,7 @@ export default define({
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mkw-broadcast
 | 
			
		||||
root(isDark)
 | 
			
		||||
	padding 10px
 | 
			
		||||
	border solid 1px #4078c0
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
@@ -142,15 +133,11 @@ export default define({
 | 
			
		||||
		z-index 1
 | 
			
		||||
		margin 0
 | 
			
		||||
		font-size 0.7em
 | 
			
		||||
		color #555
 | 
			
		||||
		color isDark ? #fff : #555
 | 
			
		||||
 | 
			
		||||
		&.fetching
 | 
			
		||||
			text-align center
 | 
			
		||||
 | 
			
		||||
		a
 | 
			
		||||
			color #555
 | 
			
		||||
			text-decoration underline
 | 
			
		||||
 | 
			
		||||
	> a
 | 
			
		||||
		display block
 | 
			
		||||
		font-size 0.7em
 | 
			
		||||
@@ -159,4 +146,10 @@ export default define({
 | 
			
		||||
		> p
 | 
			
		||||
			color #fff
 | 
			
		||||
 | 
			
		||||
.anltbovirfeutcigvwgmgxipejaeozxi[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.anltbovirfeutcigvwgmgxipejaeozxi:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,20 +4,7 @@
 | 
			
		||||
		<template slot="header">%fa:hashtag%%i18n:@title%</template>
 | 
			
		||||
 | 
			
		||||
		<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
 | 
			
		||||
			<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
			<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
 | 
			
		||||
			<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
			<!-- <transition-group v-else tag="div" name="chart"> -->
 | 
			
		||||
			<div>
 | 
			
		||||
				<div v-for="stat in stats" :key="stat.tag">
 | 
			
		||||
					<div class="tag">
 | 
			
		||||
						<router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
 | 
			
		||||
						<p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p>
 | 
			
		||||
					</div>
 | 
			
		||||
					<x-chart class="chart" :src="stat.chart"/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<!-- </transition-group> -->
 | 
			
		||||
			<mk-trends/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -25,7 +12,6 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../common/define-widget';
 | 
			
		||||
import XChart from './hashtags.chart.vue';
 | 
			
		||||
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'hashtags',
 | 
			
		||||
@@ -33,89 +19,11 @@ export default define({
 | 
			
		||||
		compact: false
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XChart
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			stats: [],
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			clock: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.fetch();
 | 
			
		||||
		this.clock = setInterval(this.fetch, 1000 * 60);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		clearInterval(this.clock);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		func() {
 | 
			
		||||
			this.props.compact = !this.props.compact;
 | 
			
		||||
			this.save();
 | 
			
		||||
		},
 | 
			
		||||
		fetch() {
 | 
			
		||||
			(this as any).api('hashtags/trend').then(stats => {
 | 
			
		||||
				this.stats = stats;
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
root(isDark)
 | 
			
		||||
	.mkw-hashtags--body
 | 
			
		||||
		> .fetching
 | 
			
		||||
		> .empty
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 16px
 | 
			
		||||
			text-align center
 | 
			
		||||
			color #aaa
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		> div
 | 
			
		||||
			.chart-move
 | 
			
		||||
				transition transform 1s ease
 | 
			
		||||
 | 
			
		||||
			> div
 | 
			
		||||
				display flex
 | 
			
		||||
				align-items center
 | 
			
		||||
				padding 14px 16px
 | 
			
		||||
 | 
			
		||||
				&:not(:last-child)
 | 
			
		||||
					border-bottom solid 1px isDark ? #393f4f : #eee
 | 
			
		||||
 | 
			
		||||
				> .tag
 | 
			
		||||
					flex 1
 | 
			
		||||
					overflow hidden
 | 
			
		||||
					font-size 14px
 | 
			
		||||
					color isDark ? #9baec8 : #65727b
 | 
			
		||||
 | 
			
		||||
					> a
 | 
			
		||||
						display block
 | 
			
		||||
						width 100%
 | 
			
		||||
						white-space nowrap
 | 
			
		||||
						overflow hidden
 | 
			
		||||
						text-overflow ellipsis
 | 
			
		||||
						color inherit
 | 
			
		||||
 | 
			
		||||
					> p
 | 
			
		||||
						margin 0
 | 
			
		||||
						font-size 75%
 | 
			
		||||
						opacity 0.7
 | 
			
		||||
 | 
			
		||||
				> .chart
 | 
			
		||||
					height 30px
 | 
			
		||||
 | 
			
		||||
.mkw-hashtags[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.mkw-hashtags:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -163,7 +163,7 @@ export default Vue.extend({
 | 
			
		||||
							});
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							alert('%i18n:@unhandled-error% ' + err);
 | 
			
		||||
							alert(`%i18n:@unhandled-error% ${err}`);
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -323,7 +323,7 @@ export default Vue.extend({
 | 
			
		||||
							});
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							alert('%i18n:@unhandled-error% ' + err);
 | 
			
		||||
							alert(`%i18n:@unhandled-error% ${err}`);
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
@@ -404,7 +404,7 @@ export default Vue.extend({
 | 
			
		||||
					folder: folder
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				window.open(url + '/i/drive/folder/' + folder.id,
 | 
			
		||||
				window.open(`${url}/i/drive/folder/${folder.id}`,
 | 
			
		||||
					'drive_window',
 | 
			
		||||
					'height=500, width=800');
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,13 +55,15 @@ export default Vue.extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		onFollow(user) {
 | 
			
		||||
			if (user.id == this.u.id) {
 | 
			
		||||
				this.user.isFollowing = user.isFollowing;
 | 
			
		||||
				this.u.isFollowing = user.isFollowing;
 | 
			
		||||
				this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onUnfollow(user) {
 | 
			
		||||
			if (user.id == this.u.id) {
 | 
			
		||||
				this.user.isFollowing = user.isFollowing;
 | 
			
		||||
				this.u.isFollowing = user.isFollowing;
 | 
			
		||||
				this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +76,7 @@ export default Vue.extend({
 | 
			
		||||
						userId: this.u.id
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					if (this.u.isLocked && this.u.hasPendingFollowRequestFromYou) {
 | 
			
		||||
					if (this.u.hasPendingFollowRequestFromYou) {
 | 
			
		||||
						this.u = await (this as any).api('following/requests/cancel', {
 | 
			
		||||
							userId: this.u.id
 | 
			
		||||
						});
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ export default Vue.extend({
 | 
			
		||||
			const mouseY = e.clientY - rect.top;
 | 
			
		||||
			const xp = mouseX / this.$el.offsetWidth * 100;
 | 
			
		||||
			const yp = mouseY / this.$el.offsetHeight * 100;
 | 
			
		||||
			this.$el.style.backgroundPosition = xp + '% ' + yp + '%';
 | 
			
		||||
			this.$el.style.backgroundPosition = `${xp}% ${yp}%`;
 | 
			
		||||
			this.$el.style.backgroundImage = `url("${this.image.url}")`;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,8 +42,8 @@
 | 
			
		||||
				<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
			
		||||
				<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="media" v-if="p.media.length > 0">
 | 
			
		||||
				<mk-media-list :media-list="p.media" :raw="true"/>
 | 
			
		||||
			<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
				<mk-media-list :media-list="p.files" :raw="true"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<mk-poll v-if="p.poll" :note="p"/>
 | 
			
		||||
			<mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="true"/>
 | 
			
		||||
@@ -86,6 +86,7 @@ import MkRenoteFormWindow from './renote-form-window.vue';
 | 
			
		||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
 | 
			
		||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
 | 
			
		||||
import XSub from './notes.note.sub.vue';
 | 
			
		||||
import { sum } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -114,7 +115,7 @@ export default Vue.extend({
 | 
			
		||||
		isRenote(): boolean {
 | 
			
		||||
			return (this.note.renote &&
 | 
			
		||||
				this.note.text == null &&
 | 
			
		||||
				this.note.mediaIds.length == 0 &&
 | 
			
		||||
				this.note.fileIds.length == 0 &&
 | 
			
		||||
				this.note.poll == null);
 | 
			
		||||
		},
 | 
			
		||||
		p(): any {
 | 
			
		||||
@@ -122,9 +123,7 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
		reactionsCount(): number {
 | 
			
		||||
			return this.p.reactionCounts
 | 
			
		||||
				? Object.keys(this.p.reactionCounts)
 | 
			
		||||
					.map(key => this.p.reactionCounts[key])
 | 
			
		||||
					.reduce((a, b) => a + b)
 | 
			
		||||
				? sum(Object.values(this.p.reactionCounts))
 | 
			
		||||
				: 0;
 | 
			
		||||
		},
 | 
			
		||||
		title(): string {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@
 | 
			
		||||
						<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/>
 | 
			
		||||
						<a class="rp" v-if="p.renote">RP:</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="media" v-if="p.media.length > 0">
 | 
			
		||||
						<mk-media-list :media-list="p.media"/>
 | 
			
		||||
					<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
						<mk-media-list :media-list="p.files"/>
 | 
			
		||||
					</div>
 | 
			
		||||
					<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
 | 
			
		||||
					<a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
 | 
			
		||||
@@ -78,6 +78,7 @@ import MkRenoteFormWindow from './renote-form-window.vue';
 | 
			
		||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
 | 
			
		||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
 | 
			
		||||
import XSub from './notes.note.sub.vue';
 | 
			
		||||
import { sum } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
function focus(el, fn) {
 | 
			
		||||
	const target = fn(el);
 | 
			
		||||
@@ -110,7 +111,7 @@ export default Vue.extend({
 | 
			
		||||
		isRenote(): boolean {
 | 
			
		||||
			return (this.note.renote &&
 | 
			
		||||
				this.note.text == null &&
 | 
			
		||||
				this.note.mediaIds.length == 0 &&
 | 
			
		||||
				this.note.fileIds.length == 0 &&
 | 
			
		||||
				this.note.poll == null);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -120,9 +121,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		reactionsCount(): number {
 | 
			
		||||
			return this.p.reactionCounts
 | 
			
		||||
				? Object.keys(this.p.reactionCounts)
 | 
			
		||||
					.map(key => this.p.reactionCounts[key])
 | 
			
		||||
					.reduce((a, b) => a + b)
 | 
			
		||||
				? sum(Object.values(this.p.reactionCounts))
 | 
			
		||||
				: 0;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@ export default Vue.extend({
 | 
			
		||||
		prepend(note, silent = false) {
 | 
			
		||||
			//#region 弾く
 | 
			
		||||
			const isMyNote = note.userId == this.$store.state.i.id;
 | 
			
		||||
			const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
 | 
			
		||||
			const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
 | 
			
		||||
 | 
			
		||||
			if (this.$store.state.settings.showMyRenotes === false) {
 | 
			
		||||
				if (isMyNote && isPureRenote) {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
		<span class="icon" v-if="geo">%fa:map-marker-alt%</span>
 | 
			
		||||
		<span v-if="!reply">%i18n:@note%</span>
 | 
			
		||||
		<span v-if="reply">%i18n:@reply%</span>
 | 
			
		||||
		<span class="count" v-if="media.length != 0">{{ '%i18n:@attaches%'.replace('{}', media.length) }}</span>
 | 
			
		||||
		<span class="count" v-if="files.length != 0">{{ '%i18n:@attaches%'.replace('{}', files.length) }}</span>
 | 
			
		||||
		<span class="count" v-if="uploadings.length != 0">{{ '%i18n:@uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
 | 
			
		||||
	</span>
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
			:reply="reply"
 | 
			
		||||
			@posted="onPosted"
 | 
			
		||||
			@change-uploadings="onChangeUploadings"
 | 
			
		||||
			@change-attached-media="onChangeMedia"
 | 
			
		||||
			@change-attached-files="onChangeFiles"
 | 
			
		||||
			@geo-attached="onGeoAttached"
 | 
			
		||||
			@geo-dettached="onGeoDettached"/>
 | 
			
		||||
	</div>
 | 
			
		||||
@@ -29,7 +29,7 @@ export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			uploadings: [],
 | 
			
		||||
			media: [],
 | 
			
		||||
			files: [],
 | 
			
		||||
			geo: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
@@ -42,8 +42,8 @@ export default Vue.extend({
 | 
			
		||||
		onChangeUploadings(files) {
 | 
			
		||||
			this.uploadings = files;
 | 
			
		||||
		},
 | 
			
		||||
		onChangeMedia(media) {
 | 
			
		||||
			this.media = media;
 | 
			
		||||
		onChangeFiles(files) {
 | 
			
		||||
			this.files = files;
 | 
			
		||||
		},
 | 
			
		||||
		onGeoAttached(geo) {
 | 
			
		||||
			this.geo = geo;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
			@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
 | 
			
		||||
			v-autocomplete="'text'"
 | 
			
		||||
		></textarea>
 | 
			
		||||
		<div class="medias" :class="{ with: poll }" v-show="files.length != 0">
 | 
			
		||||
		<div class="files" :class="{ with: poll }" v-show="files.length != 0">
 | 
			
		||||
			<x-draggable :list="files" :options="{ animation: 150 }">
 | 
			
		||||
				<div v-for="file in files" :key="file.id">
 | 
			
		||||
					<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
 | 
			
		||||
@@ -35,7 +35,7 @@
 | 
			
		||||
	<button class="upload" title="%i18n:@attach-media-from-local%" @click="chooseFile">%fa:upload%</button>
 | 
			
		||||
	<button class="drive" title="%i18n:@attach-media-from-drive%" @click="chooseFileFromDrive">%fa:cloud%</button>
 | 
			
		||||
	<button class="kao" title="%i18n:@insert-a-kao%" @click="kao">%fa:R smile%</button>
 | 
			
		||||
	<button class="poll" title="%i18n:@create-poll%" @click="poll = true">%fa:chart-pie%</button>
 | 
			
		||||
	<button class="poll" title="%i18n:@create-poll%" @click="poll = !poll">%fa:chart-pie%</button>
 | 
			
		||||
	<button class="poll" title="%i18n:@hide-contents%" @click="useCw = !useCw">%fa:eye-slash%</button>
 | 
			
		||||
	<button class="geo" title="%i18n:@attach-location-information%" @click="geo ? removeGeo() : setGeo()">%fa:map-marker-alt%</button>
 | 
			
		||||
	<button class="visibility" title="%i18n:@visibility%" @click="setVisibility" ref="visibilityButton">
 | 
			
		||||
@@ -62,6 +62,7 @@ import getFace from '../../../common/scripts/get-face';
 | 
			
		||||
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
 | 
			
		||||
import parse from '../../../../../mfm/parse';
 | 
			
		||||
import { host } from '../../../config';
 | 
			
		||||
import { erase } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -110,9 +111,9 @@ export default Vue.extend({
 | 
			
		||||
	computed: {
 | 
			
		||||
		draftId(): string {
 | 
			
		||||
			return this.renote
 | 
			
		||||
				? 'renote:' + this.renote.id
 | 
			
		||||
				? `renote:${this.renote.id}`
 | 
			
		||||
				: this.reply
 | 
			
		||||
					? 'reply:' + this.reply.id
 | 
			
		||||
					? `reply:${this.reply.id}`
 | 
			
		||||
					: 'note';
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -188,7 +189,7 @@ export default Vue.extend({
 | 
			
		||||
							(this.$refs.poll as any).set(draft.data.poll);
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
					this.$emit('change-attached-media', this.files);
 | 
			
		||||
					this.$emit('change-attached-files', this.files);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -225,12 +226,12 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		attachMedia(driveFile) {
 | 
			
		||||
			this.files.push(driveFile);
 | 
			
		||||
			this.$emit('change-attached-media', this.files);
 | 
			
		||||
			this.$emit('change-attached-files', this.files);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		detachMedia(id) {
 | 
			
		||||
			this.files = this.files.filter(x => x.id != id);
 | 
			
		||||
			this.$emit('change-attached-media', this.files);
 | 
			
		||||
			this.$emit('change-attached-files', this.files);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeFile() {
 | 
			
		||||
@@ -249,7 +250,7 @@ export default Vue.extend({
 | 
			
		||||
			this.text = '';
 | 
			
		||||
			this.files = [];
 | 
			
		||||
			this.poll = false;
 | 
			
		||||
			this.$emit('change-attached-media', this.files);
 | 
			
		||||
			this.$emit('change-attached-files', this.files);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onKeydown(e) {
 | 
			
		||||
@@ -297,7 +298,7 @@ export default Vue.extend({
 | 
			
		||||
			if (driveFile != null && driveFile != '') {
 | 
			
		||||
				const file = JSON.parse(driveFile);
 | 
			
		||||
				this.files.push(file);
 | 
			
		||||
				this.$emit('change-attached-media', this.files);
 | 
			
		||||
				this.$emit('change-attached-files', this.files);
 | 
			
		||||
				e.preventDefault();
 | 
			
		||||
			}
 | 
			
		||||
			//#endregion
 | 
			
		||||
@@ -313,7 +314,7 @@ export default Vue.extend({
 | 
			
		||||
				this.geo = pos.coords;
 | 
			
		||||
				this.$emit('geo-attached', this.geo);
 | 
			
		||||
			}, err => {
 | 
			
		||||
				alert('%i18n:@error%: ' + err.message);
 | 
			
		||||
				alert(`%i18n:@error%: ${err.message}`);
 | 
			
		||||
			}, {
 | 
			
		||||
					enableHighAccuracy: true
 | 
			
		||||
				});
 | 
			
		||||
@@ -346,7 +347,7 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		removeVisibleUser(user) {
 | 
			
		||||
			this.visibleUsers = this.visibleUsers.filter(u => u != user);
 | 
			
		||||
			this.visibleUsers = erase(user, this.visibleUsers);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		post() {
 | 
			
		||||
@@ -354,7 +355,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
			(this as any).api('notes/create', {
 | 
			
		||||
				text: this.text == '' ? undefined : this.text,
 | 
			
		||||
				mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
			
		||||
				fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
			
		||||
				replyId: this.reply ? this.reply.id : undefined,
 | 
			
		||||
				renoteId: this.renote ? this.renote.id : undefined,
 | 
			
		||||
				poll: this.poll ? (this.$refs.poll as any).get() : undefined,
 | 
			
		||||
@@ -514,7 +515,7 @@ root(isDark)
 | 
			
		||||
				margin-right 8px
 | 
			
		||||
				white-space nowrap
 | 
			
		||||
 | 
			
		||||
		> .medias
 | 
			
		||||
		> .files
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 0
 | 
			
		||||
			background isDark ? #181b23 : lighten($theme-color, 98%)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,9 @@
 | 
			
		||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
 | 
			
		||||
		<a class="rp" v-if="note.renoteId" :href="`/notes/${note.renoteId}`">RP: ...</a>
 | 
			
		||||
	</div>
 | 
			
		||||
	<details v-if="note.media.length > 0">
 | 
			
		||||
		<summary>({{ '%i18n:@media-count%'.replace('{}', note.media.length) }})</summary>
 | 
			
		||||
		<mk-media-list :media-list="note.media"/>
 | 
			
		||||
	<details v-if="note.files.length > 0">
 | 
			
		||||
		<summary>({{ '%i18n:@media-count%'.replace('{}', note.files.length) }})</summary>
 | 
			
		||||
		<mk-media-list :media-list="note.files"/>
 | 
			
		||||
	</details>
 | 
			
		||||
	<details v-if="note.poll">
 | 
			
		||||
		<summary>%i18n:@poll%</summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="qldxjjsrseehkusjuoooapmsprvfrxyl mk-admin-card">
 | 
			
		||||
	<header>%i18n:@announcements%</header>
 | 
			
		||||
	<textarea v-model="broadcasts"></textarea>
 | 
			
		||||
	<button class="ui" @click="save">%i18n:@save%</button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from "vue";
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			broadcasts: '',
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
			this.broadcasts = JSON.stringify(meta.broadcasts, null, '  ');
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		save() {
 | 
			
		||||
			(this as any).api('admin/update-meta', {
 | 
			
		||||
				broadcasts: JSON.parse(this.broadcasts)
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
@import '~const.styl'
 | 
			
		||||
 | 
			
		||||
.qldxjjsrseehkusjuoooapmsprvfrxyl
 | 
			
		||||
	textarea
 | 
			
		||||
		width 100%
 | 
			
		||||
		min-height 300px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
		<ul>
 | 
			
		||||
			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:chalkboard .fw%%i18n:@dashboard%</li>
 | 
			
		||||
			<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
 | 
			
		||||
			<li @click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
 | 
			
		||||
			<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:@drive%</li> -->
 | 
			
		||||
			<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 | 
			
		||||
		</ul>
 | 
			
		||||
@@ -13,6 +14,9 @@
 | 
			
		||||
			<x-dashboard/>
 | 
			
		||||
			<x-charts/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div v-show="page == 'announcements'">
 | 
			
		||||
			<x-announcements/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div v-if="page == 'users'">
 | 
			
		||||
			<x-suspend-user/>
 | 
			
		||||
			<x-unsuspend-user/>
 | 
			
		||||
@@ -28,6 +32,7 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from "vue";
 | 
			
		||||
import XDashboard from "./admin.dashboard.vue";
 | 
			
		||||
import XAnnouncements from "./admin.announcements.vue";
 | 
			
		||||
import XSuspendUser from "./admin.suspend-user.vue";
 | 
			
		||||
import XUnsuspendUser from "./admin.unsuspend-user.vue";
 | 
			
		||||
import XVerifyUser from "./admin.verify-user.vue";
 | 
			
		||||
@@ -37,6 +42,7 @@ import XCharts from "../../components/charts.vue";
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XDashboard,
 | 
			
		||||
		XAnnouncements,
 | 
			
		||||
		XSuspendUser,
 | 
			
		||||
		XUnsuspendUser,
 | 
			
		||||
		XVerifyUser,
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import Menu from '../../../../common/views/components/menu.vue';
 | 
			
		||||
import contextmenu from '../../../api/contextmenu';
 | 
			
		||||
import { countIf } from '../../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
@@ -117,7 +118,7 @@ export default Vue.extend({
 | 
			
		||||
		toggleActive() {
 | 
			
		||||
			if (!this.isStacked) return;
 | 
			
		||||
			const vms = this.$store.state.settings.deck.layout.find(ids => ids.indexOf(this.column.id) != -1).map(id => this.getColumnVm(id));
 | 
			
		||||
			if (this.active && vms.filter(vm => vm.$el.classList.contains('active')).length == 1) return;
 | 
			
		||||
			if (this.active && countIf(vm => vm.$el.classList.contains('active'), vms) == 1) return;
 | 
			
		||||
			this.active = !this.active;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ export default Vue.extend({
 | 
			
		||||
				(this as any).api('notes/user-list-timeline', {
 | 
			
		||||
					listId: this.list.id,
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					mediaOnly: this.mediaOnly,
 | 
			
		||||
					withFiles: this.mediaOnly,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
@@ -90,7 +90,7 @@ export default Vue.extend({
 | 
			
		||||
				listId: this.list.id,
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				mediaOnly: this.mediaOnly,
 | 
			
		||||
				withFiles: this.mediaOnly,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
@@ -109,7 +109,7 @@ export default Vue.extend({
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			if (this.mediaOnly && note.media.length == 0) return;
 | 
			
		||||
			if (this.mediaOnly && note.files.length == 0) return;
 | 
			
		||||
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@
 | 
			
		||||
						<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
			
		||||
						<a class="rp" v-if="p.renote != null">RP:</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="media" v-if="p.media.length > 0">
 | 
			
		||||
						<mk-media-list :media-list="p.media"/>
 | 
			
		||||
					<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
						<mk-media-list :media-list="p.files"/>
 | 
			
		||||
					</div>
 | 
			
		||||
					<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
 | 
			
		||||
					<a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
 | 
			
		||||
@@ -54,11 +54,11 @@
 | 
			
		||||
	</article>
 | 
			
		||||
</div>
 | 
			
		||||
<div v-else class="srwrkujossgfuhrbnvqkybtzxpblgchi">
 | 
			
		||||
	<div v-if="note.media.length > 0">
 | 
			
		||||
		<mk-media-list :media-list="note.media"/>
 | 
			
		||||
	<div v-if="note.files.length > 0">
 | 
			
		||||
		<mk-media-list :media-list="note.files"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div v-if="note.renote && note.renote.media.length > 0">
 | 
			
		||||
		<mk-media-list :media-list="note.renote.media"/>
 | 
			
		||||
	<div v-if="note.renote && note.renote.files.length > 0">
 | 
			
		||||
		<mk-media-list :media-list="note.renote.files"/>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -100,7 +100,7 @@ export default Vue.extend({
 | 
			
		||||
		isRenote(): boolean {
 | 
			
		||||
			return (this.note.renote &&
 | 
			
		||||
				this.note.text == null &&
 | 
			
		||||
				this.note.mediaIds.length == 0 &&
 | 
			
		||||
				this.note.fileIds.length == 0 &&
 | 
			
		||||
				this.note.poll == null);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -371,7 +371,7 @@ root(isDark)
 | 
			
		||||
					.mk-url-preview
 | 
			
		||||
						margin-top 8px
 | 
			
		||||
 | 
			
		||||
					> .media
 | 
			
		||||
					> .files
 | 
			
		||||
						> img
 | 
			
		||||
							display block
 | 
			
		||||
							max-width 100%
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,7 @@ export default Vue.extend({
 | 
			
		||||
		prepend(note, silent = false) {
 | 
			
		||||
			//#region 弾く
 | 
			
		||||
			const isMyNote = note.userId == this.$store.state.i.id;
 | 
			
		||||
			const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
 | 
			
		||||
			const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
 | 
			
		||||
 | 
			
		||||
			if (this.$store.state.settings.showMyRenotes === false) {
 | 
			
		||||
				if (isMyNote && isPureRenote) {
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ export default Vue.extend({
 | 
			
		||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
			
		||||
				(this as any).api(this.endpoint, {
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					mediaOnly: this.mediaOnly,
 | 
			
		||||
					withFiles: this.mediaOnly,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
@@ -117,7 +117,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
			const promise = (this as any).api(this.endpoint, {
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				mediaOnly: this.mediaOnly,
 | 
			
		||||
				withFiles: this.mediaOnly,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
@@ -138,7 +138,7 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			if (this.mediaOnly && note.media.length == 0) return;
 | 
			
		||||
			if (this.mediaOnly && note.files.length == 0) return;
 | 
			
		||||
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ export default Vue.extend({
 | 
			
		||||
			const title = folder.name + ' | %i18n:@title%';
 | 
			
		||||
 | 
			
		||||
			// Rewrite URL
 | 
			
		||||
			history.pushState(null, title, '/i/drive/folder/' + folder.id);
 | 
			
		||||
			history.pushState(null, title, `/i/drive/folder/${folder.id}`);
 | 
			
		||||
 | 
			
		||||
			document.title = title;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,10 @@ export default Vue.extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		nav(game, actualNav) {
 | 
			
		||||
			if (actualNav) {
 | 
			
		||||
				this.$router.push('/reversi/' + game.id);
 | 
			
		||||
				this.$router.push(`/reversi/${game.id}`);
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO: https://github.com/vuejs/vue-router/issues/703
 | 
			
		||||
				this.$router.push('/reversi/' + game.id);
 | 
			
		||||
				this.$router.push(`/reversi/${game.id}`);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ export default Vue.extend({
 | 
			
		||||
				this.user = user;
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
 | 
			
		||||
				document.title = 'メッセージ: ' + getUserName(this.user);
 | 
			
		||||
				document.title = `メッセージ: ${getUserName(this.user)}`;
 | 
			
		||||
 | 
			
		||||
				Progress.done();
 | 
			
		||||
			});
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
		<div class="title">
 | 
			
		||||
			<p class="name">{{ user | userName }}</p>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span class="username"><mk-acct :user="user"/></span>
 | 
			
		||||
				<span class="username"><mk-acct :user="user" :detail="true" /></span>
 | 
			
		||||
				<span v-if="user.isBot" title="%i18n:@is-bot%">%fa:robot%</span>
 | 
			
		||||
				<span class="location" v-if="user.host === null && user.profile.location">%fa:map-marker% {{ user.profile.location }}</span>
 | 
			
		||||
				<span class="birthday" v-if="user.host === null && user.profile.birthday">%fa:birthday-cake% {{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,12 +24,12 @@ export default Vue.extend({
 | 
			
		||||
	mounted() {
 | 
			
		||||
		(this as any).api('users/notes', {
 | 
			
		||||
			userId: this.user.id,
 | 
			
		||||
			withMedia: true,
 | 
			
		||||
			withFiles: true,
 | 
			
		||||
			limit: 9
 | 
			
		||||
		}).then(notes => {
 | 
			
		||||
			notes.forEach(note => {
 | 
			
		||||
				note.media.forEach(media => {
 | 
			
		||||
					if (this.images.length < 9) this.images.push(media);
 | 
			
		||||
				note.files.forEach(file => {
 | 
			
		||||
					if (this.images.length < 9) this.images.push(file);
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ export default Vue.extend({
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					untilDate: this.date ? this.date.getTime() : undefined,
 | 
			
		||||
					includeReplies: this.mode == 'with-replies',
 | 
			
		||||
					withMedia: this.mode == 'with-media'
 | 
			
		||||
					withFiles: this.mode == 'with-media'
 | 
			
		||||
				}).then(notes => {
 | 
			
		||||
					if (notes.length == fetchLimit + 1) {
 | 
			
		||||
						notes.pop();
 | 
			
		||||
@@ -86,7 +86,7 @@ export default Vue.extend({
 | 
			
		||||
				userId: this.user.id,
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				includeReplies: this.mode == 'with-replies',
 | 
			
		||||
				withMedia: this.mode == 'with-media',
 | 
			
		||||
				withFiles: this.mode == 'with-media',
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,45 +1,85 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-welcome">
 | 
			
		||||
	<img ref="pointer" class="pointer" src="/assets/pointer.png" alt="">
 | 
			
		||||
	<button @click="dark">
 | 
			
		||||
		<template v-if="$store.state.device.darkmode">%fa:moon%</template>
 | 
			
		||||
		<template v-else>%fa:R moon%</template>
 | 
			
		||||
	</button>
 | 
			
		||||
 | 
			
		||||
	<mk-forkit class="forkit"/>
 | 
			
		||||
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<div class="container">
 | 
			
		||||
			<div class="info">
 | 
			
		||||
				<span><b>{{ host }}</b></span>
 | 
			
		||||
				<span class="stats" v-if="stats">
 | 
			
		||||
					<span>%fa:user% {{ stats.originalUsersCount | number }}</span>
 | 
			
		||||
					<span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
 | 
			
		||||
				</span>
 | 
			
		||||
			</div>
 | 
			
		||||
			<main>
 | 
			
		||||
				<div class="about">
 | 
			
		||||
					<h1 v-if="name != 'Misskey'">{{ name }}</h1>
 | 
			
		||||
					<h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name"></h1>
 | 
			
		||||
					<p class="powerd-by" v-if="name != 'Misskey'" v-html="'%i18n:@powered-by-misskey%'"></p>
 | 
			
		||||
					<p class="desc" v-html="description || '%i18n:common.about%'"></p>
 | 
			
		||||
					<a ref="signup" @click="signup">📦 %i18n:@signup%</a>
 | 
			
		||||
		<div class="main block">
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1 v-if="name != 'Misskey'">{{ name }}</h1>
 | 
			
		||||
				<h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name"></h1>
 | 
			
		||||
 | 
			
		||||
				<div class="info">
 | 
			
		||||
					<span><b>{{ host }}</b> - <span v-html="'%i18n:@powered-by-misskey%'"></span></span>
 | 
			
		||||
					<span class="stats" v-if="stats">
 | 
			
		||||
						<span>%fa:user% {{ stats.originalUsersCount | number }}</span>
 | 
			
		||||
						<span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
 | 
			
		||||
					</span>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
				<p class="desc" v-html="description || '%i18n:common.about%'"></p>
 | 
			
		||||
 | 
			
		||||
				<p class="sign">
 | 
			
		||||
					<span class="signup" @click="signup">%i18n:@signup%</span>
 | 
			
		||||
					<span class="divider">|</span>
 | 
			
		||||
					<span class="signin" @click="signin">%i18n:@signin%</span>
 | 
			
		||||
				</p>
 | 
			
		||||
 | 
			
		||||
				<img src="/assets/pointer.png" alt="" class="char">
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="announcements block">
 | 
			
		||||
			<header>%fa:broadcast-tower% %i18n:@announcements%</header>
 | 
			
		||||
			<div v-if="announcements && announcements.length > 0">
 | 
			
		||||
				<div v-for="announcement in announcements">
 | 
			
		||||
					<h1 v-html="announcement.title"></h1>
 | 
			
		||||
					<div v-html="announcement.text"></div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="photos block">
 | 
			
		||||
			<header>%fa:images% %i18n:@photos%</header>
 | 
			
		||||
			<div>
 | 
			
		||||
				<div v-for="photo in photos" :style="`background-image: url(${photo.thumbnailUrl})`"></div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="nav block">
 | 
			
		||||
			<div>
 | 
			
		||||
				<mk-nav class="nav"/>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="side">
 | 
			
		||||
			<div class="trends block">
 | 
			
		||||
				<div>
 | 
			
		||||
					<mk-trends/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
			<div class="tl block">
 | 
			
		||||
				<header>%fa:comment-alt R% %i18n:@timeline%</header>
 | 
			
		||||
				<div>
 | 
			
		||||
					<mk-welcome-timeline class="tl" :max="20"/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="login">
 | 
			
		||||
					<mk-signin/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</main>
 | 
			
		||||
			<div class="hashtags">
 | 
			
		||||
				<router-link v-for="tag in tags" :key="tag" :to="`/tags/${ tag }`" :title="tag">#{{ tag }}</router-link>
 | 
			
		||||
			</div>
 | 
			
		||||
			<mk-nav class="nav"/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<mk-forkit class="forkit"/>
 | 
			
		||||
		<img src="assets/title.dark.svg" :alt="name">
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="tl">
 | 
			
		||||
		<mk-welcome-timeline :max="20"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<modal name="signup" width="500px" height="auto" scrollable>
 | 
			
		||||
		<header :class="$style.signupFormHeader">%i18n:@signup%</header>
 | 
			
		||||
		<mk-signup :class="$style.signupForm"/>
 | 
			
		||||
 | 
			
		||||
	<modal name="signup" :class="$store.state.device.darkmode ? 'modal-dark' : 'modal-light'" width="450px" height="auto" scrollable>
 | 
			
		||||
		<header class="formHeader">%i18n:@signup%</header>
 | 
			
		||||
		<mk-signup class="form"/>
 | 
			
		||||
	</modal>
 | 
			
		||||
 | 
			
		||||
	<modal name="signin" :class="$store.state.device.darkmode ? 'modal-dark' : 'modal-light'" width="450px" height="auto" scrollable>
 | 
			
		||||
		<header class="formHeader">%i18n:@signin%</header>
 | 
			
		||||
		<mk-signin class="form"/>
 | 
			
		||||
	</modal>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -47,6 +87,7 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { host, copyright } from '../../../config';
 | 
			
		||||
import { concat } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
@@ -56,43 +97,46 @@ export default Vue.extend({
 | 
			
		||||
			host,
 | 
			
		||||
			name: 'Misskey',
 | 
			
		||||
			description: '',
 | 
			
		||||
			pointerInterval: null,
 | 
			
		||||
			tags: []
 | 
			
		||||
			announcements: [],
 | 
			
		||||
			photos: []
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	created() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
			this.name = meta.name;
 | 
			
		||||
			this.description = meta.description;
 | 
			
		||||
			this.announcements = meta.broadcasts;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		(this as any).api('stats').then(stats => {
 | 
			
		||||
			this.stats = stats;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		(this as any).api('hashtags/trend').then(stats => {
 | 
			
		||||
			this.tags = stats.map(x => x.tag);
 | 
			
		||||
		const image = [
 | 
			
		||||
			'image/jpeg',
 | 
			
		||||
			'image/png',
 | 
			
		||||
			'image/gif'
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		(this as any).api('notes/local-timeline', {
 | 
			
		||||
			fileType: image,
 | 
			
		||||
			limit: 6
 | 
			
		||||
		}).then((notes: any[]) => {
 | 
			
		||||
			const files = concat(notes.map((n: any): any[] => n.files));
 | 
			
		||||
			this.photos = files.filter(f => image.includes(f.type)).slice(0, 6);
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.point();
 | 
			
		||||
		this.pointerInterval = setInterval(this.point, 100);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		clearInterval(this.pointerInterval);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		point() {
 | 
			
		||||
			const x = this.$refs.signup.getBoundingClientRect();
 | 
			
		||||
			this.$refs.pointer.style.top = x.top + x.height + 'px';
 | 
			
		||||
			this.$refs.pointer.style.left = x.left + 'px';
 | 
			
		||||
		},
 | 
			
		||||
		signup() {
 | 
			
		||||
			this.$modal.show('signup');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		signin() {
 | 
			
		||||
			this.$modal.show('signin');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		dark() {
 | 
			
		||||
			this.$store.commit('device/set', {
 | 
			
		||||
				key: 'darkmode',
 | 
			
		||||
@@ -103,11 +147,40 @@ export default Vue.extend({
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
#wait {
 | 
			
		||||
	right: auto;
 | 
			
		||||
	left: 15px;
 | 
			
		||||
}
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
#wait
 | 
			
		||||
	right auto
 | 
			
		||||
	left 15px
 | 
			
		||||
 | 
			
		||||
.v--modal-overlay
 | 
			
		||||
	background rgba(0, 0, 0, 0.6)
 | 
			
		||||
 | 
			
		||||
.modal-light
 | 
			
		||||
	.v--modal-box
 | 
			
		||||
		color #777
 | 
			
		||||
 | 
			
		||||
		.formHeader
 | 
			
		||||
			border-bottom solid 1px #eee
 | 
			
		||||
 | 
			
		||||
.modal-dark
 | 
			
		||||
	.v--modal-box
 | 
			
		||||
		background #313543
 | 
			
		||||
		color #fff
 | 
			
		||||
 | 
			
		||||
		.formHeader
 | 
			
		||||
			border-bottom solid 1px rgba(#000, 0.2)
 | 
			
		||||
 | 
			
		||||
.modal-light
 | 
			
		||||
.modal-dark
 | 
			
		||||
	.form
 | 
			
		||||
		padding 24px 48px 48px 48px
 | 
			
		||||
 | 
			
		||||
	.formHeader
 | 
			
		||||
		text-align center
 | 
			
		||||
		padding 48px 0 12px 0
 | 
			
		||||
		margin 0 48px
 | 
			
		||||
		font-size 1.5em
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
@@ -116,145 +189,170 @@ export default Vue.extend({
 | 
			
		||||
root(isDark)
 | 
			
		||||
	display flex
 | 
			
		||||
	min-height 100vh
 | 
			
		||||
	//background-color #00070F
 | 
			
		||||
	//background-image url('/assets/bg.jpg')
 | 
			
		||||
	//background-position center
 | 
			
		||||
	//background-size cover
 | 
			
		||||
 | 
			
		||||
	> .pointer
 | 
			
		||||
		display block
 | 
			
		||||
	> .forkit
 | 
			
		||||
		position absolute
 | 
			
		||||
		z-index 1
 | 
			
		||||
		top 0
 | 
			
		||||
		right 0
 | 
			
		||||
		width 180px
 | 
			
		||||
		margin 0 0 0 -180px
 | 
			
		||||
		transform rotateY(180deg) translateX(-10px) translateY(-48px)
 | 
			
		||||
		pointer-events none
 | 
			
		||||
 | 
			
		||||
	> button
 | 
			
		||||
		position fixed
 | 
			
		||||
		z-index 1
 | 
			
		||||
		top 0
 | 
			
		||||
		left 0
 | 
			
		||||
		bottom 16px
 | 
			
		||||
		left 16px
 | 
			
		||||
		padding 16px
 | 
			
		||||
		font-size 18px
 | 
			
		||||
		color #fff
 | 
			
		||||
 | 
			
		||||
		display none // TODO
 | 
			
		||||
		color isDark ? #fff : #444
 | 
			
		||||
 | 
			
		||||
	> .body
 | 
			
		||||
		flex 1
 | 
			
		||||
		padding 64px 0 0 0
 | 
			
		||||
		text-align center
 | 
			
		||||
		background #578394
 | 
			
		||||
		background-position center
 | 
			
		||||
		background-size cover
 | 
			
		||||
		display grid
 | 
			
		||||
		grid-template-rows 1fr 1fr 64px
 | 
			
		||||
		grid-template-columns 1fr 1fr 350px
 | 
			
		||||
		gap 16px
 | 
			
		||||
		width 100%
 | 
			
		||||
		max-width 1200px
 | 
			
		||||
		height 100vh
 | 
			
		||||
		min-height 950px
 | 
			
		||||
		margin 0 auto
 | 
			
		||||
		padding 64px
 | 
			
		||||
 | 
			
		||||
		&:before
 | 
			
		||||
			content ''
 | 
			
		||||
			display block
 | 
			
		||||
			position absolute
 | 
			
		||||
			top 0
 | 
			
		||||
			left 0
 | 
			
		||||
			right 0
 | 
			
		||||
			bottom 0
 | 
			
		||||
			background rgba(#000, 0.5)
 | 
			
		||||
		.block
 | 
			
		||||
			color isDark ? #fff : #444
 | 
			
		||||
			background isDark ? #282C37 : #fff
 | 
			
		||||
			box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
			//border-radius 8px
 | 
			
		||||
			overflow auto
 | 
			
		||||
 | 
			
		||||
		> .forkit
 | 
			
		||||
			position absolute
 | 
			
		||||
			top 0
 | 
			
		||||
			right 0
 | 
			
		||||
			> header
 | 
			
		||||
				z-index 1
 | 
			
		||||
				padding 0 16px
 | 
			
		||||
				line-height 48px
 | 
			
		||||
				background isDark ? #313543 : #fff
 | 
			
		||||
 | 
			
		||||
		> img
 | 
			
		||||
			position absolute
 | 
			
		||||
			bottom 16px
 | 
			
		||||
			right 16px
 | 
			
		||||
			width 150px
 | 
			
		||||
				if !isDark
 | 
			
		||||
					box-shadow 0 1px 0px rgba(0, 0, 0, 0.1)
 | 
			
		||||
 | 
			
		||||
		> .container
 | 
			
		||||
			$aboutWidth = 380px
 | 
			
		||||
			$loginWidth = 340px
 | 
			
		||||
			$width = $aboutWidth + $loginWidth
 | 
			
		||||
				& + div
 | 
			
		||||
					max-height calc(100% - 48px)
 | 
			
		||||
 | 
			
		||||
			> .info
 | 
			
		||||
				margin 0 auto 16px auto
 | 
			
		||||
				width $width
 | 
			
		||||
				font-size 14px
 | 
			
		||||
				color #fff
 | 
			
		||||
			> div
 | 
			
		||||
				overflow auto
 | 
			
		||||
 | 
			
		||||
				> .stats
 | 
			
		||||
					margin-left 16px
 | 
			
		||||
					padding-left 16px
 | 
			
		||||
					border-left solid 1px #fff
 | 
			
		||||
		> .main
 | 
			
		||||
			grid-row 1
 | 
			
		||||
			grid-column 1 / 3
 | 
			
		||||
			border-top solid 5px $theme-color
 | 
			
		||||
 | 
			
		||||
					> *
 | 
			
		||||
						margin-right 16px
 | 
			
		||||
			> div
 | 
			
		||||
				padding 32px
 | 
			
		||||
				min-height 100%
 | 
			
		||||
 | 
			
		||||
			> main
 | 
			
		||||
				display flex
 | 
			
		||||
				margin auto
 | 
			
		||||
				width $width
 | 
			
		||||
				border-radius 8px
 | 
			
		||||
				overflow hidden
 | 
			
		||||
				box-shadow 0 2px 8px rgba(#000, 0.3)
 | 
			
		||||
				> h1
 | 
			
		||||
					margin 0
 | 
			
		||||
 | 
			
		||||
				> .about
 | 
			
		||||
					width $aboutWidth
 | 
			
		||||
					color #444
 | 
			
		||||
					background #fff
 | 
			
		||||
					> img
 | 
			
		||||
						margin -8px 0 0 -16px
 | 
			
		||||
						max-width 280px
 | 
			
		||||
 | 
			
		||||
				> .info
 | 
			
		||||
					margin 0 auto 16px auto
 | 
			
		||||
					width $width
 | 
			
		||||
					font-size 14px
 | 
			
		||||
 | 
			
		||||
					> .stats
 | 
			
		||||
						margin-left 16px
 | 
			
		||||
						padding-left 16px
 | 
			
		||||
						border-left solid 1px isDark ? #fff : #444
 | 
			
		||||
 | 
			
		||||
						> *
 | 
			
		||||
							margin-right 16px
 | 
			
		||||
 | 
			
		||||
				> .sign
 | 
			
		||||
					font-size 120%
 | 
			
		||||
 | 
			
		||||
					> .divider
 | 
			
		||||
						margin 0 16px
 | 
			
		||||
 | 
			
		||||
					> .signin
 | 
			
		||||
					> .signup
 | 
			
		||||
						cursor pointer
 | 
			
		||||
 | 
			
		||||
						&:hover
 | 
			
		||||
							color $theme-color
 | 
			
		||||
 | 
			
		||||
				> .char
 | 
			
		||||
					display block
 | 
			
		||||
					position absolute
 | 
			
		||||
					right 0
 | 
			
		||||
					bottom 0
 | 
			
		||||
					width 180px
 | 
			
		||||
					opacity 0.3
 | 
			
		||||
 | 
			
		||||
				> *:not(.char)
 | 
			
		||||
					z-index 1
 | 
			
		||||
 | 
			
		||||
		> .announcements
 | 
			
		||||
			grid-row 2
 | 
			
		||||
			grid-column 1
 | 
			
		||||
 | 
			
		||||
			> div
 | 
			
		||||
				padding 32px
 | 
			
		||||
 | 
			
		||||
				> div
 | 
			
		||||
					padding 0 0 16px 0
 | 
			
		||||
					margin 0 0 16px 0
 | 
			
		||||
					border-bottom 1px solid isDark ? rgba(#000, 0.2) : rgba(#000, 0.05)
 | 
			
		||||
 | 
			
		||||
					> h1
 | 
			
		||||
						margin 0 0 16px 0
 | 
			
		||||
						padding 32px 32px 0 32px
 | 
			
		||||
						color #444
 | 
			
		||||
 | 
			
		||||
						> img
 | 
			
		||||
							width 170px
 | 
			
		||||
							vertical-align bottom
 | 
			
		||||
 | 
			
		||||
					> .powerd-by
 | 
			
		||||
						margin 16px
 | 
			
		||||
						opacity 0.7
 | 
			
		||||
 | 
			
		||||
					> .desc
 | 
			
		||||
						margin 0
 | 
			
		||||
						padding 0 32px 16px 32px
 | 
			
		||||
						font-size 1.25em
 | 
			
		||||
 | 
			
		||||
					> a
 | 
			
		||||
						display inline-block
 | 
			
		||||
						margin 0 0 32px 0
 | 
			
		||||
						font-weight bold
 | 
			
		||||
		> .photos
 | 
			
		||||
			grid-row 2
 | 
			
		||||
			grid-column 2
 | 
			
		||||
 | 
			
		||||
				> .login
 | 
			
		||||
					width $loginWidth
 | 
			
		||||
					padding 16px 32px 32px 32px
 | 
			
		||||
					background isDark ? #2e3440 : #f5f5f5
 | 
			
		||||
			> div
 | 
			
		||||
				display grid
 | 
			
		||||
				grid-template-rows 1fr 1fr 1fr
 | 
			
		||||
				grid-template-columns 1fr 1fr
 | 
			
		||||
				gap 8px
 | 
			
		||||
				height 100%
 | 
			
		||||
				padding 16px
 | 
			
		||||
 | 
			
		||||
			> .hashtags
 | 
			
		||||
				margin 16px auto
 | 
			
		||||
				width $width
 | 
			
		||||
				font-size 14px
 | 
			
		||||
				color #fff
 | 
			
		||||
				background rgba(#000, 0.3)
 | 
			
		||||
				border-radius 8px
 | 
			
		||||
				> div
 | 
			
		||||
					//border-radius 4px
 | 
			
		||||
					background-position center center
 | 
			
		||||
					background-size cover
 | 
			
		||||
 | 
			
		||||
				> *
 | 
			
		||||
					display inline-block
 | 
			
		||||
					margin 14px
 | 
			
		||||
		> .nav
 | 
			
		||||
			display flex
 | 
			
		||||
			justify-content center
 | 
			
		||||
			align-items center
 | 
			
		||||
			grid-row 3
 | 
			
		||||
			grid-column 1 / 3
 | 
			
		||||
			font-size 14px
 | 
			
		||||
 | 
			
		||||
			> .nav
 | 
			
		||||
				display block
 | 
			
		||||
				margin 16px 0
 | 
			
		||||
				font-size 14px
 | 
			
		||||
				color #fff
 | 
			
		||||
		> .side
 | 
			
		||||
			display grid
 | 
			
		||||
			grid-row 1 / 4
 | 
			
		||||
			grid-column 3
 | 
			
		||||
			grid-template-rows 1fr 350px
 | 
			
		||||
			grid-template-columns 1fr
 | 
			
		||||
			gap 16px
 | 
			
		||||
 | 
			
		||||
	> .tl
 | 
			
		||||
		margin 0
 | 
			
		||||
		width 410px
 | 
			
		||||
		height 100vh
 | 
			
		||||
		text-align left
 | 
			
		||||
		background isDark ? #313543 : #fff
 | 
			
		||||
			> .tl
 | 
			
		||||
				grid-row 1
 | 
			
		||||
				grid-column 1
 | 
			
		||||
				overflow auto
 | 
			
		||||
 | 
			
		||||
		> *
 | 
			
		||||
			max-height 100%
 | 
			
		||||
			overflow auto
 | 
			
		||||
			> .trends
 | 
			
		||||
				grid-row 2
 | 
			
		||||
				grid-column 1
 | 
			
		||||
				padding 8px
 | 
			
		||||
 | 
			
		||||
.mk-welcome[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
@@ -263,29 +361,3 @@ root(isDark)
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.signupForm
 | 
			
		||||
	padding 24px 48px 48px 48px
 | 
			
		||||
 | 
			
		||||
.signupFormHeader
 | 
			
		||||
	padding 48px 0 12px 0
 | 
			
		||||
	margin: 0 48px
 | 
			
		||||
	font-size 1.5em
 | 
			
		||||
	color #777
 | 
			
		||||
	border-bottom solid 1px #eee
 | 
			
		||||
 | 
			
		||||
.signinForm
 | 
			
		||||
	padding 24px 48px 48px 48px
 | 
			
		||||
 | 
			
		||||
.signinFormHeader
 | 
			
		||||
	padding 48px 0 12px 0
 | 
			
		||||
	margin: 0 48px
 | 
			
		||||
	font-size 1.5em
 | 
			
		||||
	color #777
 | 
			
		||||
	border-bottom solid 1px #eee
 | 
			
		||||
 | 
			
		||||
.nav
 | 
			
		||||
	a
 | 
			
		||||
		color #666
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ export default define({
 | 
			
		||||
				offset: this.offset,
 | 
			
		||||
				renote: false,
 | 
			
		||||
				reply: false,
 | 
			
		||||
				media: false,
 | 
			
		||||
				file: false,
 | 
			
		||||
				poll: false
 | 
			
		||||
			}).then(notes => {
 | 
			
		||||
				const note = notes ? notes[0] : null;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,12 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import Vuex from 'vuex';
 | 
			
		||||
import VueRouter from 'vue-router';
 | 
			
		||||
import VModal from 'vue-js-modal';
 | 
			
		||||
import * as TreeView from 'vue-json-tree-view';
 | 
			
		||||
import VAnimateCss from 'v-animate-css';
 | 
			
		||||
import Element from 'element-ui';
 | 
			
		||||
import ElementLocaleEn from 'element-ui/lib/locale/lang/en';
 | 
			
		||||
import ElementLocaleJa from 'element-ui/lib/locale/lang/ja';
 | 
			
		||||
import VueThinModal from 'vue-thin-modal';
 | 
			
		||||
import 'vue-thin-modal/dist/vue-thin-modal.css';
 | 
			
		||||
import VModal from 'vue-js-modal';
 | 
			
		||||
 | 
			
		||||
import App from './app.vue';
 | 
			
		||||
import checkForUpdate from './common/scripts/check-for-update';
 | 
			
		||||
@@ -28,13 +26,10 @@ switch (lang) {
 | 
			
		||||
 | 
			
		||||
Vue.use(Vuex);
 | 
			
		||||
Vue.use(VueRouter);
 | 
			
		||||
Vue.use(VModal);
 | 
			
		||||
Vue.use(TreeView);
 | 
			
		||||
Vue.use(VAnimateCss);
 | 
			
		||||
Vue.use(Element, { locale: elementLocale });
 | 
			
		||||
Vue.use(VueThinModal, {
 | 
			
		||||
	autoMountPortal: false
 | 
			
		||||
});
 | 
			
		||||
Vue.use(VModal);
 | 
			
		||||
 | 
			
		||||
// Register global directives
 | 
			
		||||
require('./common/views/directives');
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import { EventEmitter } from 'eventemitter3';
 | 
			
		||||
import * as uuid from 'uuid';
 | 
			
		||||
 | 
			
		||||
import initStore from './store';
 | 
			
		||||
import { apiUrl, swPublickey, version, lang, googleMapsApiKey } from './config';
 | 
			
		||||
import { apiUrl, version, lang } from './config';
 | 
			
		||||
import Progress from './common/scripts/loading';
 | 
			
		||||
import Connection from './common/scripts/streaming/stream';
 | 
			
		||||
import { HomeStreamManager } from './common/scripts/streaming/home';
 | 
			
		||||
@@ -17,6 +17,7 @@ import Err from './common/views/components/connect-failed.vue';
 | 
			
		||||
import { LocalTimelineStreamManager } from './common/scripts/streaming/local-timeline';
 | 
			
		||||
import { HybridTimelineStreamManager } from './common/scripts/streaming/hybrid-timeline';
 | 
			
		||||
import { GlobalTimelineStreamManager } from './common/scripts/streaming/global-timeline';
 | 
			
		||||
import { erase } from '../../prelude/array';
 | 
			
		||||
 | 
			
		||||
//#region api requests
 | 
			
		||||
let spinner = null;
 | 
			
		||||
@@ -230,13 +231,13 @@ export default class MiOS extends EventEmitter {
 | 
			
		||||
		//#region Init stream managers
 | 
			
		||||
		this.streams.serverStatsStream = new ServerStatsStreamManager(this);
 | 
			
		||||
		this.streams.notesStatsStream = new NotesStatsStreamManager(this);
 | 
			
		||||
		this.streams.localTimelineStream = new LocalTimelineStreamManager(this, this.store.state.i);
 | 
			
		||||
 | 
			
		||||
		this.once('signedin', () => {
 | 
			
		||||
			// Init home stream manager
 | 
			
		||||
			this.stream = new HomeStreamManager(this, this.store.state.i);
 | 
			
		||||
 | 
			
		||||
			// Init other stream manager
 | 
			
		||||
			this.streams.localTimelineStream = new LocalTimelineStreamManager(this, this.store.state.i);
 | 
			
		||||
			this.streams.hybridTimelineStream = new HybridTimelineStreamManager(this, this.store.state.i);
 | 
			
		||||
			this.streams.globalTimelineStream = new GlobalTimelineStreamManager(this, this.store.state.i);
 | 
			
		||||
			this.streams.driveStream = new DriveStreamManager(this, this.store.state.i);
 | 
			
		||||
@@ -361,7 +362,7 @@ export default class MiOS extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
				// A public key your push server will use to send
 | 
			
		||||
				// messages to client apps via a push server.
 | 
			
		||||
				applicationServerKey: urlBase64ToUint8Array(swPublickey)
 | 
			
		||||
				applicationServerKey: urlBase64ToUint8Array(this.meta.data.swPublickey)
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			// Subscribe push notification
 | 
			
		||||
@@ -537,7 +538,7 @@ export default class MiOS extends EventEmitter {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public unregisterStreamConnection(connection: Connection) {
 | 
			
		||||
		this.connections = this.connections.filter(c => c != connection);
 | 
			
		||||
		this.connections = erase(connection, this.connections);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								src/client/app/mobile/api/post.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/client/app/mobile/api/post.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import PostForm from '../views/components/post-form-dialog.vue';
 | 
			
		||||
 | 
			
		||||
export default (os) => (opts) => {
 | 
			
		||||
	const o = opts || {};
 | 
			
		||||
 | 
			
		||||
	document.documentElement.style.overflow = 'hidden';
 | 
			
		||||
 | 
			
		||||
	function recover() {
 | 
			
		||||
		document.documentElement.style.overflow = 'auto';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const vm = new PostForm({
 | 
			
		||||
		parent: os.app,
 | 
			
		||||
		propsData: {
 | 
			
		||||
			reply: o.reply,
 | 
			
		||||
			renote: o.renote
 | 
			
		||||
		}
 | 
			
		||||
	}).$mount();
 | 
			
		||||
	vm.$once('cancel', recover);
 | 
			
		||||
	vm.$once('posted', recover);
 | 
			
		||||
	document.body.appendChild(vm.$el);
 | 
			
		||||
	(vm as any).focus();
 | 
			
		||||
};
 | 
			
		||||
@@ -14,6 +14,7 @@ import chooseDriveFolder from './api/choose-drive-folder';
 | 
			
		||||
import chooseDriveFile from './api/choose-drive-file';
 | 
			
		||||
import dialog from './api/dialog';
 | 
			
		||||
import input from './api/input';
 | 
			
		||||
import post from './api/post';
 | 
			
		||||
import notify from './api/notify';
 | 
			
		||||
 | 
			
		||||
import MkIndex from './views/pages/index.vue';
 | 
			
		||||
@@ -90,7 +91,7 @@ init((launch) => {
 | 
			
		||||
		chooseDriveFile,
 | 
			
		||||
		dialog: dialog(os),
 | 
			
		||||
		input,
 | 
			
		||||
		post: () => alert('deprecated'),
 | 
			
		||||
		post: post(os),
 | 
			
		||||
		notify
 | 
			
		||||
	}));
 | 
			
		||||
}, true);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,13 +17,3 @@ body
 | 
			
		||||
	display flex
 | 
			
		||||
	flex-direction column
 | 
			
		||||
	min-height 100%
 | 
			
		||||
 | 
			
		||||
.modal-backdrop
 | 
			
		||||
	z-index 10000 !important
 | 
			
		||||
 | 
			
		||||
.modal-content-wrapper
 | 
			
		||||
	z-index 10001 !important
 | 
			
		||||
 | 
			
		||||
.modal-content
 | 
			
		||||
	padding 0 !important
 | 
			
		||||
	background-color transparent !important
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-drive-file-chooser">
 | 
			
		||||
<div class="cdxzvcfawjxdyxsekbxbfgtplebnoneb">
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<header>
 | 
			
		||||
			<h1>%i18n:@select-file%<span class="count" v-if="files.length > 0">({{ files.length }})</span></h1>
 | 
			
		||||
			<button class="close" @click="cancel">%fa:times%</button>
 | 
			
		||||
			<button v-if="multiple" class="ok" @click="ok">%fa:check%</button>
 | 
			
		||||
		</header>
 | 
			
		||||
		<mk-drive ref="browser"
 | 
			
		||||
		<mk-drive class="drive" ref="browser"
 | 
			
		||||
			:select-file="true"
 | 
			
		||||
			:multiple="multiple"
 | 
			
		||||
			@change-selection="onChangeSelection"
 | 
			
		||||
@@ -46,7 +46,7 @@ export default Vue.extend({
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-drive-file-chooser
 | 
			
		||||
root(isDark)
 | 
			
		||||
	position fixed
 | 
			
		||||
	z-index 20000
 | 
			
		||||
	top 0
 | 
			
		||||
@@ -59,10 +59,11 @@ export default Vue.extend({
 | 
			
		||||
	> .body
 | 
			
		||||
		width 100%
 | 
			
		||||
		height 100%
 | 
			
		||||
		background #fff
 | 
			
		||||
		background isDark ? #282c37 : #fff
 | 
			
		||||
 | 
			
		||||
		> header
 | 
			
		||||
			border-bottom solid 1px #eee
 | 
			
		||||
			border-bottom solid 1px isDark ? #1b1f29 : #eee
 | 
			
		||||
			color isDark ? #fff : #111
 | 
			
		||||
 | 
			
		||||
			> h1
 | 
			
		||||
				margin 0
 | 
			
		||||
@@ -90,9 +91,15 @@ export default Vue.extend({
 | 
			
		||||
				line-height 42px
 | 
			
		||||
				width 42px
 | 
			
		||||
 | 
			
		||||
		> .mk-drive
 | 
			
		||||
		> .drive
 | 
			
		||||
			height calc(100% - 42px)
 | 
			
		||||
			overflow scroll
 | 
			
		||||
			-webkit-overflow-scrolling touch
 | 
			
		||||
 | 
			
		||||
.cdxzvcfawjxdyxsekbxbfgtplebnoneb[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.cdxzvcfawjxdyxsekbxbfgtplebnoneb:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as EXIF from 'exif-js';
 | 
			
		||||
import * as hljs from 'highlight.js';
 | 
			
		||||
import gcd from '../../../common/scripts/gcd';
 | 
			
		||||
import { gcd } from '../../../../../prelude/math';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['file'],
 | 
			
		||||
 
 | 
			
		||||
@@ -48,12 +48,14 @@ export default Vue.extend({
 | 
			
		||||
		onFollow(user) {
 | 
			
		||||
			if (user.id == this.u.id) {
 | 
			
		||||
				this.u.isFollowing = user.isFollowing;
 | 
			
		||||
				this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onUnfollow(user) {
 | 
			
		||||
			if (user.id == this.u.id) {
 | 
			
		||||
				this.u.isFollowing = user.isFollowing;
 | 
			
		||||
				this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +68,7 @@ export default Vue.extend({
 | 
			
		||||
						userId: this.u.id
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					if (this.u.isLocked && this.u.hasPendingFollowRequestFromYou) {
 | 
			
		||||
					if (this.u.hasPendingFollowRequestFromYou) {
 | 
			
		||||
						this.u = await (this as any).api('following/requests/cancel', {
 | 
			
		||||
							userId: this.u.id
 | 
			
		||||
						});
 | 
			
		||||
 
 | 
			
		||||
@@ -40,8 +40,8 @@
 | 
			
		||||
				<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
			
		||||
				<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="media" v-if="p.media.length > 0">
 | 
			
		||||
				<mk-media-list :media-list="p.media" :raw="true"/>
 | 
			
		||||
			<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
				<mk-media-list :media-list="p.files" :raw="true"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<mk-poll v-if="p.poll" :note="p"/>
 | 
			
		||||
			<mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="true"/>
 | 
			
		||||
@@ -75,13 +75,6 @@
 | 
			
		||||
	<div class="replies" v-if="!compact">
 | 
			
		||||
		<x-sub v-for="note in replies" :key="note.id" :note="note"/>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<modal name="replyForm">
 | 
			
		||||
		<mk-post-form @posted="replyFormClosed" @cancel="replyFormClosed" :reply="p"/>
 | 
			
		||||
	</modal>
 | 
			
		||||
	<modal name="renoteForm">
 | 
			
		||||
		<mk-post-form @posted="renoteFormClosed" @cancel="renoteFormClosed" :renote="p"/>
 | 
			
		||||
	</modal>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -92,6 +85,7 @@ import parse from '../../../../../mfm/parse';
 | 
			
		||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
 | 
			
		||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
 | 
			
		||||
import XSub from './note.sub.vue';
 | 
			
		||||
import { sum } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -120,7 +114,7 @@ export default Vue.extend({
 | 
			
		||||
		isRenote(): boolean {
 | 
			
		||||
			return (this.note.renote &&
 | 
			
		||||
				this.note.text == null &&
 | 
			
		||||
				this.note.mediaIds.length == 0 &&
 | 
			
		||||
				this.note.fileIds.length == 0 &&
 | 
			
		||||
				this.note.poll == null);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -130,9 +124,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		reactionsCount(): number {
 | 
			
		||||
			return this.p.reactionCounts
 | 
			
		||||
				? Object.keys(this.p.reactionCounts)
 | 
			
		||||
					.map(key => this.p.reactionCounts[key])
 | 
			
		||||
					.reduce((a, b) => a + b)
 | 
			
		||||
				? sum(Object.values(this.p.reactionCounts))
 | 
			
		||||
				: 0;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -192,19 +184,15 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		reply() {
 | 
			
		||||
			this.$modal.push('replyForm');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		replyFormClosed() {
 | 
			
		||||
			this.$modal.pop();
 | 
			
		||||
			(this as any).apis.post({
 | 
			
		||||
				reply: this.p
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		renote() {
 | 
			
		||||
			this.$modal.push('renoteForm');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		renoteFormClosed() {
 | 
			
		||||
			this.$modal.pop();
 | 
			
		||||
			(this as any).apis.post({
 | 
			
		||||
				renote: this.p
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		react() {
 | 
			
		||||
@@ -380,7 +368,7 @@ root(isDark)
 | 
			
		||||
			> .mk-url-preview
 | 
			
		||||
				margin-top 8px
 | 
			
		||||
 | 
			
		||||
			> .media
 | 
			
		||||
			> .files
 | 
			
		||||
				> img
 | 
			
		||||
					display block
 | 
			
		||||
					max-width 100%
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@
 | 
			
		||||
						<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/>
 | 
			
		||||
						<a class="rp" v-if="p.renote != null">RP:</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="media" v-if="p.media.length > 0">
 | 
			
		||||
						<mk-media-list :media-list="p.media"/>
 | 
			
		||||
					<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
						<mk-media-list :media-list="p.files"/>
 | 
			
		||||
					</div>
 | 
			
		||||
					<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
 | 
			
		||||
					<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
 | 
			
		||||
@@ -60,13 +60,6 @@
 | 
			
		||||
			</footer>
 | 
			
		||||
		</div>
 | 
			
		||||
	</article>
 | 
			
		||||
 | 
			
		||||
	<modal name="replyForm">
 | 
			
		||||
		<mk-post-form @posted="replyFormClosed" @cancel="replyFormClosed" :reply="p"/>
 | 
			
		||||
	</modal>
 | 
			
		||||
	<modal name="renoteForm">
 | 
			
		||||
		<mk-post-form @posted="renoteFormClosed" @cancel="renoteFormClosed" :renote="p"/>
 | 
			
		||||
	</modal>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -77,6 +70,7 @@ import parse from '../../../../../mfm/parse';
 | 
			
		||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
 | 
			
		||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
 | 
			
		||||
import XSub from './note.sub.vue';
 | 
			
		||||
import { sum } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -97,7 +91,7 @@ export default Vue.extend({
 | 
			
		||||
		isRenote(): boolean {
 | 
			
		||||
			return (this.note.renote &&
 | 
			
		||||
				this.note.text == null &&
 | 
			
		||||
				this.note.mediaIds.length == 0 &&
 | 
			
		||||
				this.note.fileIds.length == 0 &&
 | 
			
		||||
				this.note.poll == null);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -107,9 +101,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		reactionsCount(): number {
 | 
			
		||||
			return this.p.reactionCounts
 | 
			
		||||
				? Object.keys(this.p.reactionCounts)
 | 
			
		||||
					.map(key => this.p.reactionCounts[key])
 | 
			
		||||
					.reduce((a, b) => a + b)
 | 
			
		||||
				? sum(Object.values(this.p.reactionCounts))
 | 
			
		||||
				: 0;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -202,19 +194,15 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		reply() {
 | 
			
		||||
			this.$modal.push('replyForm');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		replyFormClosed() {
 | 
			
		||||
			this.$modal.pop();
 | 
			
		||||
			(this as any).apis.post({
 | 
			
		||||
				reply: this.p
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		renote() {
 | 
			
		||||
			this.$modal.push('renoteForm');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		renoteFormClosed() {
 | 
			
		||||
			this.$modal.pop();
 | 
			
		||||
			(this as any).apis.post({
 | 
			
		||||
				renote: this.p
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		react() {
 | 
			
		||||
@@ -425,7 +413,7 @@ root(isDark)
 | 
			
		||||
					.mk-url-preview
 | 
			
		||||
						margin-top 8px
 | 
			
		||||
 | 
			
		||||
					> .media
 | 
			
		||||
					> .files
 | 
			
		||||
						> img
 | 
			
		||||
							display block
 | 
			
		||||
							max-width 100%
 | 
			
		||||
@@ -482,10 +470,6 @@ root(isDark)
 | 
			
		||||
					&.reacted
 | 
			
		||||
						color $theme-color
 | 
			
		||||
 | 
			
		||||
					&.menu
 | 
			
		||||
						@media (max-width 350px)
 | 
			
		||||
							display none
 | 
			
		||||
 | 
			
		||||
.note[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ export default Vue.extend({
 | 
			
		||||
		prepend(note, silent = false) {
 | 
			
		||||
			//#region 弾く
 | 
			
		||||
			const isMyNote = note.userId == this.$store.state.i.id;
 | 
			
		||||
			const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
 | 
			
		||||
			const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
 | 
			
		||||
 | 
			
		||||
			if (this.$store.state.settings.showMyRenotes === false) {
 | 
			
		||||
				if (isMyNote && isPureRenote) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-notify">
 | 
			
		||||
	<mk-notification-preview :notification="notification"/>
 | 
			
		||||
<div class="mk-notify" :class="pos">
 | 
			
		||||
	<div>
 | 
			
		||||
		<mk-notification-preview :notification="notification"/>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -10,11 +12,16 @@ import * as anime from 'animejs';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['notification'],
 | 
			
		||||
	computed: {
 | 
			
		||||
		pos() {
 | 
			
		||||
			return this.$store.state.device.mobileNotificationPosition;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$el,
 | 
			
		||||
				bottom: '0px',
 | 
			
		||||
				[this.pos]: '0px',
 | 
			
		||||
				duration: 500,
 | 
			
		||||
				easing: 'easeOutQuad'
 | 
			
		||||
			});
 | 
			
		||||
@@ -22,7 +29,7 @@ export default Vue.extend({
 | 
			
		||||
			setTimeout(() => {
 | 
			
		||||
				anime({
 | 
			
		||||
					targets: this.$el,
 | 
			
		||||
					bottom: '-64px',
 | 
			
		||||
					[this.pos]: `-${this.$el.offsetHeight}px`,
 | 
			
		||||
					duration: 500,
 | 
			
		||||
					easing: 'easeOutQuad',
 | 
			
		||||
					complete: () => this.$destroy()
 | 
			
		||||
@@ -35,15 +42,32 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-notify
 | 
			
		||||
	$height = 78px
 | 
			
		||||
 | 
			
		||||
	position fixed
 | 
			
		||||
	z-index 1024
 | 
			
		||||
	bottom -64px
 | 
			
		||||
	left 0
 | 
			
		||||
	right 0
 | 
			
		||||
	width 100%
 | 
			
		||||
	height 64px
 | 
			
		||||
	max-width 500px
 | 
			
		||||
	height $height
 | 
			
		||||
	margin 0 auto
 | 
			
		||||
	padding 8px
 | 
			
		||||
	pointer-events none
 | 
			
		||||
	-webkit-backdrop-filter blur(2px)
 | 
			
		||||
	backdrop-filter blur(2px)
 | 
			
		||||
	background-color rgba(#000, 0.5)
 | 
			
		||||
	font-size 80%
 | 
			
		||||
 | 
			
		||||
	&.bottom
 | 
			
		||||
		bottom -($height)
 | 
			
		||||
 | 
			
		||||
	&.top
 | 
			
		||||
		top -($height)
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		height 100%
 | 
			
		||||
		-webkit-backdrop-filter blur(2px)
 | 
			
		||||
		backdrop-filter blur(2px)
 | 
			
		||||
		background-color rgba(#000, 0.5)
 | 
			
		||||
		border-radius 7px
 | 
			
		||||
		overflow hidden
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								src/client/app/mobile/views/components/post-form-dialog.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/client/app/mobile/views/components/post-form-dialog.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ulveipglmagnxfgvitaxyszerjwiqmwl">
 | 
			
		||||
	<div class="bg" ref="bg"></div>
 | 
			
		||||
	<div class="main" ref="main">
 | 
			
		||||
		<mk-post-form ref="form"
 | 
			
		||||
			:reply="reply"
 | 
			
		||||
			:renote="renote"
 | 
			
		||||
			:initial-text="initialText"
 | 
			
		||||
			:instant="instant"
 | 
			
		||||
			@posted="onPosted"
 | 
			
		||||
			@cancel="onCanceled"/>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as anime from 'animejs';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		reply: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		renote: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		initialText: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		instant: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			(this.$refs.bg as any).style.pointerEvents = 'auto';
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$refs.bg,
 | 
			
		||||
				opacity: 1,
 | 
			
		||||
				duration: 100,
 | 
			
		||||
				easing: 'linear'
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$refs.main,
 | 
			
		||||
				opacity: 1,
 | 
			
		||||
				translateY: [-16, 0],
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				easing: 'easeOutQuad'
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		focus() {
 | 
			
		||||
			this.$refs.form.focus();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		close() {
 | 
			
		||||
			(this.$refs.bg as any).style.pointerEvents = 'none';
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$refs.bg,
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				easing: 'linear'
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			(this.$refs.main as any).style.pointerEvents = 'none';
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$refs.main,
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				translateY: 16,
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				easing: 'easeOutQuad',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onPosted() {
 | 
			
		||||
			this.$emit('posted');
 | 
			
		||||
			this.close();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onCanceled() {
 | 
			
		||||
			this.$emit('cancel');
 | 
			
		||||
			this.close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.ulveipglmagnxfgvitaxyszerjwiqmwl
 | 
			
		||||
	> .bg
 | 
			
		||||
		display block
 | 
			
		||||
		position fixed
 | 
			
		||||
		z-index 10000
 | 
			
		||||
		top 0
 | 
			
		||||
		left 0
 | 
			
		||||
		width 100%
 | 
			
		||||
		height 100%
 | 
			
		||||
		background rgba(#000, 0.7)
 | 
			
		||||
		opacity 0
 | 
			
		||||
		pointer-events none
 | 
			
		||||
 | 
			
		||||
	> .main
 | 
			
		||||
		display block
 | 
			
		||||
		position fixed
 | 
			
		||||
		z-index 10000
 | 
			
		||||
		top 0
 | 
			
		||||
		left 0
 | 
			
		||||
		right 0
 | 
			
		||||
		height 100%
 | 
			
		||||
		overflow auto
 | 
			
		||||
		margin 0 auto 0 auto
 | 
			
		||||
		opacity 0
 | 
			
		||||
		transform translateY(-16px)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -59,6 +59,7 @@ import MkVisibilityChooser from '../../../common/views/components/visibility-cho
 | 
			
		||||
import getFace from '../../../common/scripts/get-face';
 | 
			
		||||
import parse from '../../../../../mfm/parse';
 | 
			
		||||
import { host } from '../../../config';
 | 
			
		||||
import { erase } from '../../../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -105,9 +106,9 @@ export default Vue.extend({
 | 
			
		||||
	computed: {
 | 
			
		||||
		draftId(): string {
 | 
			
		||||
			return this.renote
 | 
			
		||||
				? 'renote:' + this.renote.id
 | 
			
		||||
				? `renote:${this.renote.id}`
 | 
			
		||||
				: this.reply
 | 
			
		||||
					? 'reply:' + this.reply.id
 | 
			
		||||
					? `reply:${this.reply.id}`
 | 
			
		||||
					: 'note';
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -170,6 +171,8 @@ export default Vue.extend({
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.focus();
 | 
			
		||||
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			this.focus();
 | 
			
		||||
		});
 | 
			
		||||
@@ -198,12 +201,12 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		attachMedia(driveFile) {
 | 
			
		||||
			this.files.push(driveFile);
 | 
			
		||||
			this.$emit('change-attached-media', this.files);
 | 
			
		||||
			this.$emit('change-attached-files', this.files);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		detachMedia(file) {
 | 
			
		||||
			this.files = this.files.filter(x => x.id != file.id);
 | 
			
		||||
			this.$emit('change-attached-media', this.files);
 | 
			
		||||
			this.$emit('change-attached-files', this.files);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeFile() {
 | 
			
		||||
@@ -227,7 +230,7 @@ export default Vue.extend({
 | 
			
		||||
			navigator.geolocation.getCurrentPosition(pos => {
 | 
			
		||||
				this.geo = pos.coords;
 | 
			
		||||
			}, err => {
 | 
			
		||||
				alert('%i18n:@error%: ' + err.message);
 | 
			
		||||
				alert(`%i18n:@error%: ${err.message}`);
 | 
			
		||||
			}, {
 | 
			
		||||
					enableHighAccuracy: true
 | 
			
		||||
				});
 | 
			
		||||
@@ -260,14 +263,14 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		removeVisibleUser(user) {
 | 
			
		||||
			this.visibleUsers = this.visibleUsers.filter(u => u != user);
 | 
			
		||||
			this.visibleUsers = erase(user, this.visibleUsers);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		clear() {
 | 
			
		||||
			this.text = '';
 | 
			
		||||
			this.files = [];
 | 
			
		||||
			this.poll = false;
 | 
			
		||||
			this.$emit('change-attached-media');
 | 
			
		||||
			this.$emit('change-attached-files');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		post() {
 | 
			
		||||
@@ -275,7 +278,7 @@ export default Vue.extend({
 | 
			
		||||
			const viaMobile = this.$store.state.settings.disableViaMobile !== true;
 | 
			
		||||
			(this as any).api('notes/create', {
 | 
			
		||||
				text: this.text == '' ? undefined : this.text,
 | 
			
		||||
				mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
			
		||||
				fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
			
		||||
				replyId: this.reply ? this.reply.id : undefined,
 | 
			
		||||
				renoteId: this.renote ? this.renote.id : undefined,
 | 
			
		||||
				poll: this.poll ? (this.$refs.poll as any).get() : undefined,
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user