Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -1,18 +1,19 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# BEARER_TOKEN=
|
# __MISSKEY_BEARER_TOKEN=
|
||||||
# CAMPAIGN_ID=
|
# __MISSKEY_CAMPAIGN_ID=
|
||||||
# GITHUB_TOKEN=
|
# __MISSKEY_GITHUB_TOKEN=
|
||||||
# HEAD='acid-chicken:patch-autogen'
|
# __MISSKEY_HEAD=acid-chicken:patch-autogen
|
||||||
# REPO='syuilo/misskey'
|
# __MISSKEY_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_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)/.." && \
|
cd "$(dirname $0)/.." && \
|
||||||
touch null.cache && \
|
touch null.cache && \
|
||||||
rm *.cache && \
|
rm *.cache && \
|
||||||
git checkout master && \
|
git checkout $__MISSKEY_BRANCH && \
|
||||||
git pull origin master && \
|
git pull origin $__MISSKEY_BRANCH && \
|
||||||
git pull upstream master && \
|
git pull upstream $__MISSKEY_BRANCH && \
|
||||||
git stash && \
|
git stash && \
|
||||||
git rebase -f upstream/master && \
|
git rebase -f upstream/$__MISSKEY_BRANCH && \
|
||||||
git branch patch-autogen && \
|
git branch patch-autogen && \
|
||||||
git checkout patch-autogen && \
|
git checkout patch-autogen && \
|
||||||
git reset --hard HEAD || \
|
git reset --hard HEAD || \
|
||||||
@@ -20,12 +21,12 @@ exit 1
|
|||||||
touch patreon.md.cache && \
|
touch patreon.md.cache && \
|
||||||
rm patreon.md.cache && \
|
rm patreon.md.cache && \
|
||||||
echo '<!-- PATREON_START -->' > 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 :
|
while :
|
||||||
do
|
do
|
||||||
touch patreon.raw.cache && \
|
touch patreon.raw.cache && \
|
||||||
rm 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 && \
|
touch patreon.cache && \
|
||||||
rm patreon.cache && \
|
rm patreon.cache && \
|
||||||
cat patreon.raw.cache | \
|
cat patreon.raw.cache | \
|
||||||
@@ -42,31 +43,31 @@ while :
|
|||||||
xargs -I% echo '<td><a href="%</a></td>' >> patreon.md.cache && \
|
xargs -I% echo '<td><a href="%</a></td>' >> patreon.md.cache && \
|
||||||
echo '</tr></table>' >> patreon.md.cache || \
|
echo '</tr></table>' >> patreon.md.cache || \
|
||||||
exit 1
|
exit 1
|
||||||
NEW_URL="$(cat patreon.raw.cache | jq -r '.links.next')"
|
new_url="$(cat patreon.raw.cache | jq -r '.links.next')"
|
||||||
test "$NEW_URL" = 'null' && \
|
test "$new_url" = 'null' && \
|
||||||
break || \
|
break || \
|
||||||
URL="$NEW_URL"
|
URL="$url"
|
||||||
done
|
done
|
||||||
IGNORE= && \
|
ignore= && \
|
||||||
echo -e "\n**Last updated:** $(date -uR | sed 's/\+0000/UTC/')\n<!-- PATREON_END -->" >> patreon.md.cache && \
|
echo -e "\n**Last updated:** $(date -uR | sed 's/\+0000/UTC/')\n<!-- PATREON_END -->" >> patreon.md.cache && \
|
||||||
touch README.md && \
|
touch README.md && \
|
||||||
touch .autogen/README.md && \
|
touch .autogen/README.md && \
|
||||||
rm .autogen/README.md && \
|
rm .autogen/README.md && \
|
||||||
mv README.md .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
|
do
|
||||||
if [[ -z "$IGNORE" ]]
|
if [[ -z "$ignore" ]]
|
||||||
then
|
then
|
||||||
if [[ "$LINE" = '<!-- PATREON_START -->' ]]
|
if [[ "$line" = '<!-- PATREON_START -->' ]]
|
||||||
then
|
then
|
||||||
IGNORE='PATREON_INSIDE'
|
ignore='PATREON_INSIDE'
|
||||||
else
|
else
|
||||||
echo "$LINE" >> README.md
|
echo "$line" >> README.md
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ "$LINE" = '<!-- PATREON_END -->' ]]
|
if [[ "$LINE" = '<!-- PATREON_END -->' ]]
|
||||||
then
|
then
|
||||||
IGNORE=
|
ignore=
|
||||||
cat patreon.md.cache >> README.md
|
cat patreon.md.cache >> README.md
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -80,7 +81,7 @@ test 4 -lt $(cat diff.cache | wc -l) && \
|
|||||||
git add README.md && \
|
git add README.md && \
|
||||||
git commit -m 'Update README.md [AUTOGEN]' && \
|
git commit -m 'Update README.md [AUTOGEN]' && \
|
||||||
git push -f origin patch-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 stash
|
||||||
git checkout master
|
git checkout $__MISSKEY_BRANCH
|
||||||
git branch -D patch-autogen
|
git branch -D patch-autogen
|
||||||
|
|||||||
2
.npmrc
2
.npmrc
@@ -1,2 +1,2 @@
|
|||||||
save-exact=true
|
save-exact = true
|
||||||
package-lock = false
|
package-lock = false
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ Please run `node cli/migration/5.0.0` before launch.
|
|||||||
|
|
||||||
オセロがリバーシに変更されました。
|
オセロがリバーシに変更されました。
|
||||||
|
|
||||||
Othello is now Reversi.
|
Othello is rename to Reversi.
|
||||||
|
|
||||||
### Migration
|
### Migration
|
||||||
|
|
||||||
MongoDBの、`othelloGames`と`othelloMatchings`コレクションをそれぞれ`reversiGames`と`reversiMatchings`にリネームしてください。
|
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
|
3.0.0
|
||||||
-----
|
-----
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
# Contribution guide
|
# Contribution guide
|
||||||
:v: Misskeyへの貢献ありがとうございます。 :v:
|
:v: Thanks for your contributions :v:
|
||||||
|
|
||||||
## Issueの報告
|
## Issues
|
||||||
新機能の提案や不具合の報告は https://github.com/syuilo/misskey/issues で管理しています。
|
Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues .
|
||||||
Issueを作成する前に、既に同じIssueが作成されていないかご確認ください。
|
Before creating a new issue, please search existing issues to avoid duplication.
|
||||||
もし既にIssueが作成されている場合は、既存のIssueにコメントをしたりリアクションをするようお願いします。
|
If you find the existing issue, please add your reaction or comment to the issue.
|
||||||
|
|
||||||
## Issueの解決
|
## Internationalization (i18n)
|
||||||
[pr-welcomeのラベルがついているIssue](https://github.com/syuilo/misskey/labels/pr-welcome)
|
Please see [Translation guide](./docs/translate.en.md).
|
||||||
の解決を目的としたPull Requestを作成してくださると非常にありがたいです。
|
|
||||||
|
|
||||||
## 翻訳の改善
|
## Localization (l10n)
|
||||||
ソースコード中の `%i18n:id%` という形の文字列は、言語ファイルの対応するテキストに置換されます。
|
Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
|
||||||
言語ファイルは /locales ディレクトリに存在します。
|
|
||||||
|
|
||||||
## ドキュメントの編集
|

|
||||||
現在Misskeyはドキュメントが大きく不足しています。
|
|
||||||
ドキュメントは /docs ディレクトリに存在します。
|
|
||||||
|
|
||||||
## テストの追加
|
## Documentation
|
||||||
現在Misskeyはテストが大きく不足しています。
|
* Documents for contributors are located in `/docs`.
|
||||||
テストコードは /test ディレクトリに存在します。
|
* Documents for instance admins are located in `/docs`.
|
||||||
|
* Documents for end users are located in `src/docs`.
|
||||||
|
|
||||||
## 自動テスト及び自動リリース
|
## Test
|
||||||
Travis CIで行っています。
|
* Test codes are located in `/test`.
|
||||||
設定ファイルは /.travis に存在します。
|
|
||||||
|
## 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
|
* Reactions
|
||||||
* User lists
|
* User lists
|
||||||
* Customizable column view (called MisskeyDeck)
|
* Customizable column view (called MisskeyDeck)
|
||||||
* and widgets!
|
* Customizable widgets
|
||||||
* Private messages
|
* Private messages
|
||||||
* ActivityPub support
|
* 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
|
: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!**
|
Please see [Contribution guide](./CONTRIBUTING.md).
|
||||||
|
|
||||||
### i18n
|
|
||||||
|
|
||||||
Please see [Translation guide](./docs/translate.en.md).
|
|
||||||
|
|
||||||
### l10n
|
|
||||||
|
|
||||||
Misskey is using Crowdin for l10n.
|
|
||||||
|
|
||||||
[](https://crowdin.com/project/misskey)
|
|
||||||
|
|
||||||
:heart: Backers & Sponsors
|
:heart: Backers & Sponsors
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
<!-- PATREON_START -->
|
<!-- PATREON_START -->
|
||||||
<table><tr>
|
<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/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/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/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/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://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://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>
|
<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>
|
</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=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=13099460">ne_moni</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||||
@@ -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/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/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/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/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>
|
</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=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/user?u=12931605">Reiju</a></td>
|
||||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</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/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/user?u=12531784">Takashi Shibuya</a></td>
|
||||||
<td><a href="https://www.patreon.com/fujishan">fujishan</a></td>
|
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
**Last updated:** Sun, 26 Aug 2018 08:55:06 UTC
|
**Last updated:** Sun, 02 Sep 2018 05:30:06 UTC
|
||||||
<!-- PATREON_END -->
|
<!-- PATREON_END -->
|
||||||
|
|
||||||
:four_leaf_clover: Copyright
|
:four_leaf_clover: Copyright
|
||||||
|
|||||||
@@ -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)`
|
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||||
3. `npm install`
|
3. `npm install`
|
||||||
4. `npm run build`
|
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)`
|
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||||
3. `npm install`
|
3. `npm install`
|
||||||
4. `npm run build`
|
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...
|
Please see [Contribution guide](../CONTRIBUTING.md) for more information.
|
||||||
* i18n ... please see [Translation guide](../docs/translate.en.md).
|
|
||||||
* l10n ... please visit https://crowdin.com/project/misskey
|
|
||||||
|
|||||||
@@ -5,24 +5,9 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
const loadLang = lang => yaml.safeLoad(
|
const langs = ['de-DE', 'en-US', 'fr-FR', 'ja-JP', 'ja-KS', 'pl-PL', 'es-ES'];
|
||||||
fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8'));
|
|
||||||
|
|
||||||
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 = {
|
module.exports = locales.reduce((a, b) => ({ ...a, ...b }));
|
||||||
'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;
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "8.19.0",
|
"version": "8.22.0",
|
||||||
"clientVersion": "1.0.9217",
|
"clientVersion": "1.0.9273",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -210,13 +210,12 @@
|
|||||||
"vue": "2.5.17",
|
"vue": "2.5.17",
|
||||||
"vue-chartjs": "3.4.0",
|
"vue-chartjs": "3.4.0",
|
||||||
"vue-cropperjs": "2.2.1",
|
"vue-cropperjs": "2.2.1",
|
||||||
"vue-js-modal": "1.3.24",
|
"vue-js-modal": "1.3.25",
|
||||||
"vue-json-tree-view": "2.1.4",
|
"vue-json-tree-view": "2.1.4",
|
||||||
"vue-loader": "15.4.1",
|
"vue-loader": "15.4.1",
|
||||||
"vue-router": "3.0.1",
|
"vue-router": "3.0.1",
|
||||||
"vue-style-loader": "4.1.2",
|
"vue-style-loader": "4.1.2",
|
||||||
"vue-template-compiler": "2.5.17",
|
"vue-template-compiler": "2.5.17",
|
||||||
"vue-thin-modal": "1.1.1",
|
|
||||||
"vuedraggable": "2.16.0",
|
"vuedraggable": "2.16.0",
|
||||||
"vuex": "3.0.1",
|
"vuex": "3.0.1",
|
||||||
"vuex-persistedstate": "2.5.4",
|
"vuex-persistedstate": "2.5.4",
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ html
|
|||||||
&, *
|
&, *
|
||||||
cursor progress !important
|
cursor progress !important
|
||||||
|
|
||||||
|
html
|
||||||
|
// iOSのため
|
||||||
|
overflow auto
|
||||||
|
|
||||||
body
|
body
|
||||||
overflow-wrap break-word
|
overflow-wrap break-word
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<router-view id="app"></router-view>
|
||||||
<router-view id="app"></router-view>
|
|
||||||
<modal-portal/>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export default Vue.extend({
|
|||||||
accepted() {
|
accepted() {
|
||||||
this.state = 'accepted';
|
this.state = 'accepted';
|
||||||
if (this.session.app.callbackUrl) {
|
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
|
// Get salt query
|
||||||
const salt = localStorage.getItem('salt')
|
const salt = localStorage.getItem('salt')
|
||||||
? '?salt=' + localStorage.getItem('salt')
|
? `?salt=${localStorage.getItem('salt')}`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
// Load an app script
|
// Load an app script
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ export default class Connection extends EventEmitter {
|
|||||||
|
|
||||||
const query = params
|
const query = params
|
||||||
? Object.keys(params)
|
? Object.keys(params)
|
||||||
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
|
||||||
.join('&')
|
.join('&')
|
||||||
: null;
|
: 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('open', this.onOpen);
|
||||||
this.socket.addEventListener('close', this.onClose);
|
this.socket.addEventListener('close', this.onClose);
|
||||||
this.socket.addEventListener('message', this.onMessage);
|
this.socket.addEventListener('message', this.onMessage);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.type == 'user') {
|
if (this.type == 'user') {
|
||||||
const cacheKey = 'autocomplete:user:' + this.q;
|
const cacheKey = `autocomplete:user:${this.q}`;
|
||||||
const cache = sessionStorage.getItem(cacheKey);
|
const cache = sessionStorage.getItem(cacheKey);
|
||||||
if (cache) {
|
if (cache) {
|
||||||
const users = JSON.parse(cache);
|
const users = JSON.parse(cache);
|
||||||
@@ -148,7 +148,7 @@ export default Vue.extend({
|
|||||||
this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]');
|
this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]');
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
} else {
|
} else {
|
||||||
const cacheKey = 'autocomplete:hashtag:' + this.q;
|
const cacheKey = `autocomplete:hashtag:${this.q}`;
|
||||||
const cache = sessionStorage.getItem(cacheKey);
|
const cache = sessionStorage.getItem(cacheKey);
|
||||||
if (cache) {
|
if (cache) {
|
||||||
const hashtags = JSON.parse(cache);
|
const hashtags = JSON.parse(cache);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check internet connection
|
// Check internet connection
|
||||||
fetch('https://google.com?rand=' + Math.random(), {
|
fetch(`https://google.com?rand=${Math.random()}`, {
|
||||||
mode: 'no-cors'
|
mode: 'no-cors'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.internet = true;
|
this.internet = true;
|
||||||
|
|||||||
@@ -159,11 +159,9 @@ export default Vue.extend({
|
|||||||
canPutEverywhere: this.game.settings.canPutEverywhere,
|
canPutEverywhere: this.game.settings.canPutEverywhere,
|
||||||
loopedBoard: this.game.settings.loopedBoard
|
loopedBoard: this.game.settings.loopedBoard
|
||||||
});
|
});
|
||||||
this.logs.forEach((log, i) => {
|
for (const log of this.logs.slice(0, v)) {
|
||||||
if (i < v) {
|
this.o.put(log.color, log.pos);
|
||||||
this.o.put(log.color, log.pos);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-menu">
|
<div class="onchrpzrvnoruiaenfcqvccjfuupzzwv">
|
||||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||||
<div class="popover" :class="{ hukidasi }" ref="popover">
|
<div class="popover" :class="{ hukidasi }" ref="popover">
|
||||||
<template v-for="item in items">
|
<template v-for="item in items">
|
||||||
@@ -119,9 +119,10 @@ export default Vue.extend({
|
|||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@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
|
position initial
|
||||||
|
|
||||||
> .backdrop
|
> .backdrop
|
||||||
@@ -131,14 +132,14 @@ $border-color = rgba(27, 31, 35, 0.15)
|
|||||||
z-index 10000
|
z-index 10000
|
||||||
width 100%
|
width 100%
|
||||||
height 100%
|
height 100%
|
||||||
background rgba(#000, 0.1)
|
background rgba(#000, isDark ? 0.5 : 0.1)
|
||||||
opacity 0
|
opacity 0
|
||||||
|
|
||||||
> .popover
|
> .popover
|
||||||
position absolute
|
position absolute
|
||||||
z-index 10001
|
z-index 10001
|
||||||
padding 8px 0
|
padding 8px 0
|
||||||
background #fff
|
background $bg-color
|
||||||
border 1px solid $border-color
|
border 1px solid $border-color
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
|
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-top solid $balloon-size transparent
|
||||||
border-left solid $balloon-size transparent
|
border-left solid $balloon-size transparent
|
||||||
border-right solid $balloon-size transparent
|
border-right solid $balloon-size transparent
|
||||||
border-bottom solid $balloon-size #fff
|
border-bottom solid $balloon-size $bg-color
|
||||||
|
|
||||||
> button
|
> button
|
||||||
display block
|
display block
|
||||||
padding 8px 16px
|
padding 8px 16px
|
||||||
width 100%
|
width 100%
|
||||||
|
color isDark ? #d6dce2 : #111
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
color $theme-color-foreground
|
color $theme-color-foreground
|
||||||
@@ -191,6 +193,12 @@ $border-color = rgba(27, 31, 35, 0.15)
|
|||||||
> div
|
> div
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
height 1px
|
height 1px
|
||||||
background #eee
|
background isDark ? #1c2023 : #eee
|
||||||
|
|
||||||
|
.onchrpzrvnoruiaenfcqvccjfuupzzwv[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.onchrpzrvnoruiaenfcqvccjfuupzzwv:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -205,17 +205,8 @@ export default Vue.component('misskey-flavored-markdown', {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const _els = [];
|
// el.tag === 'br' のとき i !== 0 が保証されるため、短絡評価により els[i - 1] は配列外参照しない
|
||||||
els.forEach((el, i) => {
|
const _els = els.filter((el, i) => !(el.tag === 'br' && ['div', 'pre'].includes(els[i - 1].tag)));
|
||||||
if (el.tag == 'br') {
|
|
||||||
if (!['div', 'pre'].includes(els[i - 1].tag)) {
|
|
||||||
_els.push(el);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_els.push(el);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return createElement('span', _els);
|
return createElement('span', _els);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export default Vue.extend({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
|
fetch(`/url?url=${encodeURIComponent(this.url)}`).then(res => {
|
||||||
res.json().then(info => {
|
res.json().then(info => {
|
||||||
if (info.url == null) return;
|
if (info.url == null) return;
|
||||||
this.title = info.title;
|
this.title = info.title;
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ class Autocomplete {
|
|||||||
const acct = renderAcct(value);
|
const acct = renderAcct(value);
|
||||||
|
|
||||||
// 挿入
|
// 挿入
|
||||||
this.text = trimmedBefore + '@' + acct + ' ' + after;
|
this.text = `${trimmedBefore}@${acct} ${after}`;
|
||||||
|
|
||||||
// キャレットを戻す
|
// キャレットを戻す
|
||||||
this.vm.$nextTick(() => {
|
this.vm.$nextTick(() => {
|
||||||
@@ -207,7 +207,7 @@ class Autocomplete {
|
|||||||
const after = source.substr(caret);
|
const after = source.substr(caret);
|
||||||
|
|
||||||
// 挿入
|
// 挿入
|
||||||
this.text = trimmedBefore + '#' + value + ' ' + after;
|
this.text = `${trimmedBefore}#${value} ${after}`;
|
||||||
|
|
||||||
// キャレットを戻す
|
// キャレットを戻す
|
||||||
this.vm.$nextTick(() => {
|
this.vm.$nextTick(() => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
Vue.filter('notePage', note => {
|
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?) => {
|
Vue.filter('userPage', (user, path?) => {
|
||||||
return '/@' + Vue.filter('acct')(user) + (path ? '/' + path : '');
|
return `/@${Vue.filter('acct')(user)}${(path ? `/${path}` : '')}`;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="syxhndwprovvuqhmyvveewmbqayniwkv" v-if="!fetching" :data-darkmode="$store.state.device.darkmode">
|
<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>
|
<main>
|
||||||
<div class="banner" :style="bannerStyle"></div>
|
<div class="banner" :style="bannerStyle"></div>
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
alert('%i18n:@unhandled-error% ' + err);
|
alert(`%i18n:@unhandled-error% ${err}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
alert('%i18n:@unhandled-error% ' + err);
|
alert(`%i18n:@unhandled-error% ${err}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -404,7 +404,7 @@ export default Vue.extend({
|
|||||||
folder: folder
|
folder: folder
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
window.open(url + '/i/drive/folder/' + folder.id,
|
window.open(`${url}/i/drive/folder/${folder.id}`,
|
||||||
'drive_window',
|
'drive_window',
|
||||||
'height=500, width=800');
|
'height=500, width=800');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default Vue.extend({
|
|||||||
const mouseY = e.clientY - rect.top;
|
const mouseY = e.clientY - rect.top;
|
||||||
const xp = mouseX / this.$el.offsetWidth * 100;
|
const xp = mouseX / this.$el.offsetWidth * 100;
|
||||||
const yp = mouseY / this.$el.offsetHeight * 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}")`;
|
this.$el.style.backgroundImage = `url("${this.image.url}")`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -110,9 +110,9 @@ export default Vue.extend({
|
|||||||
computed: {
|
computed: {
|
||||||
draftId(): string {
|
draftId(): string {
|
||||||
return this.renote
|
return this.renote
|
||||||
? 'renote:' + this.renote.id
|
? `renote:${this.renote.id}`
|
||||||
: this.reply
|
: this.reply
|
||||||
? 'reply:' + this.reply.id
|
? `reply:${this.reply.id}`
|
||||||
: 'note';
|
: 'note';
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ export default Vue.extend({
|
|||||||
this.geo = pos.coords;
|
this.geo = pos.coords;
|
||||||
this.$emit('geo-attached', this.geo);
|
this.$emit('geo-attached', this.geo);
|
||||||
}, err => {
|
}, err => {
|
||||||
alert('%i18n:@error%: ' + err.message);
|
alert(`%i18n:@error%: ${err.message}`);
|
||||||
}, {
|
}, {
|
||||||
enableHighAccuracy: true
|
enableHighAccuracy: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default Vue.extend({
|
|||||||
const title = folder.name + ' | %i18n:@title%';
|
const title = folder.name + ' | %i18n:@title%';
|
||||||
|
|
||||||
// Rewrite URL
|
// Rewrite URL
|
||||||
history.pushState(null, title, '/i/drive/folder/' + folder.id);
|
history.pushState(null, title, `/i/drive/folder/${folder.id}`);
|
||||||
|
|
||||||
document.title = title;
|
document.title = title;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ export default Vue.extend({
|
|||||||
methods: {
|
methods: {
|
||||||
nav(game, actualNav) {
|
nav(game, actualNav) {
|
||||||
if (actualNav) {
|
if (actualNav) {
|
||||||
this.$router.push('/reversi/' + game.id);
|
this.$router.push(`/reversi/${game.id}`);
|
||||||
} else {
|
} else {
|
||||||
// TODO: https://github.com/vuejs/vue-router/issues/703
|
// 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.user = user;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
document.title = 'メッセージ: ' + getUserName(this.user);
|
document.title = `メッセージ: ${getUserName(this.user)}`;
|
||||||
|
|
||||||
Progress.done();
|
Progress.done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<div class="tl">
|
<div class="tl">
|
||||||
<mk-welcome-timeline :max="20"/>
|
<mk-welcome-timeline :max="20"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<modal name="signup" width="500px" height="auto" scrollable>
|
<modal name="signup" width="500px" height="auto" scrollable>
|
||||||
<header :class="$style.signupFormHeader">%i18n:@signup%</header>
|
<header :class="$style.signupFormHeader">%i18n:@signup%</header>
|
||||||
<mk-signup :class="$style.signupForm"/>
|
<mk-signup :class="$style.signupForm"/>
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueRouter from 'vue-router';
|
import VueRouter from 'vue-router';
|
||||||
import VModal from 'vue-js-modal';
|
|
||||||
import * as TreeView from 'vue-json-tree-view';
|
import * as TreeView from 'vue-json-tree-view';
|
||||||
import VAnimateCss from 'v-animate-css';
|
import VAnimateCss from 'v-animate-css';
|
||||||
import Element from 'element-ui';
|
import Element from 'element-ui';
|
||||||
import ElementLocaleEn from 'element-ui/lib/locale/lang/en';
|
import ElementLocaleEn from 'element-ui/lib/locale/lang/en';
|
||||||
import ElementLocaleJa from 'element-ui/lib/locale/lang/ja';
|
import ElementLocaleJa from 'element-ui/lib/locale/lang/ja';
|
||||||
import VueThinModal from 'vue-thin-modal';
|
import VModal from 'vue-js-modal';
|
||||||
import 'vue-thin-modal/dist/vue-thin-modal.css';
|
|
||||||
|
|
||||||
import App from './app.vue';
|
import App from './app.vue';
|
||||||
import checkForUpdate from './common/scripts/check-for-update';
|
import checkForUpdate from './common/scripts/check-for-update';
|
||||||
@@ -28,13 +26,10 @@ switch (lang) {
|
|||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(VModal);
|
|
||||||
Vue.use(TreeView);
|
Vue.use(TreeView);
|
||||||
Vue.use(VAnimateCss);
|
Vue.use(VAnimateCss);
|
||||||
Vue.use(Element, { locale: elementLocale });
|
Vue.use(Element, { locale: elementLocale });
|
||||||
Vue.use(VueThinModal, {
|
Vue.use(VModal);
|
||||||
autoMountPortal: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register global directives
|
// Register global directives
|
||||||
require('./common/views/directives');
|
require('./common/views/directives');
|
||||||
|
|||||||
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 chooseDriveFile from './api/choose-drive-file';
|
||||||
import dialog from './api/dialog';
|
import dialog from './api/dialog';
|
||||||
import input from './api/input';
|
import input from './api/input';
|
||||||
|
import post from './api/post';
|
||||||
import notify from './api/notify';
|
import notify from './api/notify';
|
||||||
|
|
||||||
import MkIndex from './views/pages/index.vue';
|
import MkIndex from './views/pages/index.vue';
|
||||||
@@ -90,7 +91,7 @@ init((launch) => {
|
|||||||
chooseDriveFile,
|
chooseDriveFile,
|
||||||
dialog: dialog(os),
|
dialog: dialog(os),
|
||||||
input,
|
input,
|
||||||
post: () => alert('deprecated'),
|
post: post(os),
|
||||||
notify
|
notify
|
||||||
}));
|
}));
|
||||||
}, true);
|
}, true);
|
||||||
|
|||||||
@@ -17,13 +17,3 @@ body
|
|||||||
display flex
|
display flex
|
||||||
flex-direction column
|
flex-direction column
|
||||||
min-height 100%
|
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>
|
<template>
|
||||||
<div class="mk-drive-file-chooser">
|
<div class="cdxzvcfawjxdyxsekbxbfgtplebnoneb">
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<header>
|
<header>
|
||||||
<h1>%i18n:@select-file%<span class="count" v-if="files.length > 0">({{ files.length }})</span></h1>
|
<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 class="close" @click="cancel">%fa:times%</button>
|
||||||
<button v-if="multiple" class="ok" @click="ok">%fa:check%</button>
|
<button v-if="multiple" class="ok" @click="ok">%fa:check%</button>
|
||||||
</header>
|
</header>
|
||||||
<mk-drive ref="browser"
|
<mk-drive class="drive" ref="browser"
|
||||||
:select-file="true"
|
:select-file="true"
|
||||||
:multiple="multiple"
|
:multiple="multiple"
|
||||||
@change-selection="onChangeSelection"
|
@change-selection="onChangeSelection"
|
||||||
@@ -46,7 +46,7 @@ export default Vue.extend({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-drive-file-chooser
|
root(isDark)
|
||||||
position fixed
|
position fixed
|
||||||
z-index 20000
|
z-index 20000
|
||||||
top 0
|
top 0
|
||||||
@@ -59,10 +59,11 @@ export default Vue.extend({
|
|||||||
> .body
|
> .body
|
||||||
width 100%
|
width 100%
|
||||||
height 100%
|
height 100%
|
||||||
background #fff
|
background isDark ? #282c37 : #fff
|
||||||
|
|
||||||
> header
|
> header
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px isDark ? #1b1f29 : #eee
|
||||||
|
color isDark ? #fff : #111
|
||||||
|
|
||||||
> h1
|
> h1
|
||||||
margin 0
|
margin 0
|
||||||
@@ -90,9 +91,15 @@ export default Vue.extend({
|
|||||||
line-height 42px
|
line-height 42px
|
||||||
width 42px
|
width 42px
|
||||||
|
|
||||||
> .mk-drive
|
> .drive
|
||||||
height calc(100% - 42px)
|
height calc(100% - 42px)
|
||||||
overflow scroll
|
overflow scroll
|
||||||
-webkit-overflow-scrolling touch
|
-webkit-overflow-scrolling touch
|
||||||
|
|
||||||
|
.cdxzvcfawjxdyxsekbxbfgtplebnoneb[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.cdxzvcfawjxdyxsekbxbfgtplebnoneb:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -75,13 +75,6 @@
|
|||||||
<div class="replies" v-if="!compact">
|
<div class="replies" v-if="!compact">
|
||||||
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
|
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -192,19 +185,15 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
reply() {
|
reply() {
|
||||||
this.$modal.push('replyForm');
|
(this as any).apis.post({
|
||||||
},
|
reply: this.p
|
||||||
|
});
|
||||||
replyFormClosed() {
|
|
||||||
this.$modal.pop();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renote() {
|
renote() {
|
||||||
this.$modal.push('renoteForm');
|
(this as any).apis.post({
|
||||||
},
|
renote: this.p
|
||||||
|
});
|
||||||
renoteFormClosed() {
|
|
||||||
this.$modal.pop();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
react() {
|
react() {
|
||||||
|
|||||||
@@ -60,13 +60,6 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -202,19 +195,15 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
reply() {
|
reply() {
|
||||||
this.$modal.push('replyForm');
|
(this as any).apis.post({
|
||||||
},
|
reply: this.p
|
||||||
|
});
|
||||||
replyFormClosed() {
|
|
||||||
this.$modal.pop();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renote() {
|
renote() {
|
||||||
this.$modal.push('renoteForm');
|
(this as any).apis.post({
|
||||||
},
|
renote: this.p
|
||||||
|
});
|
||||||
renoteFormClosed() {
|
|
||||||
this.$modal.pop();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
react() {
|
react() {
|
||||||
@@ -482,10 +471,6 @@ root(isDark)
|
|||||||
&.reacted
|
&.reacted
|
||||||
color $theme-color
|
color $theme-color
|
||||||
|
|
||||||
&.menu
|
|
||||||
@media (max-width 350px)
|
|
||||||
display none
|
|
||||||
|
|
||||||
.note[data-darkmode]
|
.note[data-darkmode]
|
||||||
root(true)
|
root(true)
|
||||||
|
|
||||||
|
|||||||
131
src/client/app/mobile/views/components/post-form-dialog.vue
Normal file
131
src/client/app/mobile/views/components/post-form-dialog.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ulveipglmagnxfgvitaxyszerjwiqmwl">
|
||||||
|
<div class="bg" ref="bg" @click="onBgClick"></div>
|
||||||
|
<div class="main" ref="main" @click.self="onBgClick">
|
||||||
|
<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()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onBgClick() {
|
||||||
|
this.$emit('cancel');
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
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>
|
||||||
@@ -105,9 +105,9 @@ export default Vue.extend({
|
|||||||
computed: {
|
computed: {
|
||||||
draftId(): string {
|
draftId(): string {
|
||||||
return this.renote
|
return this.renote
|
||||||
? 'renote:' + this.renote.id
|
? `renote:${this.renote.id}`
|
||||||
: this.reply
|
: this.reply
|
||||||
? 'reply:' + this.reply.id
|
? `reply:${this.reply.id}`
|
||||||
: 'note';
|
: 'note';
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -170,6 +170,8 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.focus();
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.focus();
|
this.focus();
|
||||||
});
|
});
|
||||||
@@ -227,7 +229,7 @@ export default Vue.extend({
|
|||||||
navigator.geolocation.getCurrentPosition(pos => {
|
navigator.geolocation.getCurrentPosition(pos => {
|
||||||
this.geo = pos.coords;
|
this.geo = pos.coords;
|
||||||
}, err => {
|
}, err => {
|
||||||
alert('%i18n:@error%: ' + err.message);
|
alert(`%i18n:@error%: ${err.message}`);
|
||||||
}, {
|
}, {
|
||||||
enableHighAccuracy: true
|
enableHighAccuracy: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export default Vue.extend({
|
|||||||
search() {
|
search() {
|
||||||
const query = window.prompt('%i18n:@search%');
|
const query = window.prompt('%i18n:@search%');
|
||||||
if (query == null || query == '') return;
|
if (query == null || query == '') return;
|
||||||
this.$router.push('/search?q=' + encodeURIComponent(query));
|
this.$router.push(`/search?q=${encodeURIComponent(query)}`);
|
||||||
},
|
},
|
||||||
onReversiInvited() {
|
onReversiInvited() {
|
||||||
this.hasGameInvitation = true;
|
this.hasGameInvitation = true;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
// Rewrite URL
|
// Rewrite URL
|
||||||
history.pushState(null, title, '/i/drive/folder/' + folder.id);
|
history.pushState(null, title, `/i/drive/folder/${folder.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = title;
|
document.title = title;
|
||||||
@@ -93,7 +93,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
// Rewrite URL
|
// Rewrite URL
|
||||||
history.pushState(null, title, '/i/drive/file/' + file.id);
|
history.pushState(null, title, `/i/drive/file/${file.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = title;
|
document.title = title;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default Vue.extend({
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + (this as any).os.instanceName;
|
document.title = `${'%i18n:@followers-of%'.replace('{}', this.name)} | ${(this as any).os.instanceName}`;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onLoaded() {
|
onLoaded() {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default Vue.extend({
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + (this as any).os.instanceName;
|
document.title = `${'%i18n:@followers-of%'.replace('{}', this.name)} | ${(this as any).os.instanceName}`;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onLoaded() {
|
onLoaded() {
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ export default Vue.extend({
|
|||||||
methods: {
|
methods: {
|
||||||
nav(game, actualNav) {
|
nav(game, actualNav) {
|
||||||
if (actualNav) {
|
if (actualNav) {
|
||||||
this.$router.push('/reversi/' + game.id);
|
this.$router.push(`/reversi/${game.id}`);
|
||||||
} else {
|
} else {
|
||||||
// TODO: https://github.com/vuejs/vue-router/issues/703
|
// TODO: https://github.com/vuejs/vue-router/issues/703
|
||||||
this.$router.push('/reversi/' + game.id);
|
this.$router.push(`/reversi/${game.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,6 @@
|
|||||||
<mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
|
<mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<modal name="postForm">
|
|
||||||
<mk-post-form @posted="postFormClosed" @cancel="postFormClosed"/>
|
|
||||||
</modal>
|
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -111,11 +107,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
fn() {
|
fn() {
|
||||||
this.$modal.push('postForm');
|
(this as any).apis.post();
|
||||||
},
|
|
||||||
|
|
||||||
postFormClosed() {
|
|
||||||
this.$modal.pop();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
saveSrc() {
|
saveSrc() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<mk-ui>
|
<mk-ui>
|
||||||
<span slot="header">%fa:cog%%i18n:@settings%</span>
|
<span slot="header">%fa:cog%%i18n:@settings%</span>
|
||||||
<main :data-darkmode="$store.state.device.darkmode">
|
<main :data-darkmode="$store.state.device.darkmode">
|
||||||
<div class="signin-as" v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></div>
|
<div class="signin-as" v-html="'%i18n:@signed-in-as%'.replace('{}', `<b>${name}</b>`)"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-profile/>
|
<x-profile/>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default Vue.extend({
|
|||||||
title
|
title
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$router.push('/i/lists/' + list.id);
|
this.$router.push(`/i/lists/${list.id}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default Vue.extend({
|
|||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
Progress.done();
|
Progress.done();
|
||||||
document.title = Vue.filter('userName')(this.user) + ' | ' + (this as any).os.instanceName;
|
document.title = `${Vue.filter('userName')(this.user)} | ${(this as any).os.instanceName}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ev.on('requestNotesStatsLog', id => {
|
ev.on('requestNotesStatsLog', id => {
|
||||||
ev.emit('notesStatsLog:' + id, log.toArray());
|
ev.emit(`notesStatsLog:${id}`, log.toArray());
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('exit', code => {
|
process.on('exit', code => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function() {
|
|||||||
const log = new Deque<any>();
|
const log = new Deque<any>();
|
||||||
|
|
||||||
ev.on('requestServerStatsLog', x => {
|
ev.on('requestServerStatsLog', x => {
|
||||||
ev.emit('serverStatsLog:' + x.id, log.toArray().slice(0, x.length || 50));
|
ev.emit(`serverStatsLog:${x.id}`, log.toArray().slice(0, x.length || 50));
|
||||||
});
|
});
|
||||||
|
|
||||||
async function tick() {
|
async function tick() {
|
||||||
|
|||||||
@@ -205,13 +205,7 @@ export default class Reversi {
|
|||||||
* 打つことができる場所を取得します
|
* 打つことができる場所を取得します
|
||||||
*/
|
*/
|
||||||
public canPutSomewhere(color: Color): number[] {
|
public canPutSomewhere(color: Color): number[] {
|
||||||
const result: number[] = [];
|
return Array.from(this.board.keys()).filter(i => this.canPut(color, i));
|
||||||
|
|
||||||
this.board.forEach((x, i) => {
|
|
||||||
if (this.canPut(color, i)) result.push(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import Logger from './misc/logger';
|
|||||||
import ProgressBar from './misc/cli/progressbar';
|
import ProgressBar from './misc/cli/progressbar';
|
||||||
import EnvironmentInfo from './misc/environmentInfo';
|
import EnvironmentInfo from './misc/environmentInfo';
|
||||||
import MachineInfo from './misc/machineInfo';
|
import MachineInfo from './misc/machineInfo';
|
||||||
import DependencyInfo from './misc/dependencyInfo';
|
|
||||||
import serverStats from './daemons/server-stats';
|
import serverStats from './daemons/server-stats';
|
||||||
import notesStats from './daemons/notes-stats';
|
import notesStats from './daemons/notes-stats';
|
||||||
import loadConfig from './config/load';
|
import loadConfig from './config/load';
|
||||||
@@ -116,7 +115,6 @@ async function init(): Promise<Config> {
|
|||||||
new Logger('Deps').info(`Node.js ${process.version}`);
|
new Logger('Deps').info(`Node.js ${process.version}`);
|
||||||
MachineInfo.show();
|
MachineInfo.show();
|
||||||
EnvironmentInfo.show();
|
EnvironmentInfo.show();
|
||||||
new DependencyInfo().showAll();
|
|
||||||
|
|
||||||
const configLogger = new Logger('Config');
|
const configLogger = new Logger('Config');
|
||||||
let config;
|
let config;
|
||||||
|
|||||||
@@ -33,26 +33,27 @@ export default function(html: string): string {
|
|||||||
|
|
||||||
case 'a':
|
case 'a':
|
||||||
const txt = getText(node);
|
const txt = getText(node);
|
||||||
|
const rel = node.attrs.find((x: any) => x.name == 'rel');
|
||||||
|
const href = node.attrs.find((x: any) => x.name == 'href');
|
||||||
|
|
||||||
|
// ハッシュタグ / hrefがない / txtがURL
|
||||||
|
if ((rel && rel.value.match('tag') !== null) || !href || href.value == txt) {
|
||||||
|
text += txt;
|
||||||
// メンション
|
// メンション
|
||||||
if (txt.startsWith('@')) {
|
} else if (txt.startsWith('@')) {
|
||||||
const part = txt.split('@');
|
const part = txt.split('@');
|
||||||
|
|
||||||
if (part.length == 2) {
|
if (part.length == 2) {
|
||||||
//#region ホスト名部分が省略されているので復元する
|
//#region ホスト名部分が省略されているので復元する
|
||||||
const href = new URL(node.attrs.find((x: any) => x.name == 'href').value);
|
const acct = `${txt}@${(new URL(href.value)).hostname}`;
|
||||||
const acct = txt + '@' + href.hostname;
|
|
||||||
text += acct;
|
text += acct;
|
||||||
break;
|
|
||||||
//#endregion
|
//#endregion
|
||||||
} else if (part.length == 3) {
|
} else if (part.length == 3) {
|
||||||
text += txt;
|
text += txt;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
// その他
|
||||||
|
} else {
|
||||||
if (node.childNodes) {
|
text += `[${txt}](${href.value})`;
|
||||||
node.childNodes.forEach((n: any) => analyze(n));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ const handlers: { [key: string]: (window: any, token: any, mentionedRemoteUsers:
|
|||||||
|
|
||||||
hashtag({ document }, { hashtag }) {
|
hashtag({ document }, { hashtag }) {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = config.url + '/tags/' + hashtag;
|
a.href = `${config.url}/tags/${hashtag}`;
|
||||||
a.textContent = '#' + hashtag;
|
a.textContent = `#${hashtag}`;
|
||||||
a.setAttribute('rel', 'tag');
|
a.setAttribute('rel', 'tag');
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import Logger from './logger';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
|
|
||||||
export default class {
|
|
||||||
private logger: Logger;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.logger = new Logger('Deps');
|
|
||||||
}
|
|
||||||
|
|
||||||
public showAll(): void {
|
|
||||||
this.show('MongoDB', 'mongo --version', x => x.match(/^MongoDB shell version:? v(.*)\r?\n/));
|
|
||||||
this.show('Redis', 'redis-server --version', x => x.match(/v=([0-9\.]*)/));
|
|
||||||
}
|
|
||||||
|
|
||||||
public show(serviceName: string, command: string, transform: (x: string) => RegExpMatchArray): void {
|
|
||||||
try {
|
|
||||||
// ステータス0以外のときにexecSyncはstderrをコンソール上に出力してしまうので
|
|
||||||
// プロセスからのstderrをすべて無視するように stdio オプションをセット
|
|
||||||
const x = execSync(command, { stdio: ['pipe', 'pipe', 'ignore'] });
|
|
||||||
const ver = transform(x.toString());
|
|
||||||
if (ver != null) {
|
|
||||||
this.logger.succ(`${serviceName} ${ver[1]} found`);
|
|
||||||
} else {
|
|
||||||
this.logger.warn(`${serviceName} not found`);
|
|
||||||
this.logger.warn(`Regexp used for version check of ${serviceName} is probably messed up`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.warn(`${serviceName} not found`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ export const replacement = (match: string, key: string) => {
|
|||||||
arg == 'B' ? 'fab' :
|
arg == 'B' ? 'fab' :
|
||||||
'';
|
'';
|
||||||
} else if (arg.startsWith('.')) {
|
} else if (arg.startsWith('.')) {
|
||||||
classes.push('fa-' + arg.substr(1));
|
classes.push(`fa-${arg.substr(1)}`);
|
||||||
} else if (arg.startsWith('-')) {
|
} else if (arg.startsWith('-')) {
|
||||||
transform = arg.substr(1).split('|').join(' ');
|
transform = arg.substr(1).split('|').join(' ');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -193,5 +193,10 @@ export const pack = (
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete _target.withoutChunks;
|
||||||
|
delete _target.storage;
|
||||||
|
delete _target.storageProps;
|
||||||
|
delete _target.isRemote;
|
||||||
|
|
||||||
resolve(_target);
|
resolve(_target);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ import * as mongo from 'mongodb';
|
|||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
|
|
||||||
const Stats = db.get<IStats>('stats');
|
const Stats = db.get<IStats>('stats');
|
||||||
Stats.dropIndex({ date: -1 }); // 後方互換性のため
|
|
||||||
|
// 後方互換性のため
|
||||||
|
Stats.dropIndex({ date: -1 } as any).catch((e: mongo.MongoError) => {
|
||||||
|
if (e.code !== 27) throw e;
|
||||||
|
});
|
||||||
|
|
||||||
Stats.createIndex({ span: -1, date: -1 }, { unique: true });
|
Stats.createIndex({ span: -1, date: -1 }, { unique: true });
|
||||||
export default Stats;
|
export default Stats;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const httpSignature = require('http-signature');
|
|||||||
import parseAcct from '../../../misc/acct/parse';
|
import parseAcct from '../../../misc/acct/parse';
|
||||||
import User, { IRemoteUser } from '../../../models/user';
|
import User, { IRemoteUser } from '../../../models/user';
|
||||||
import perform from '../../../remote/activitypub/perform';
|
import perform from '../../../remote/activitypub/perform';
|
||||||
import { resolvePerson } from '../../../remote/activitypub/models/person';
|
import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person';
|
||||||
import { toUnicode } from 'punycode';
|
import { toUnicode } from 'punycode';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
|
|
||||||
@@ -44,11 +44,6 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
|
user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
|
||||||
|
|
||||||
// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
|
|
||||||
if (user === null) {
|
|
||||||
user = await resolvePerson(activity.actor) as IRemoteUser;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// アクティビティ内のホストの検証
|
// アクティビティ内のホストの検証
|
||||||
const host = toUnicode(new URL(signature.keyId).hostname.toLowerCase());
|
const host = toUnicode(new URL(signature.keyId).hostname.toLowerCase());
|
||||||
@@ -64,11 +59,26 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
|||||||
host: { $ne: null },
|
host: { $ne: null },
|
||||||
'publicKey.id': signature.keyId
|
'publicKey.id': signature.keyId
|
||||||
}) as IRemoteUser;
|
}) as IRemoteUser;
|
||||||
|
}
|
||||||
|
|
||||||
// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
|
// Update activityの場合は、ここで署名検証/更新処理まで実施して終了
|
||||||
if (user === null) {
|
if (activity.type === 'Update') {
|
||||||
user = await resolvePerson(activity.actor) as IRemoteUser;
|
if (activity.object && activity.object.type === 'Person') {
|
||||||
|
if (user == null) {
|
||||||
|
console.warn('Update activity received, but user not registed.');
|
||||||
|
} else if (!httpSignature.verifySignature(signature, user.publicKey.publicKeyPem)) {
|
||||||
|
console.warn('Update activity received, but signature verification failed.');
|
||||||
|
} else {
|
||||||
|
updatePerson(activity.actor, null, activity.object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
|
||||||
|
if (user === null) {
|
||||||
|
user = await resolvePerson(activity.actor) as IRemoteUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
|||||||
avatarId: null,
|
avatarId: null,
|
||||||
bannerId: null,
|
bannerId: null,
|
||||||
createdAt: Date.parse(person.published) || null,
|
createdAt: Date.parse(person.published) || null,
|
||||||
|
updatedAt: new Date(),
|
||||||
description: htmlToMFM(person.summary),
|
description: htmlToMFM(person.summary),
|
||||||
followersCount,
|
followersCount,
|
||||||
followingCount,
|
followingCount,
|
||||||
@@ -215,10 +216,12 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Personの情報を更新します。
|
* Personの情報を更新します。
|
||||||
*
|
|
||||||
* Misskeyに対象のPersonが登録されていなければ無視します。
|
* Misskeyに対象のPersonが登録されていなければ無視します。
|
||||||
|
* @param uri URI of Person
|
||||||
|
* @param resolver Resolver
|
||||||
|
* @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します)
|
||||||
*/
|
*/
|
||||||
export async function updatePerson(uri: string, resolver?: Resolver): Promise<void> {
|
export async function updatePerson(uri: string, resolver?: Resolver, hint?: object): Promise<void> {
|
||||||
if (typeof uri !== 'string') throw 'uri is not string';
|
if (typeof uri !== 'string') throw 'uri is not string';
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならスキップ
|
// URIがこのサーバーを指しているならスキップ
|
||||||
@@ -236,7 +239,7 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise<vo
|
|||||||
|
|
||||||
if (resolver == null) resolver = new Resolver();
|
if (resolver == null) resolver = new Resolver();
|
||||||
|
|
||||||
const object = await resolver.resolve(uri) as any;
|
const object = hint || await resolver.resolve(uri) as any;
|
||||||
|
|
||||||
const err = validatePerson(object, uri);
|
const err = validatePerson(object, uri);
|
||||||
|
|
||||||
@@ -290,7 +293,14 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise<vo
|
|||||||
name: person.name,
|
name: person.name,
|
||||||
url: person.url,
|
url: person.url,
|
||||||
endpoints: person.endpoints,
|
endpoints: person.endpoints,
|
||||||
isCat: (person as any).isCat === true ? true : false
|
isBot: object.type == 'Service',
|
||||||
|
isCat: (person as any).isCat === true ? true : false,
|
||||||
|
isLocked: person.manuallyApprovesFollowers,
|
||||||
|
createdAt: Date.parse(person.published) || null,
|
||||||
|
publicKey: {
|
||||||
|
id: person.publicKey.id,
|
||||||
|
publicKeyPem: person.publicKey.publicKeyPem
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import config from '../../../config';
|
|||||||
export default (tag: string) => ({
|
export default (tag: string) => ({
|
||||||
type: 'Hashtag',
|
type: 'Hashtag',
|
||||||
href: `${config.url}/tags/${encodeURIComponent(tag)}`,
|
href: `${config.url}/tags/${encodeURIComponent(tag)}`,
|
||||||
name: '#' + tag
|
name: `#${tag}`
|
||||||
});
|
});
|
||||||
|
|||||||
4
src/remote/activitypub/renderer/tombstone.ts
Normal file
4
src/remote/activitypub/renderer/tombstone.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default (id: string) => ({
|
||||||
|
id,
|
||||||
|
type: 'Tombstone'
|
||||||
|
});
|
||||||
14
src/remote/activitypub/renderer/update.ts
Normal file
14
src/remote/activitypub/renderer/update.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import config from '../../../config';
|
||||||
|
import { ILocalUser } from '../../../models/user';
|
||||||
|
|
||||||
|
export default (object: any, user: ILocalUser) => {
|
||||||
|
const activity = {
|
||||||
|
id: `${config.url}/users/${user._id}#updates/${new Date().getTime()}`,
|
||||||
|
actor: `${config.url}/users/${user._id}`,
|
||||||
|
type: 'Update',
|
||||||
|
to: [ 'https://www.w3.org/ns/activitystreams#Public' ],
|
||||||
|
object
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
return activity;
|
||||||
|
};
|
||||||
@@ -22,7 +22,7 @@ const router = new Router();
|
|||||||
function inbox(ctx: Router.IRouterContext) {
|
function inbox(ctx: Router.IRouterContext) {
|
||||||
let signature;
|
let signature;
|
||||||
|
|
||||||
ctx.req.headers.authorization = 'Signature ' + ctx.req.headers.signature;
|
ctx.req.headers.authorization = `Signature ${ctx.req.headers.signature}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signature = httpSignature.parseRequest(ctx.req, { 'headers': [] });
|
signature = httpSignature.parseRequest(ctx.req, { 'headers': [] });
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ const files = glob.sync('**/*.js', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const endpoints: IEndpoint[] = files.map(f => {
|
const endpoints: IEndpoint[] = files.map(f => {
|
||||||
const ep = require('./endpoints/' + f);
|
const ep = require(`./endpoints/${f}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: f.replace('.js', ''),
|
name: f.replace('.js', ''),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default (params: any) => new Promise(async (res, rej) => {
|
|||||||
|
|
||||||
const set = {} as any;
|
const set = {} as any;
|
||||||
|
|
||||||
if (ps.disableRegistration === true || ps.disableRegistration === false) {
|
if (typeof ps.disableRegistration === 'boolean') {
|
||||||
set.disableRegistration = ps.disableRegistration;
|
set.disableRegistration = ps.disableRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import DriveFile from '../../../../models/drive-file';
|
|||||||
import acceptAllFollowRequests from '../../../../services/following/requests/accept-all';
|
import acceptAllFollowRequests from '../../../../services/following/requests/accept-all';
|
||||||
import { IApp } from '../../../../models/app';
|
import { IApp } from '../../../../models/app';
|
||||||
import config from '../../../../config';
|
import config from '../../../../config';
|
||||||
|
import { publishToFollowers } from '../../../../services/i/update';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@@ -144,4 +145,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
|
|||||||
if (user.isLocked && isLocked === false) {
|
if (user.isLocked && isLocked === false) {
|
||||||
acceptAllFollowRequests(user);
|
acceptAllFollowRequests(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// フォロワーにUpdateを配信
|
||||||
|
publishToFollowers(user._id);
|
||||||
});
|
});
|
||||||
|
|||||||
43
src/server/api/endpoints/users/lists/delete.ts
Normal file
43
src/server/api/endpoints/users/lists/delete.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import $ from 'cafy';
|
||||||
|
import ID from '../../../../../misc/cafy-id';
|
||||||
|
import UserList, { deleteUserList } from '../../../../../models/user-list';
|
||||||
|
import { ILocalUser } from '../../../../../models/user';
|
||||||
|
import getParams from '../../../get-params';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '指定したユーザーリストを削除します。',
|
||||||
|
'en-US': 'Delete a user list'
|
||||||
|
},
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'account-write',
|
||||||
|
|
||||||
|
params: {
|
||||||
|
listId: $.type(ID).note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '対象となるユーザーリストのID',
|
||||||
|
'en-US': 'ID of target user list'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
|
const [ps, psErr] = getParams(meta, params);
|
||||||
|
if (psErr) return rej(psErr);
|
||||||
|
|
||||||
|
const userList = await UserList.findOne({
|
||||||
|
_id: ps.listId,
|
||||||
|
userId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userList == null) {
|
||||||
|
return rej('list not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteUserList(userList);
|
||||||
|
|
||||||
|
res();
|
||||||
|
});
|
||||||
56
src/server/api/endpoints/users/lists/update.ts
Normal file
56
src/server/api/endpoints/users/lists/update.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import $ from 'cafy';
|
||||||
|
import ID from '../../../../../misc/cafy-id';
|
||||||
|
import UserList, { pack } from '../../../../../models/user-list';
|
||||||
|
import { ILocalUser } from '../../../../../models/user';
|
||||||
|
import getParams from '../../../get-params';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '指定したユーザーリストを更新します。',
|
||||||
|
'en-US': 'Update a user list'
|
||||||
|
},
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'account-write',
|
||||||
|
|
||||||
|
params: {
|
||||||
|
listId: $.type(ID).note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '対象となるユーザーリストのID',
|
||||||
|
'en-US': 'ID of target user list'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
title: $.str.range(1, 100).note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'このユーザーリストの名前',
|
||||||
|
'en-US': 'name of this user list'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
|
const [ps, psErr] = getParams(meta, params);
|
||||||
|
if (psErr) throw psErr;
|
||||||
|
|
||||||
|
// Fetch the list
|
||||||
|
const userList = await UserList.findOne({
|
||||||
|
_id: ps.listId,
|
||||||
|
userId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userList == null) {
|
||||||
|
return rej('list not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// update
|
||||||
|
await UserList.update({ _id: userList._id }, {
|
||||||
|
$set: {
|
||||||
|
title: ps.title
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Response
|
||||||
|
res(await pack(userList._id));
|
||||||
|
});
|
||||||
@@ -16,7 +16,7 @@ export default function(request: websocket.request, connection: websocket.connec
|
|||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case 'requestLog':
|
case 'requestLog':
|
||||||
ev.once('notesStatsLog:' + msg.id, statsLog => {
|
ev.once(`notesStatsLog:${msg.id}`, statsLog => {
|
||||||
connection.send(JSON.stringify({
|
connection.send(JSON.stringify({
|
||||||
type: 'statsLog',
|
type: 'statsLog',
|
||||||
body: statsLog
|
body: statsLog
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function(request: websocket.request, connection: websocket.connec
|
|||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case 'requestLog':
|
case 'requestLog':
|
||||||
ev.once('serverStatsLog:' + msg.id, statsLog => {
|
ev.once(`serverStatsLog:${msg.id}`, statsLog => {
|
||||||
connection.send(JSON.stringify({
|
connection.send(JSON.stringify({
|
||||||
type: 'statsLog',
|
type: 'statsLog',
|
||||||
body: statsLog
|
body: statsLog
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ router.get('/*/api/entities/*', async ctx => {
|
|||||||
const lang = ctx.params[0];
|
const lang = ctx.params[0];
|
||||||
const entity = ctx.params[1];
|
const entity = ctx.params[1];
|
||||||
|
|
||||||
const x = yaml.safeLoad(fs.readFileSync(path.resolve(__dirname + '/../../../src/docs/api/entities/' + entity + '.yaml'), 'utf-8')) as any;
|
const x = yaml.safeLoad(fs.readFileSync(path.resolve(`${__dirname}/../../../src/docs/api/entities/${entity}.yaml`), 'utf-8')) as any;
|
||||||
|
|
||||||
await ctx.render('../../../../src/docs/api/entities/view', Object.assign(await genVars(lang), {
|
await ctx.render('../../../../src/docs/api/entities/view', Object.assign(await genVars(lang), {
|
||||||
id: `api/entities/${entity}`,
|
id: `api/entities/${entity}`,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ extends ../../../../src/client/app/base
|
|||||||
|
|
||||||
block vars
|
block vars
|
||||||
- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
|
- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
|
||||||
- const url = config.url + '/@' + (user.host ? `${user.username}@${user.host}` : user.username);
|
- const url = `${config.url}/@${(user.host ? `${user.username}@${user.host}` : user.username)}`;
|
||||||
- const img = user.avatarId ? `${config.drive_url}/${user.avatarId}` : null;
|
- const img = user.avatarId ? `${config.drive_url}/${user.avatarId}` : null;
|
||||||
|
|
||||||
block title
|
block title
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ async function save(path: string, name: string, type: string, hash: string, size
|
|||||||
const thumbnailKey = `${config.drive.prefix}/${uuid.v4()}/${name}.thumbnail.jpg`;
|
const thumbnailKey = `${config.drive.prefix}/${uuid.v4()}/${name}.thumbnail.jpg`;
|
||||||
|
|
||||||
const baseUrl = config.drive.baseUrl
|
const baseUrl = config.drive.baseUrl
|
||||||
|| `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
|
|| `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`;
|
||||||
|
|
||||||
await minio.putObject(config.drive.bucket, key, fs.createReadStream(path), size, {
|
await minio.putObject(config.drive.bucket, key, fs.createReadStream(path), size, {
|
||||||
'Content-Type': type,
|
'Content-Type': type,
|
||||||
|
|||||||
38
src/services/i/update.ts
Normal file
38
src/services/i/update.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import User, { isLocalUser, isRemoteUser } from '../../models/user';
|
||||||
|
import Following from '../../models/following';
|
||||||
|
import renderPerson from '../../remote/activitypub/renderer/person';
|
||||||
|
import renderUpdate from '../../remote/activitypub/renderer/update';
|
||||||
|
import packAp from '../../remote/activitypub/renderer';
|
||||||
|
import { deliver } from '../../queue';
|
||||||
|
|
||||||
|
export async function publishToFollowers(userId: mongo.ObjectID) {
|
||||||
|
const user = await User.findOne({
|
||||||
|
_id: userId
|
||||||
|
});
|
||||||
|
|
||||||
|
const followers = await Following.find({
|
||||||
|
followeeId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
const queue: string[] = [];
|
||||||
|
|
||||||
|
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
||||||
|
if (isLocalUser(user)) {
|
||||||
|
followers.map(following => {
|
||||||
|
const follower = following._follower;
|
||||||
|
|
||||||
|
if (isRemoteUser(follower)) {
|
||||||
|
const inbox = follower.sharedInbox || follower.inbox;
|
||||||
|
if (!queue.includes(inbox)) queue.push(inbox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (queue.length > 0) {
|
||||||
|
const content = packAp(renderUpdate(await renderPerson(user), user));
|
||||||
|
queue.forEach(inbox => {
|
||||||
|
deliver(user, content, inbox);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,9 @@ import renderDelete from '../../remote/activitypub/renderer/delete';
|
|||||||
import pack from '../../remote/activitypub/renderer';
|
import pack from '../../remote/activitypub/renderer';
|
||||||
import { deliver } from '../../queue';
|
import { deliver } from '../../queue';
|
||||||
import Following from '../../models/following';
|
import Following from '../../models/following';
|
||||||
import renderNote from '../../remote/activitypub/renderer/note';
|
import renderTombstone from '../../remote/activitypub/renderer/tombstone';
|
||||||
import { updateNoteStats } from '../update-chart';
|
import { updateNoteStats } from '../update-chart';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 投稿を削除します。
|
* 投稿を削除します。
|
||||||
@@ -32,7 +33,7 @@ export default async function(user: IUser, note: INote) {
|
|||||||
|
|
||||||
//#region ローカルの投稿なら削除アクティビティを配送
|
//#region ローカルの投稿なら削除アクティビティを配送
|
||||||
if (isLocalUser(user)) {
|
if (isLocalUser(user)) {
|
||||||
const content = pack(renderDelete(await renderNote(note), user));
|
const content = pack(renderDelete(renderTombstone(`${config.url}/notes/${note._id}`), user));
|
||||||
|
|
||||||
const followings = await Following.find({
|
const followings = await Following.find({
|
||||||
followeeId: user._id,
|
followeeId: user._id,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const constants = require('./src/const.json');
|
|||||||
|
|
||||||
const locales = require('./locales');
|
const locales = require('./locales');
|
||||||
const meta = require('./package.json');
|
const meta = require('./package.json');
|
||||||
const version = meta.clientVersion + '-' + rndstr({ length: 8, chars: '0-9a-z' });
|
const version = `${meta.clientVersion}-${rndstr({ length: 8, chars: '0-9a-z' })}`;
|
||||||
const codename = meta.codename;
|
const codename = meta.codename;
|
||||||
|
|
||||||
declare var global: {
|
declare var global: {
|
||||||
@@ -42,7 +42,7 @@ global['collapseSpacesReplacement'] = (html: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
global['base64replacement'] = (_: any, key: string) => {
|
global['base64replacement'] = (_: any, key: string) => {
|
||||||
return fs.readFileSync(__dirname + '/src/client/' + key, 'base64');
|
return fs.readFileSync(`${__dirname}/src/client/${key}`, 'base64');
|
||||||
};
|
};
|
||||||
|
|
||||||
global['i18nReplacement'] = i18nReplacement;
|
global['i18nReplacement'] = i18nReplacement;
|
||||||
|
|||||||
Reference in New Issue
Block a user