Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb0874f15a | ||
|
|
9239e37b45 | ||
|
|
57d80932a4 | ||
|
|
8569970fbe | ||
|
|
713e9ad5f4 | ||
|
|
59e229d962 | ||
|
|
3c5f09cda2 | ||
|
|
e42aa2530d | ||
|
|
1fd298ac9c | ||
|
|
1a73f52541 | ||
|
|
27e458f884 | ||
|
|
ba3e2a9371 | ||
|
|
831e8f8583 | ||
|
|
0ff390ed80 | ||
|
|
e3b8495431 | ||
|
|
da10ba3fea | ||
|
|
cc7de853b4 | ||
|
|
23d8235197 | ||
|
|
37e3d60ade | ||
|
|
83a3426dd5 | ||
|
|
a2549192ca | ||
|
|
9d3a1cab6e | ||
|
|
06f8d8f0a3 | ||
|
|
fa66b79e2d | ||
|
|
81312f5a93 | ||
|
|
ad84901f39 | ||
|
|
d2385a0e52 | ||
|
|
39285fc2d0 | ||
|
|
6e491c1466 | ||
|
|
84152aa663 | ||
|
|
600fc65c2f | ||
|
|
40e2733424 | ||
|
|
bceb02d760 | ||
|
|
aaaaf2681a | ||
|
|
3c3ef9bba0 | ||
|
|
338f3a981f | ||
|
|
a86ae9fa50 | ||
|
|
a3c4e8a1bc | ||
|
|
6a7c18e8db | ||
|
|
672b7a4c3d | ||
|
|
bf3fee4481 | ||
|
|
a3c8d1d732 | ||
|
|
bb03d8c49a | ||
|
|
fbd5abe3b6 | ||
|
|
5ac390abe9 | ||
|
|
766cae2299 |
@@ -13,7 +13,7 @@ This guide describes how to install and setup Misskey with Docker.
|
|||||||
2. `cd misskey` Move to misskey directory.
|
2. `cd misskey` Move to misskey directory.
|
||||||
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag.
|
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag.
|
||||||
|
|
||||||
*2.* Make configuration files
|
*2.* Configure Misskey
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
|
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
|
||||||
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`.
|
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`.
|
||||||
@@ -31,12 +31,12 @@ Build misskey with the following:
|
|||||||
|
|
||||||
*5.* That is it.
|
*5.* That is it.
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Well done! Now, you have an environment that run to Misskey.
|
Well done! Now you have an environment to run Misskey.
|
||||||
|
|
||||||
### Launch normally
|
### Launch normally
|
||||||
Just `docker-compose up -d`. GLHF!
|
Just `docker-compose up -d`. GLHF!
|
||||||
|
|
||||||
### Way to Update to latest version of your Misskey
|
### How to update your Misskey server to the latest version
|
||||||
1. `git fetch`
|
1. `git fetch`
|
||||||
2. `git stash`
|
2. `git stash`
|
||||||
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||||
@@ -45,9 +45,9 @@ Just `docker-compose up -d`. GLHF!
|
|||||||
6. Check [ChangeLog](../CHANGELOG.md) for migration information
|
6. Check [ChangeLog](../CHANGELOG.md) for migration information
|
||||||
7. `docker-compose stop && docker-compose up -d`
|
7. `docker-compose stop && docker-compose up -d`
|
||||||
|
|
||||||
### Way to execute cli command:
|
### How to execute [cli commands](manage.en.md):
|
||||||
`docker-compose run --rm web node cli/mark-admin @example`
|
`docker-compose run --rm web node cli/mark-admin @example`
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
|
||||||
If you have any questions or troubles, feel free to contact us!
|
If you have any questions or trouble, feel free to contact us!
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This guide describes how to install and setup Misskey.
|
|||||||
|
|
||||||
*1.* Create Misskey user
|
*1.* Create Misskey user
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
Running misskey on root is not a good idea so we create a user for that.
|
Running misskey as root is not a good idea so we create a user for that.
|
||||||
In debian for exemple :
|
In debian for exemple :
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -32,7 +32,7 @@ Please install and setup these softwares:
|
|||||||
|
|
||||||
*3.* Setup MongoDB
|
*3.* Setup MongoDB
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
In root :
|
As root:
|
||||||
1. `mongo` Go to the mongo shell
|
1. `mongo` Go to the mongo shell
|
||||||
2. `use misskey` Use the misskey database
|
2. `use misskey` Use the misskey database
|
||||||
3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db.
|
3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db.
|
||||||
@@ -47,17 +47,17 @@ In root :
|
|||||||
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
|
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
|
||||||
5. `npm install` Install misskey dependencies.
|
5. `npm install` Install misskey dependencies.
|
||||||
|
|
||||||
*(optional)* Generating VAPID keys
|
*(optional)* Generate VAPID keys
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
If you want to enable ServiceWorker, 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.
|
Unless you have set your global node_modules location elsewhere, you need to run this as root.
|
||||||
|
|
||||||
``` shell
|
``` shell
|
||||||
npm install web-push -g
|
npm install web-push -g
|
||||||
web-push generate-vapid-keys
|
web-push generate-vapid-keys
|
||||||
```
|
```
|
||||||
|
|
||||||
*5.* Make configuration file
|
*5.* Configure Misskey
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
|
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
|
||||||
2. Edit `default.yml`
|
2. Edit `default.yml`
|
||||||
@@ -114,7 +114,7 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
You can check if the service is running with `systemctl status misskey`.
|
You can check if the service is running with `systemctl status misskey`.
|
||||||
|
|
||||||
### Way to Update to latest version of your Misskey
|
### How to update your Misskey server to the latest version
|
||||||
1. `git fetch`
|
1. `git fetch`
|
||||||
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`
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ common:
|
|||||||
specified: "ダイレクト"
|
specified: "ダイレクト"
|
||||||
specified-desc: "指定したユーザーにのみ公開"
|
specified-desc: "指定したユーザーにのみ公開"
|
||||||
private: "非公開"
|
private: "非公開"
|
||||||
|
local-public: "公開(ローカルのみ)"
|
||||||
|
local-home: "ホーム(ローカルのみ)"
|
||||||
|
local-followers: "フォロワー(ローカルのみ)"
|
||||||
|
|
||||||
note-placeholders:
|
note-placeholders:
|
||||||
a: "今どうしてる?"
|
a: "今どうしてる?"
|
||||||
@@ -471,6 +474,9 @@ common/views/components/visibility-chooser.vue:
|
|||||||
specified: "ダイレクト"
|
specified: "ダイレクト"
|
||||||
specified-desc: "指定したユーザーにのみ公開"
|
specified-desc: "指定したユーザーにのみ公開"
|
||||||
private: "非公開"
|
private: "非公開"
|
||||||
|
local-public: "公開(ローカルのみ)"
|
||||||
|
local-home: "ホーム(ローカルのみ)"
|
||||||
|
local-followers: "フォロワー(ローカルのみ)"
|
||||||
|
|
||||||
common/views/components/trends.vue:
|
common/views/components/trends.vue:
|
||||||
count: "{}人が投稿"
|
count: "{}人が投稿"
|
||||||
@@ -761,6 +767,7 @@ desktop/views/components/post-form.vue:
|
|||||||
create-poll: "アンケートを作成"
|
create-poll: "アンケートを作成"
|
||||||
text-remain: "残り{}文字"
|
text-remain: "残り{}文字"
|
||||||
recent-tags: "最近"
|
recent-tags: "最近"
|
||||||
|
local-only-message: "この投稿はローカルにのみ公開されます"
|
||||||
click-to-tagging: "クリックでタグ付け"
|
click-to-tagging: "クリックでタグ付け"
|
||||||
visibility: "公開範囲"
|
visibility: "公開範囲"
|
||||||
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
||||||
@@ -799,6 +806,7 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
|
tags: "ハッシュタグ"
|
||||||
mute-and-block: "ミュート/ブロック"
|
mute-and-block: "ミュート/ブロック"
|
||||||
blocking: "ブロック"
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
@@ -843,6 +851,7 @@ desktop/views/components/settings.vue:
|
|||||||
show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する"
|
show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する"
|
||||||
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
|
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
|
||||||
show-maps: "マップの自動展開"
|
show-maps: "マップの自動展開"
|
||||||
|
remain-deleted-note: "削除された投稿を表示し続ける"
|
||||||
deck-column-align: "デッキのカラムの位置"
|
deck-column-align: "デッキのカラムの位置"
|
||||||
deck-column-align-center: "中央"
|
deck-column-align-center: "中央"
|
||||||
deck-column-align-left: "左"
|
deck-column-align-left: "左"
|
||||||
@@ -1045,7 +1054,7 @@ admin/views/index.vue:
|
|||||||
emoji: "カスタム絵文字"
|
emoji: "カスタム絵文字"
|
||||||
moderators: "モデレーター"
|
moderators: "モデレーター"
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
update: "更新"
|
federation: "連合"
|
||||||
announcements: "お知らせ"
|
announcements: "お知らせ"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "ハッシュタグ"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
|
|||||||
@@ -137,11 +137,11 @@ common:
|
|||||||
widgets:
|
widgets:
|
||||||
analog-clock: "指针时钟"
|
analog-clock: "指针时钟"
|
||||||
profile: "简介"
|
profile: "简介"
|
||||||
calendar: "カレンダー"
|
calendar: "日历"
|
||||||
timemachine: "カレンダー(タイムマシン)"
|
timemachine: "时光机"
|
||||||
activity: "アクティビティ"
|
activity: "动态"
|
||||||
rss: "RSSリーダー"
|
rss: "RSS 订阅"
|
||||||
memo: "付箋"
|
memo: "便签"
|
||||||
trends: "トレンド"
|
trends: "トレンド"
|
||||||
photo-stream: "フォトストリーム"
|
photo-stream: "フォトストリーム"
|
||||||
posts-monitor: "投稿チャート"
|
posts-monitor: "投稿チャート"
|
||||||
@@ -195,60 +195,60 @@ common/views/components/games/reversi/reversi.game.vue:
|
|||||||
can-put-everywhere: "どこでも置けるモード"
|
can-put-everywhere: "どこでも置けるモード"
|
||||||
common/views/components/games/reversi/reversi.index.vue:
|
common/views/components/games/reversi/reversi.index.vue:
|
||||||
title: "Misskey Reversi"
|
title: "Misskey Reversi"
|
||||||
sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
|
sub-title: "和你的朋友一起玩 Reversi!"
|
||||||
invite: "招待"
|
invite: "邀请"
|
||||||
rule: "遊び方"
|
rule: "游戏说明"
|
||||||
rule-desc: "リバーシは、相手と交互に石をボードに置いて、相手の石を挟んで自分の色に変えてゆき、最終的に残った石が多い方が勝ちというボードゲームです。"
|
rule-desc: "Reversi是与对方交替地把石头放在板上,把对方的石头夹在自己的颜色上,最终留下的石头多的人获胜。"
|
||||||
mode-invite: "招待"
|
mode-invite: "邀请"
|
||||||
mode-invite-desc: "指定したユーザーと対戦するモードです。"
|
mode-invite-desc: "邀请指定用户参加游戏"
|
||||||
invitations: "対局の招待があります!"
|
invitations: "您收到了一则邀请!"
|
||||||
my-games: "自分の対局"
|
my-games: "我的游戏"
|
||||||
all-games: "みんなの対局"
|
all-games: "所有游戏"
|
||||||
enter-username: "ユーザー名を入力してください"
|
enter-username: "输入用户名"
|
||||||
game-state:
|
game-state:
|
||||||
ended: "終了"
|
ended: "完成"
|
||||||
playing: "進行中"
|
playing: "进行中"
|
||||||
common/views/components/games/reversi/reversi.room.vue:
|
common/views/components/games/reversi/reversi.room.vue:
|
||||||
settings-of-the-game: "ゲームの設定"
|
settings-of-the-game: "游戏设置"
|
||||||
choose-map: "マップを選択"
|
choose-map: "选择一个地图"
|
||||||
random: "ランダム"
|
random: "随机"
|
||||||
black-or-white: "先手/後手"
|
black-or-white: "黑/白"
|
||||||
black-is: "{}が黒"
|
black-is: "{}是黑"
|
||||||
rules: "ルール"
|
rules: "规则"
|
||||||
is-llotheo: "石の少ない方が勝ち(ロセオ)"
|
is-llotheo: "较少一方获胜"
|
||||||
looped-map: "ループマップ"
|
looped-map: "环状地图"
|
||||||
can-put-everywhere: "どこでも置けるモード"
|
can-put-everywhere: "可以随意放置"
|
||||||
settings-of-the-bot: "Botの設定"
|
settings-of-the-bot: "机器人设定"
|
||||||
this-game-is-started-soon: "ゲームは数秒後に開始されます"
|
this-game-is-started-soon: "游戏即将在数秒后开始"
|
||||||
waiting-for-other: "相手の準備が完了するのを待っています"
|
waiting-for-other: "等待对手准备"
|
||||||
waiting-for-me: "あなたの準備が完了するのを待っています"
|
waiting-for-me: "等待您的准备"
|
||||||
waiting-for-both: "準備中"
|
waiting-for-both: "准备中"
|
||||||
cancel: "キャンセル"
|
cancel: "取消"
|
||||||
ready: "準備完了"
|
ready: "准备好啦"
|
||||||
cancel-ready: "準備続行"
|
cancel-ready: "取消准备"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "无法连接至服务器"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "您的网络连接可能出现了问题, 或是远程服务器暂时不可用. 请稍后{重试}."
|
||||||
thanks: "いつもMisskeyをご利用いただきありがとうございます。"
|
thanks: "感谢您使用 Misskey"
|
||||||
troubleshoot: "トラブルシュート"
|
troubleshoot: "故障排除"
|
||||||
common/views/components/connect-failed.troubleshooter.vue:
|
common/views/components/connect-failed.troubleshooter.vue:
|
||||||
title: "トラブルシューティング"
|
title: "正在排除故障"
|
||||||
network: "ネットワーク接続"
|
network: "网络已连接"
|
||||||
checking-network: "ネットワーク接続を確認中"
|
checking-network: "正在检查网络连接"
|
||||||
internet: "インターネット接続"
|
internet: "网络连接"
|
||||||
checking-internet: "インターネット接続を確認中"
|
checking-internet: "正在检查网络连接"
|
||||||
server: "サーバー接続"
|
server: "已连接至服务器"
|
||||||
checking-server: "サーバー接続を確認中"
|
checking-server: "正在检查与服务器的连接"
|
||||||
finding: "問題を調べています"
|
finding: "搜索问题"
|
||||||
no-network: "ネットワークに接続されていません"
|
no-network: "无网络连接"
|
||||||
no-network-desc: "お使いのPCのネットワーク接続が正常か確認してください。"
|
no-network-desc: "请确保您已连接至互联网"
|
||||||
no-internet: "インターネットに接続されていません"
|
no-internet: "无网络连接"
|
||||||
no-internet-desc: "ネットワークには接続されていますが、インターネットには接続されていないようです。お使いのPCのインターネット接続が正常か確認してください。"
|
no-internet-desc: "请确保您已连接至互联网"
|
||||||
no-server: "Misskeyのサーバーに接続できません"
|
no-server: "无法连接到 Misskey 服务器"
|
||||||
no-server-desc: "お使いのPCのインターネット接続は正常ですが、Misskeyのサーバーには接続できませんでした。サーバーがダウンまたはメンテナンスしている可能性があるので、しばらくしてから再度御アクセスください。"
|
no-server-desc: "您设备与互联网的网络连接正常, 但是无法连接至 Misskey 服务器. 这可能是服务器暂时不可用或正在维护. 请稍后再试."
|
||||||
success: "Misskeyのサーバーに接続できました"
|
success: "成功连接至 Misskey 服务器"
|
||||||
success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
|
success-desc: "看起来我们连接正常. 请刷新网页."
|
||||||
flush: "キャッシュの削除"
|
flush: "清除缓存"
|
||||||
set-version: "バージョン指定"
|
set-version: "バージョン指定"
|
||||||
common/views/components/media-banner.vue:
|
common/views/components/media-banner.vue:
|
||||||
sensitive: "閲覧注意"
|
sensitive: "閲覧注意"
|
||||||
@@ -305,36 +305,36 @@ common/views/components/messaging-room.form.vue:
|
|||||||
input-message-here: "ここにメッセージを入力"
|
input-message-here: "ここにメッセージを入力"
|
||||||
send: "送信"
|
send: "送信"
|
||||||
attach-from-local: "PCからファイルを添付する"
|
attach-from-local: "PCからファイルを添付する"
|
||||||
attach-from-drive: "ドライブからファイルを添付する"
|
attach-from-drive: "从云盘中添加文件"
|
||||||
only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
|
only-one-file-attached: "在信息中只允许添加一个附件"
|
||||||
common/views/components/messaging-room.message.vue:
|
common/views/components/messaging-room.message.vue:
|
||||||
is-read: "既読"
|
is-read: "已阅"
|
||||||
deleted: "このメッセージは削除されました"
|
deleted: "这条信息已被删除"
|
||||||
common/views/components/nav.vue:
|
common/views/components/nav.vue:
|
||||||
about: "Misskeyについて"
|
about: "关于"
|
||||||
stats: "統計"
|
stats: "统计"
|
||||||
status: "ステータス"
|
status: "状态"
|
||||||
wiki: "Wiki"
|
wiki: "百科 (Wiki)"
|
||||||
donors: "ドナー"
|
donors: "捐赠者"
|
||||||
repository: "リポジトリ"
|
repository: "源"
|
||||||
develop: "開発者"
|
develop: "开发者"
|
||||||
feedback: "フィードバック"
|
feedback: "反馈"
|
||||||
common/views/components/note-menu.vue:
|
common/views/components/note-menu.vue:
|
||||||
detail: "詳細"
|
detail: "详细信息"
|
||||||
copy-link: "リンクをコピー"
|
copy-link: "复制链接"
|
||||||
favorite: "お気に入り"
|
favorite: "收藏这个投稿"
|
||||||
unfavorite: "お気に入り解除"
|
unfavorite: "取消收藏"
|
||||||
pin: "ピン留め"
|
pin: "固定个人资料"
|
||||||
unpin: "ピン留め解除"
|
unpin: "解除固定"
|
||||||
delete: "削除"
|
delete: "删除"
|
||||||
delete-confirm: "この投稿を削除しますか?"
|
delete-confirm: "确定删除这个投稿吗?"
|
||||||
remote: "投稿元で見る"
|
remote: "显示原始投稿"
|
||||||
common/views/components/poll.vue:
|
common/views/components/poll.vue:
|
||||||
vote-to: "「{}」に投票する"
|
vote-to: "为\"{}\"投票"
|
||||||
vote-count: "{}票"
|
vote-count: "{}票"
|
||||||
total-users: "{}人が投票"
|
total-users: "{} 人投票"
|
||||||
vote: "投票する"
|
vote: "投票"
|
||||||
show-result: "結果を見る"
|
show-result: "显示结果"
|
||||||
voted: "投票済み"
|
voted: "投票済み"
|
||||||
common/views/components/poll-editor.vue:
|
common/views/components/poll-editor.vue:
|
||||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.52.0",
|
"version": "10.54.0",
|
||||||
"clientVersion": "2.0.11848",
|
"clientVersion": "2.0.11894",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
"@types/is-root": "1.0.0",
|
"@types/is-root": "1.0.0",
|
||||||
"@types/is-url": "1.2.28",
|
"@types/is-url": "1.2.28",
|
||||||
"@types/js-yaml": "3.11.2",
|
"@types/js-yaml": "3.11.2",
|
||||||
|
"@types/katex": "0.5.0",
|
||||||
"@types/koa": "2.0.46",
|
"@types/koa": "2.0.46",
|
||||||
"@types/koa-bodyparser": "5.0.1",
|
"@types/koa-bodyparser": "5.0.1",
|
||||||
"@types/koa-compress": "2.0.8",
|
"@types/koa-compress": "2.0.8",
|
||||||
@@ -140,6 +141,7 @@
|
|||||||
"jsdom": "13.0.0",
|
"jsdom": "13.0.0",
|
||||||
"json5": "2.1.0",
|
"json5": "2.1.0",
|
||||||
"json5-loader": "1.0.1",
|
"json5-loader": "1.0.1",
|
||||||
|
"katex": "0.10.0",
|
||||||
"koa": "2.6.1",
|
"koa": "2.6.1",
|
||||||
"koa-bodyparser": "4.2.1",
|
"koa-bodyparser": "4.2.1",
|
||||||
"koa-compress": "3.0.0",
|
"koa-compress": "3.0.0",
|
||||||
@@ -158,7 +160,7 @@
|
|||||||
"mocha": "5.2.0",
|
"mocha": "5.2.0",
|
||||||
"moji": "0.5.1",
|
"moji": "0.5.1",
|
||||||
"moment": "2.22.2",
|
"moment": "2.22.2",
|
||||||
"mongodb": "3.1.8",
|
"mongodb": "3.1.9",
|
||||||
"monk": "6.0.6",
|
"monk": "6.0.6",
|
||||||
"ms": "2.1.1",
|
"ms": "2.1.1",
|
||||||
"nan": "2.11.1",
|
"nan": "2.11.1",
|
||||||
@@ -189,7 +191,7 @@
|
|||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"seedrandom": "2.4.4",
|
"seedrandom": "2.4.4",
|
||||||
"sharp": "0.21.0",
|
"sharp": "0.21.0",
|
||||||
"showdown": "1.8.7",
|
"showdown": "1.9.0",
|
||||||
"showdown-highlightjs-extension": "0.1.2",
|
"showdown-highlightjs-extension": "0.1.2",
|
||||||
"speakeasy": "2.0.0",
|
"speakeasy": "2.0.0",
|
||||||
"stringz": "1.0.0",
|
"stringz": "1.0.0",
|
||||||
@@ -207,7 +209,7 @@
|
|||||||
"ts-node": "7.0.1",
|
"ts-node": "7.0.1",
|
||||||
"tslint": "5.10.0",
|
"tslint": "5.10.0",
|
||||||
"typescript": "3.1.6",
|
"typescript": "3.1.6",
|
||||||
"typescript-eslint-parser": "20.1.1",
|
"typescript-eslint-parser": "21.0.0",
|
||||||
"uglify-es": "3.3.9",
|
"uglify-es": "3.3.9",
|
||||||
"url-loader": "1.1.2",
|
"url-loader": "1.1.2",
|
||||||
"uuid": "3.3.2",
|
"uuid": "3.3.2",
|
||||||
|
|||||||
@@ -8,13 +8,19 @@
|
|||||||
<p>{{ $t('@.ai-chan-kawaii') }}</p>
|
<p>{{ $t('@.ai-chan-kawaii') }}</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<marquee-text v-if="instances.length > 0" class="instances" :repeat="10" :duration="60">
|
||||||
|
<span v-for="instance in instances" class="instance">
|
||||||
|
<b :style="{ background: instance.bg }">{{ instance.host }}</b>{{ instance.notesCount | number }} / {{ instance.usersCount | number }}
|
||||||
|
</span>
|
||||||
|
</marquee-text>
|
||||||
|
|
||||||
<div v-if="stats" class="stats">
|
<div v-if="stats" class="stats">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<div><fa icon="user"/></div>
|
<div><fa icon="user"/></div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ $t('accounts') }}</span>
|
<span>{{ $t('accounts') }}</span>
|
||||||
<b class="primary">{{ stats.originalUsersCount | number }}</b>
|
<b>{{ stats.originalUsersCount | number }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -27,7 +33,7 @@
|
|||||||
<div><fa icon="pencil-alt"/></div>
|
<div><fa icon="pencil-alt"/></div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ $t('notes') }}</span>
|
<span>{{ $t('notes') }}</span>
|
||||||
<b class="primary">{{ stats.originalNotesCount | number }}</b>
|
<b>{{ stats.originalNotesCount | number }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -84,6 +90,8 @@ import XCpuMemory from "./cpu-memory.vue";
|
|||||||
import XCharts from "./charts.vue";
|
import XCharts from "./charts.vue";
|
||||||
import XApLog from "./ap-log.vue";
|
import XApLog from "./ap-log.vue";
|
||||||
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import MarqueeText from 'vue-marquee-text-component';
|
||||||
|
import randomColor from 'randomcolor';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('admin/views/dashboard.vue'),
|
i18n: i18n('admin/views/dashboard.vue'),
|
||||||
@@ -91,7 +99,8 @@ export default Vue.extend({
|
|||||||
components: {
|
components: {
|
||||||
XCpuMemory,
|
XCpuMemory,
|
||||||
XCharts,
|
XCharts,
|
||||||
XApLog
|
XApLog,
|
||||||
|
MarqueeText
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -99,6 +108,8 @@ export default Vue.extend({
|
|||||||
stats: null,
|
stats: null,
|
||||||
connection: null,
|
connection: null,
|
||||||
meta: null,
|
meta: null,
|
||||||
|
instances: [],
|
||||||
|
clock: null,
|
||||||
faDatabase
|
faDatabase
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -106,22 +117,40 @@ export default Vue.extend({
|
|||||||
created() {
|
created() {
|
||||||
this.connection = this.$root.stream.useSharedConnection('serverStats');
|
this.connection = this.$root.stream.useSharedConnection('serverStats');
|
||||||
|
|
||||||
|
this.updateStats();
|
||||||
|
this.clock = setInterval(this.updateStats, 1000);
|
||||||
|
|
||||||
this.$root.getMeta().then(meta => {
|
this.$root.getMeta().then(meta => {
|
||||||
this.meta = meta;
|
this.meta = meta;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$root.api('stats').then(stats => {
|
this.$root.api('instances', {
|
||||||
this.stats = stats;
|
sort: '+notes'
|
||||||
|
}).then(instances => {
|
||||||
|
instances.forEach(i => {
|
||||||
|
i.bg = randomColor({
|
||||||
|
seed: i.host,
|
||||||
|
luminosity: 'dark'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.instances = instances;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
|
clearInterval(this.clock);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setChartSrc(src) {
|
setChartSrc(src) {
|
||||||
this.$refs.charts.setSrc(src);
|
this.$refs.charts.setSrc(src);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStats() {
|
||||||
|
this.$root.api('stats', {}, false, true).then(stats => {
|
||||||
|
this.stats = stats;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -136,7 +165,6 @@ export default Vue.extend({
|
|||||||
|
|
||||||
> header
|
> header
|
||||||
display flex
|
display flex
|
||||||
margin-bottom 16px
|
|
||||||
padding-bottom 16px
|
padding-bottom 16px
|
||||||
border-bottom solid 1px var(--adminDashboardHeaderBorder)
|
border-bottom solid 1px var(--adminDashboardHeaderBorder)
|
||||||
color var(--adminDashboardHeaderFg)
|
color var(--adminDashboardHeaderFg)
|
||||||
@@ -161,6 +189,20 @@ export default Vue.extend({
|
|||||||
margin-left auto
|
margin-left auto
|
||||||
margin-right 0
|
margin-right 0
|
||||||
|
|
||||||
|
> .instances
|
||||||
|
padding 16px
|
||||||
|
color var(--adminDashboardHeaderFg)
|
||||||
|
font-size 13px
|
||||||
|
|
||||||
|
>>> .instance
|
||||||
|
margin 0 10px
|
||||||
|
|
||||||
|
> b
|
||||||
|
padding 2px 6px
|
||||||
|
margin-right 4px
|
||||||
|
border-radius 4px
|
||||||
|
color #fff
|
||||||
|
|
||||||
> .stats
|
> .stats
|
||||||
display flex
|
display flex
|
||||||
justify-content space-between
|
justify-content space-between
|
||||||
@@ -201,9 +243,6 @@ export default Vue.extend({
|
|||||||
> b
|
> b
|
||||||
display block
|
display block
|
||||||
|
|
||||||
&.primary
|
|
||||||
color var(--primary)
|
|
||||||
|
|
||||||
> div:last-child
|
> div:last-child
|
||||||
display flex
|
display flex
|
||||||
padding 6px 16px
|
padding 6px 16px
|
||||||
|
|||||||
@@ -22,12 +22,12 @@
|
|||||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||||
|
<!-- <li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faShareAlt" fixed-width/>{{ $t('federation') }}</li> -->
|
||||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
||||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
||||||
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
|
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
|
||||||
|
|
||||||
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li> -->
|
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li> -->
|
||||||
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">{{ $t('update') }}</li> -->
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="back-to-misskey">
|
<div class="back-to-misskey">
|
||||||
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
|
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
|
||||||
@@ -37,11 +37,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<main>
|
<main>
|
||||||
<marquee-text v-if="instances.length > 0" class="instances" :repeat="10" :duration="30">
|
|
||||||
<span v-for="instance in instances" class="instance">
|
|
||||||
<b :style="{ background: instance.bg }">{{ instance.host }}</b>{{ instance.notesCount | number }} / {{ instance.usersCount | number }}
|
|
||||||
</span>
|
|
||||||
</marquee-text>
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div v-if="page == 'dashboard'"><x-dashboard/></div>
|
<div v-if="page == 'dashboard'"><x-dashboard/></div>
|
||||||
<div v-if="page == 'instance'"><x-instance/></div>
|
<div v-if="page == 'instance'"><x-instance/></div>
|
||||||
@@ -68,10 +63,8 @@ import XEmoji from "./emoji.vue";
|
|||||||
import XAnnouncements from "./announcements.vue";
|
import XAnnouncements from "./announcements.vue";
|
||||||
import XHashtags from "./hashtags.vue";
|
import XHashtags from "./hashtags.vue";
|
||||||
import XUsers from "./users.vue";
|
import XUsers from "./users.vue";
|
||||||
import { faHeadset, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
|
import { faHeadset, faArrowLeft, faShareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
||||||
import MarqueeText from 'vue-marquee-text-component';
|
|
||||||
import randomColor from 'randomcolor';
|
|
||||||
|
|
||||||
// Detect the user agent
|
// Detect the user agent
|
||||||
const ua = navigator.userAgent.toLowerCase();
|
const ua = navigator.userAgent.toLowerCase();
|
||||||
@@ -86,8 +79,7 @@ export default Vue.extend({
|
|||||||
XEmoji,
|
XEmoji,
|
||||||
XAnnouncements,
|
XAnnouncements,
|
||||||
XHashtags,
|
XHashtags,
|
||||||
XUsers,
|
XUsers
|
||||||
MarqueeText
|
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
isMobile
|
isMobile
|
||||||
@@ -98,25 +90,12 @@ export default Vue.extend({
|
|||||||
version,
|
version,
|
||||||
isMobile,
|
isMobile,
|
||||||
navOpend: !isMobile,
|
navOpend: !isMobile,
|
||||||
instances: [],
|
|
||||||
faGrin,
|
faGrin,
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
faHeadset
|
faHeadset,
|
||||||
|
faShareAlt
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.$root.api('instances', {
|
|
||||||
sort: '+notes'
|
|
||||||
}).then(instances => {
|
|
||||||
instances.forEach(i => {
|
|
||||||
i.bg = randomColor({
|
|
||||||
seed: i.host,
|
|
||||||
luminosity: 'dark'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.instances = instances;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
nav(page: string) {
|
nav(page: string) {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
@@ -287,22 +266,8 @@ export default Vue.extend({
|
|||||||
width 100%
|
width 100%
|
||||||
padding 0 0 0 250px
|
padding 0 0 0 250px
|
||||||
|
|
||||||
> .instances
|
|
||||||
padding 10px
|
|
||||||
background #000
|
|
||||||
color #fff
|
|
||||||
font-size 13px
|
|
||||||
|
|
||||||
>>> .instance
|
|
||||||
margin 0 10px
|
|
||||||
|
|
||||||
> b
|
|
||||||
padding 0px 6px
|
|
||||||
margin-right 4px
|
|
||||||
border-radius 4px
|
|
||||||
|
|
||||||
> .page
|
> .page
|
||||||
max-width 1300px
|
max-width 1150px
|
||||||
|
|
||||||
&.isMobile
|
&.isMobile
|
||||||
> main
|
> main
|
||||||
|
|||||||
@@ -69,11 +69,14 @@
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
let locale = localStorage.getItem('locale');
|
let locale = localStorage.getItem('locale');
|
||||||
if (locale == null) {
|
const localeKey = localStorage.getItem('localeKey');
|
||||||
|
|
||||||
|
if (locale == null || localeKey != `${ver}.${lang}`) {
|
||||||
const locale = await fetch(`/assets/locales/${lang}.json?ver=${ver}`)
|
const locale = await fetch(`/assets/locales/${lang}.json?ver=${ver}`)
|
||||||
.then(response => response.json());
|
.then(response => response.json());
|
||||||
|
|
||||||
localStorage.setItem('locale', JSON.stringify(locale));
|
localStorage.setItem('locale', JSON.stringify(locale));
|
||||||
|
localStorage.setItem('localeKey', `${ver}.${lang}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect the user agent
|
// Detect the user agent
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import parse from '../../../../mfm/parse';
|
import parse from '../../../../mfm/parse';
|
||||||
import { sum } from '../../../../prelude/array';
|
import { sum, unique } from '../../../../prelude/array';
|
||||||
import shouldMuteNote from './should-mute-note';
|
import shouldMuteNote from './should-mute-note';
|
||||||
import MkNoteMenu from '../views/components/note-menu.vue';
|
import MkNoteMenu from '../views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../views/components/reaction-picker.vue';
|
import MkReactionPicker from '../views/components/reaction-picker.vue';
|
||||||
@@ -78,9 +78,9 @@ export default (opts: Opts = {}) => ({
|
|||||||
urls(): string[] {
|
urls(): string[] {
|
||||||
if (this.appearNote.text) {
|
if (this.appearNote.text) {
|
||||||
const ast = parse(this.appearNote.text);
|
const ast = parse(this.appearNote.text);
|
||||||
return ast
|
return unique(ast
|
||||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
.map(t => t.url);
|
.map(t => t.url));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/client/app/common/views/components/formula-core.vue
Normal file
26
src/client/app/common/views/components/formula-core.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<span v-html="compiledFormula"></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import * as katex from 'katex';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: {
|
||||||
|
formula: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
compiledFormula(): any {
|
||||||
|
return katex.renderToString(this.formula);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import "../../../../../../node_modules/katex/dist/katex.min.css";
|
||||||
|
</style>
|
||||||
20
src/client/app/common/views/components/formula.vue
Normal file
20
src/client/app/common/views/components/formula.vue
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<x-formula :formula="formula"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XFormula: () => import('./formula-core.vue').then(m => m.default)
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
formula: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import parse from '../../../../../mfm/parse';
|
import parse from '../../../../../mfm/parse';
|
||||||
|
import { unique } from '../../../../../prelude/array';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('common/views/components/messaging-room.message.vue'),
|
i18n: i18n('common/views/components/messaging-room.message.vue'),
|
||||||
@@ -49,9 +50,9 @@ export default Vue.extend({
|
|||||||
urls(): string[] {
|
urls(): string[] {
|
||||||
if (this.message.text) {
|
if (this.message.text) {
|
||||||
const ast = parse(this.message.text);
|
const ast = parse(this.message.text);
|
||||||
return ast
|
return unique(ast
|
||||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
.map(t => t.url);
|
.map(t => t.url));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { length } from 'stringz';
|
|||||||
import parse from '../../../../../mfm/parse';
|
import parse from '../../../../../mfm/parse';
|
||||||
import getAcct from '../../../../../misc/acct/render';
|
import getAcct from '../../../../../misc/acct/render';
|
||||||
import MkUrl from './url.vue';
|
import MkUrl from './url.vue';
|
||||||
import MkGoogle from './google.vue';
|
|
||||||
import { concat } from '../../../../../prelude/array';
|
import { concat } from '../../../../../prelude/array';
|
||||||
|
import MkFormula from './formula.vue';
|
||||||
|
import MkGoogle from './google.vue';
|
||||||
|
|
||||||
export default Vue.component('misskey-flavored-markdown', {
|
export default Vue.component('misskey-flavored-markdown', {
|
||||||
props: {
|
props: {
|
||||||
@@ -199,7 +200,17 @@ export default Vue.component('misskey-flavored-markdown', {
|
|||||||
})];
|
})];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'math': {
|
||||||
|
//const MkFormula = () => import('./formula.vue').then(m => m.default);
|
||||||
|
return [createElement(MkFormula, {
|
||||||
|
props: {
|
||||||
|
formula: token.formula
|
||||||
|
}
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
case 'search': {
|
case 'search': {
|
||||||
|
//const MkGoogle = () => import('./google.vue').then(m => m.default);
|
||||||
return [createElement(MkGoogle, {
|
return [createElement(MkGoogle, {
|
||||||
props: {
|
props: {
|
||||||
q: token.query
|
q: token.query
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
<mk-time :time="note.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<span class="visibility" v-if="note.visibility != 'public'">
|
<span class="visibility" v-if="note.visibility != 'public'">
|
||||||
<template v-if="note.visibility == 'home'"><fa icon="home"/></template>
|
<fa v-if="note.visibility == 'home'" icon="home"/>
|
||||||
<template v-if="note.visibility == 'followers'"><fa icon="unlock"/></template>
|
<fa v-if="note.visibility == 'followers'" icon="unlock"/>
|
||||||
<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
|
<fa v-if="note.visibility == 'specified'" icon="envelope"/>
|
||||||
<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
|
<fa v-if="note.visibility == 'private'" icon="lock"/>
|
||||||
</span>
|
</span>
|
||||||
|
<span class="localOnly" v-if="note.localOnly == true"><fa icon="heart"/></span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
@@ -115,4 +116,7 @@ export default Vue.extend({
|
|||||||
> .visibility
|
> .visibility
|
||||||
margin-left 8px
|
margin-left 8px
|
||||||
|
|
||||||
|
> .localOnly
|
||||||
|
margin-left 4px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -35,6 +35,24 @@
|
|||||||
<span>{{ $t('private') }}</span>
|
<span>{{ $t('private') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div @click="choose('local-public')" :class="{ active: v == 'local-public' }">
|
||||||
|
<div><fa icon="globe"/></div>
|
||||||
|
<div>
|
||||||
|
<span>{{ $t('local-public') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div @click="choose('local-home')" :class="{ active: v == 'local-home' }">
|
||||||
|
<div><fa icon="home"/></div>
|
||||||
|
<div>
|
||||||
|
<span>{{ $t('local-home') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div @click="choose('local-followers')" :class="{ active: v == 'local-followers' }">
|
||||||
|
<div><fa icon="unlock"/></div>
|
||||||
|
<div>
|
||||||
|
<span>{{ $t('local-followers') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainer.email" target="_blank">{{ meta.maintainer.name }}</a></b></p>
|
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainer.email" target="_blank">{{ meta.maintainer.name }}</a></b></p>
|
||||||
<p>Machine: {{ meta.machine }}</p>
|
<p>Machine: {{ meta.machine }}</p>
|
||||||
<p>Node: {{ meta.node }}</p>
|
<p>Node: {{ meta.node }}</p>
|
||||||
|
<p>Version: {{ meta.version }} </p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ import MkRenoteFormWindow from './renote-form-window.vue';
|
|||||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
import XSub from './note.sub.vue';
|
import XSub from './note.sub.vue';
|
||||||
import { sum } from '../../../../../prelude/array';
|
import { sum, unique } from '../../../../../prelude/array';
|
||||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
@@ -149,9 +149,9 @@ export default Vue.extend({
|
|||||||
urls(): string[] {
|
urls(): string[] {
|
||||||
if (this.p.text) {
|
if (this.p.text) {
|
||||||
const ast = parse(this.p.text);
|
const ast = parse(this.p.text);
|
||||||
return ast
|
return unique(ast
|
||||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
.map(t => t.url);
|
.map(t => t.url));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div
|
<div
|
||||||
class="note"
|
class="note"
|
||||||
:class="{ mini }"
|
:class="{ mini }"
|
||||||
v-show="appearNote.deletedAt == null && !hideThisNote"
|
v-show="(this.$store.state.settings.remainDeletedNote || appearNote.deletedAt == null) && !hideThisNote"
|
||||||
:tabindex="appearNote.deletedAt == null ? '-1' : null"
|
:tabindex="appearNote.deletedAt == null ? '-1' : null"
|
||||||
v-hotkey="keymap"
|
v-hotkey="keymap"
|
||||||
:title="title"
|
:title="title"
|
||||||
@@ -20,12 +20,19 @@
|
|||||||
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
|
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
|
||||||
<span>{{ this.$t('reposted-by').substr(this.$t('reposted-by').indexOf('}') + 1) }}</span>
|
<span>{{ this.$t('reposted-by').substr(this.$t('reposted-by').indexOf('}') + 1) }}</span>
|
||||||
<mk-time :time="note.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
|
<span class="visibility" v-if="note.visibility != 'public'">
|
||||||
|
<fa v-if="note.visibility == 'home'" icon="home"/>
|
||||||
|
<fa v-if="note.visibility == 'followers'" icon="unlock"/>
|
||||||
|
<fa v-if="note.visibility == 'specified'" icon="envelope"/>
|
||||||
|
<fa v-if="note.visibility == 'private'" icon="lock"/>
|
||||||
|
</span>
|
||||||
|
<span class="localOnly" v-if="note.localOnly == true"><fa icon="heart"/></span>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
<mk-avatar class="avatar" :user="appearNote.user"/>
|
<mk-avatar class="avatar" :user="appearNote.user"/>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<mk-note-header class="header" :note="appearNote" :mini="mini"/>
|
<mk-note-header class="header" :note="appearNote" :mini="mini"/>
|
||||||
<div class="body">
|
<div class="body" v-if="appearNote.deletedAt == null">
|
||||||
<p v-if="appearNote.cw != null" class="cw">
|
<p v-if="appearNote.cw != null" class="cw">
|
||||||
<span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
|
<span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
|
||||||
<mk-cw-button v-model="showContent"/>
|
<mk-cw-button v-model="showContent"/>
|
||||||
@@ -46,7 +53,7 @@
|
|||||||
<mk-url-preview v-for="url in urls" :url="url" :key="url" :mini="mini"/>
|
<mk-url-preview v-for="url in urls" :url="url" :key="url" :mini="mini"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer v-if="appearNote.deletedAt == null">
|
||||||
<span class="app" v-if="appearNote.app && mini && $store.state.settings.showVia">via <b>{{ appearNote.app.name }}</b></span>
|
<span class="app" v-if="appearNote.app && mini && $store.state.settings.showVia">via <b>{{ appearNote.app.name }}</b></span>
|
||||||
<mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
|
<mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
|
||||||
<button class="replyButton" @click="reply()" :title="$t('reply')">
|
<button class="replyButton" @click="reply()" :title="$t('reply')">
|
||||||
@@ -64,6 +71,7 @@
|
|||||||
<fa icon="ellipsis-h"/>
|
<fa icon="ellipsis-h"/>
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
|
<div class="deleted" v-if="appearNote.deletedAt != null">{{ $t('deleted') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<div class="replies" v-if="detail && replies.length > 0">
|
<div class="replies" v-if="detail && replies.length > 0">
|
||||||
@@ -82,6 +90,7 @@ import noteSubscriber from '../../../common/scripts/note-subscriber';
|
|||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('desktop/views/components/note.vue'),
|
i18n: i18n('desktop/views/components/note.vue'),
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
XSub
|
XSub
|
||||||
},
|
},
|
||||||
@@ -199,9 +208,6 @@ export default Vue.extend({
|
|||||||
> span
|
> span
|
||||||
flex-shrink 0
|
flex-shrink 0
|
||||||
|
|
||||||
&:last-of-type
|
|
||||||
margin-right 8px
|
|
||||||
|
|
||||||
.name
|
.name
|
||||||
overflow hidden
|
overflow hidden
|
||||||
flex-shrink 1
|
flex-shrink 1
|
||||||
@@ -215,6 +221,18 @@ export default Vue.extend({
|
|||||||
flex-shrink 0
|
flex-shrink 0
|
||||||
font-size 0.9em
|
font-size 0.9em
|
||||||
|
|
||||||
|
> .visibility
|
||||||
|
margin-left 8px
|
||||||
|
|
||||||
|
[data-icon]
|
||||||
|
margin-right 0
|
||||||
|
|
||||||
|
> .localOnly
|
||||||
|
margin-left 4px
|
||||||
|
|
||||||
|
[data-icon]
|
||||||
|
margin-right 0
|
||||||
|
|
||||||
& + article
|
& + article
|
||||||
padding-top 8px
|
padding-top 8px
|
||||||
|
|
||||||
@@ -327,6 +345,7 @@ export default Vue.extend({
|
|||||||
margin-left 0.5em
|
margin-left 0.5em
|
||||||
color var(--noteHeaderInfo)
|
color var(--noteHeaderInfo)
|
||||||
font-size 0.8em
|
font-size 0.8em
|
||||||
|
|
||||||
> button
|
> button
|
||||||
margin 0 28px 0 0
|
margin 0 28px 0 0
|
||||||
padding 0 8px
|
padding 0 8px
|
||||||
@@ -360,6 +379,10 @@ export default Vue.extend({
|
|||||||
&.reacted, &.reacted:hover
|
&.reacted, &.reacted:hover
|
||||||
color var(--noteActionsReactionHover)
|
color var(--noteActionsReactionHover)
|
||||||
|
|
||||||
|
> .deleted
|
||||||
|
color var(--noteText)
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="stylus" module>
|
<style lang="stylus" module>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<b>{{ $t('recent-tags') }}:</b>
|
<b>{{ $t('recent-tags') }}:</b>
|
||||||
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="local-only" v-if="this.localOnly == true">{{ $t('local-only-message') }}</div>
|
||||||
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
||||||
<div class="textarea">
|
<div class="textarea">
|
||||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||||
@@ -112,6 +113,7 @@ export default Vue.extend({
|
|||||||
geo: null,
|
geo: null,
|
||||||
visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
|
visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
|
||||||
visibleUsers: [],
|
visibleUsers: [],
|
||||||
|
localOnly: false,
|
||||||
autocomplete: null,
|
autocomplete: null,
|
||||||
draghover: false,
|
draghover: false,
|
||||||
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
||||||
@@ -363,7 +365,14 @@ export default Vue.extend({
|
|||||||
source: this.$refs.visibilityButton
|
source: this.$refs.visibilityButton
|
||||||
});
|
});
|
||||||
w.$once('chosen', v => {
|
w.$once('chosen', v => {
|
||||||
this.visibility = v;
|
const m = v.match(/^local-(.+)/);
|
||||||
|
if (m) {
|
||||||
|
this.localOnly = true;
|
||||||
|
this.visibility = m[1];
|
||||||
|
} else {
|
||||||
|
this.localOnly = false;
|
||||||
|
this.visibility = v;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -407,6 +416,7 @@ export default Vue.extend({
|
|||||||
cw: this.useCw ? this.cw || '' : undefined,
|
cw: this.useCw ? this.cw || '' : undefined,
|
||||||
visibility: this.visibility,
|
visibility: this.visibility,
|
||||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||||
|
localOnly: this.localOnly,
|
||||||
geo: this.geo ? {
|
geo: this.geo ? {
|
||||||
coordinates: [this.geo.longitude, this.geo.latitude],
|
coordinates: [this.geo.longitude, this.geo.latitude],
|
||||||
altitude: this.geo.altitude,
|
altitude: this.geo.altitude,
|
||||||
@@ -498,6 +508,7 @@ export default Vue.extend({
|
|||||||
border solid 1px var(--primaryAlpha01)
|
border solid 1px var(--primaryAlpha01)
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
transition border-color .2s ease
|
transition border-color .2s ease
|
||||||
|
padding-right 30px
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
border-color var(--primaryAlpha02)
|
border-color var(--primaryAlpha02)
|
||||||
@@ -640,6 +651,10 @@ export default Vue.extend({
|
|||||||
margin-right 8px
|
margin-right 8px
|
||||||
white-space nowrap
|
white-space nowrap
|
||||||
|
|
||||||
|
> .local-only
|
||||||
|
margin 0 0 8px 0
|
||||||
|
color var(--primary)
|
||||||
|
|
||||||
> .mk-uploader
|
> .mk-uploader
|
||||||
margin 8px 0 0 0
|
margin 8px 0 0 0
|
||||||
padding 8px
|
padding 8px
|
||||||
|
|||||||
@@ -130,6 +130,7 @@
|
|||||||
<ui-switch v-model="showReplyTarget">{{ $t('show-reply-target') }}</ui-switch>
|
<ui-switch v-model="showReplyTarget">{{ $t('show-reply-target') }}</ui-switch>
|
||||||
<ui-switch v-model="showMaps">{{ $t('show-maps') }}</ui-switch>
|
<ui-switch v-model="showMaps">{{ $t('show-maps') }}</ui-switch>
|
||||||
<ui-switch v-model="disableAnimatedMfm">{{ $t('@.disable-animated-mfm') }}</ui-switch>
|
<ui-switch v-model="disableAnimatedMfm">{{ $t('@.disable-animated-mfm') }}</ui-switch>
|
||||||
|
<ui-switch v-model="remainDeletedNote">{{ $t('remain-deleted-note') }}</ui-switch>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<header>{{ $t('deck-column-align') }}</header>
|
<header>{{ $t('deck-column-align') }}</header>
|
||||||
@@ -529,6 +530,11 @@ export default Vue.extend({
|
|||||||
disableAnimatedMfm: {
|
disableAnimatedMfm: {
|
||||||
get() { return this.$store.state.settings.disableAnimatedMfm; },
|
get() { return this.$store.state.settings.disableAnimatedMfm; },
|
||||||
set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
|
set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
|
||||||
|
},
|
||||||
|
|
||||||
|
remainDeletedNote: {
|
||||||
|
get() { return this.$store.state.settings.remainDeletedNote; },
|
||||||
|
set(value) { this.$store.dispatch('settings/set', { key: 'remainDeletedNote', value }); }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<x-column :menu="menu" :name="name" :column="column" :is-stacked="isStacked">
|
<x-column :menu="menu" :name="name" :column="column" :is-stacked="isStacked">
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
<template v-if="column.type == 'home'"><fa icon="home"/></template>
|
<fa v-if="column.type == 'home'" icon="home"/>
|
||||||
<template v-if="column.type == 'local'"><fa :icon="['far', 'comments']"/></template>
|
<fa v-if="column.type == 'local'" :icon="['far', 'comments']"/>
|
||||||
<template v-if="column.type == 'hybrid'"><fa icon="share-alt"/></template>
|
<fa v-if="column.type == 'hybrid'" icon="share-alt"/>
|
||||||
<template v-if="column.type == 'global'"><fa icon="globe"/></template>
|
<fa v-if="column.type == 'global'" icon="globe"/>
|
||||||
<template v-if="column.type == 'list'"><fa icon="list"/></template>
|
<fa v-if="column.type == 'list'" icon="list"/>
|
||||||
<template v-if="column.type == 'hashtag'"><fa icon="hashtag"/></template>
|
<fa v-if="column.type == 'hashtag'" icon="hashtag"/>
|
||||||
<span>{{ name }}</span>
|
<span>{{ name }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ export default define({
|
|||||||
background var(--desktopPostFormTextareaBg)
|
background var(--desktopPostFormTextareaBg)
|
||||||
border none
|
border none
|
||||||
border-bottom solid 1px var(--faceDivider)
|
border-bottom solid 1px var(--faceDivider)
|
||||||
|
padding-right 30px
|
||||||
|
|
||||||
&:focus
|
&:focus
|
||||||
& + .emoji
|
& + .emoji
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ import {
|
|||||||
faUserPlus,
|
faUserPlus,
|
||||||
faExternalLinkSquareAlt,
|
faExternalLinkSquareAlt,
|
||||||
faSync,
|
faSync,
|
||||||
|
faArrowLeft,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -138,6 +139,7 @@ import {
|
|||||||
faClock as farClock,
|
faClock as farClock,
|
||||||
faCalendarAlt as farCalendarAlt,
|
faCalendarAlt as farCalendarAlt,
|
||||||
faHdd as farHdd,
|
faHdd as farHdd,
|
||||||
|
faMoon as farMoon,
|
||||||
} from '@fortawesome/free-regular-svg-icons';
|
} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -240,6 +242,7 @@ library.add(
|
|||||||
faUserPlus,
|
faUserPlus,
|
||||||
faExternalLinkSquareAlt,
|
faExternalLinkSquareAlt,
|
||||||
faSync,
|
faSync,
|
||||||
|
faArrowLeft,
|
||||||
|
|
||||||
farBell,
|
farBell,
|
||||||
farEnvelope,
|
farEnvelope,
|
||||||
@@ -258,6 +261,7 @@ library.add(
|
|||||||
farClock,
|
farClock,
|
||||||
farCalendarAlt,
|
farCalendarAlt,
|
||||||
farHdd,
|
farHdd,
|
||||||
|
farMoon,
|
||||||
|
|
||||||
fabTwitter,
|
fabTwitter,
|
||||||
fabGithub,
|
fabGithub,
|
||||||
|
|||||||
@@ -385,15 +385,19 @@ export default class MiOS extends EventEmitter {
|
|||||||
* @param data パラメータ
|
* @param data パラメータ
|
||||||
*/
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
public api(endpoint: string, data: { [x: string]: any } = {}, forceFetch = false): Promise<{ [x: string]: any }> {
|
public api(endpoint: string, data: { [x: string]: any } = {}, forceFetch = false, silent = false): Promise<{ [x: string]: any }> {
|
||||||
if (++pending === 1) {
|
if (!silent) {
|
||||||
spinner = document.createElement('div');
|
if (++pending === 1) {
|
||||||
spinner.setAttribute('id', 'wait');
|
spinner = document.createElement('div');
|
||||||
document.body.appendChild(spinner);
|
spinner.setAttribute('id', 'wait');
|
||||||
|
document.body.appendChild(spinner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFinally = () => {
|
const onFinally = () => {
|
||||||
if (--pending === 0) spinner.parentNode.removeChild(spinner);
|
if (!silent) {
|
||||||
|
if (--pending === 0) spinner.parentNode.removeChild(spinner);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ import parse from '../../../../../mfm/parse';
|
|||||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
import XSub from './note.sub.vue';
|
import XSub from './note.sub.vue';
|
||||||
import { sum } from '../../../../../prelude/array';
|
import { sum, unique } from '../../../../../prelude/array';
|
||||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
@@ -143,9 +143,9 @@ export default Vue.extend({
|
|||||||
urls(): string[] {
|
urls(): string[] {
|
||||||
if (this.p.text) {
|
if (this.p.text) {
|
||||||
const ast = parse(this.p.text);
|
const ast = parse(this.p.text);
|
||||||
return ast
|
return unique(ast
|
||||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
.map(t => t.url);
|
.map(t => t.url));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,19 @@
|
|||||||
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
||||||
<span>{{ this.$t('reposted-by').substr(this.$t('reposted-by').indexOf('}') + 1) }}</span>
|
<span>{{ this.$t('reposted-by').substr(this.$t('reposted-by').indexOf('}') + 1) }}</span>
|
||||||
<mk-time :time="note.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
|
<span class="visibility" v-if="note.visibility != 'public'">
|
||||||
|
<fa v-if="note.visibility == 'home'" icon="home"/>
|
||||||
|
<fa v-if="note.visibility == 'followers'" icon="unlock"/>
|
||||||
|
<fa v-if="note.visibility == 'specified'" icon="envelope"/>
|
||||||
|
<fa v-if="note.visibility == 'private'" icon="lock"/>
|
||||||
|
</span>
|
||||||
|
<span class="localOnly" v-if="note.localOnly == true"><fa icon="heart"/></span>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
<mk-avatar class="avatar" :user="appearNote.user" v-if="$store.state.device.postStyle != 'smart'"/>
|
<mk-avatar class="avatar" :user="appearNote.user" v-if="$store.state.device.postStyle != 'smart'"/>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<mk-note-header class="header" :note="appearNote" :mini="true"/>
|
<mk-note-header class="header" :note="appearNote" :mini="true"/>
|
||||||
<div class="body">
|
<div class="body" v-if="appearNote.deletedAt == null">
|
||||||
<p v-if="appearNote.cw != null" class="cw">
|
<p v-if="appearNote.cw != null" class="cw">
|
||||||
<span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
|
<span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
|
||||||
<mk-cw-button v-model="showContent"/>
|
<mk-cw-button v-model="showContent"/>
|
||||||
@@ -30,7 +37,7 @@
|
|||||||
<div class="text">
|
<div class="text">
|
||||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
|
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
|
||||||
<a class="reply" v-if="appearNote.reply"><fa icon="reply"/></a>
|
<a class="reply" v-if="appearNote.reply"><fa icon="reply"/></a>
|
||||||
<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text" :customEmojis="appearNote.emojis"/>
|
<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text" :custom-emojis="appearNote.emojis"/>
|
||||||
<a class="rp" v-if="appearNote.renote != null">RN:</a>
|
<a class="rp" v-if="appearNote.renote != null">RN:</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="files" v-if="appearNote.files.length > 0">
|
<div class="files" v-if="appearNote.files.length > 0">
|
||||||
@@ -43,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="app" v-if="appearNote.app && $store.state.settings.showVia">via <b>{{ appearNote.app.name }}</b></span>
|
<span class="app" v-if="appearNote.app && $store.state.settings.showVia">via <b>{{ appearNote.app.name }}</b></span>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer v-if="appearNote.deletedAt == null">
|
||||||
<mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
|
<mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
|
||||||
<button @click="reply()">
|
<button @click="reply()">
|
||||||
<template v-if="appearNote.reply"><fa icon="reply-all"/></template>
|
<template v-if="appearNote.reply"><fa icon="reply-all"/></template>
|
||||||
@@ -60,6 +67,7 @@
|
|||||||
<fa icon="ellipsis-h"/>
|
<fa icon="ellipsis-h"/>
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
|
<div class="deleted" v-if="appearNote.deletedAt != null">{{ $t('deleted') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,9 +171,6 @@ export default Vue.extend({
|
|||||||
> span
|
> span
|
||||||
flex-shrink 0
|
flex-shrink 0
|
||||||
|
|
||||||
&:last-of-type
|
|
||||||
margin-right 8px
|
|
||||||
|
|
||||||
.name
|
.name
|
||||||
overflow hidden
|
overflow hidden
|
||||||
flex-shrink 1
|
flex-shrink 1
|
||||||
@@ -179,6 +184,18 @@ export default Vue.extend({
|
|||||||
flex-shrink 0
|
flex-shrink 0
|
||||||
font-size 0.9em
|
font-size 0.9em
|
||||||
|
|
||||||
|
> .visibility
|
||||||
|
margin-left 8px
|
||||||
|
|
||||||
|
[data-icon]
|
||||||
|
margin-right 0
|
||||||
|
|
||||||
|
> .localOnly
|
||||||
|
margin-left 4px
|
||||||
|
|
||||||
|
[data-icon]
|
||||||
|
margin-right 0
|
||||||
|
|
||||||
& + article
|
& + article
|
||||||
padding-top 8px
|
padding-top 8px
|
||||||
|
|
||||||
@@ -339,6 +356,10 @@ export default Vue.extend({
|
|||||||
&.reacted
|
&.reacted
|
||||||
color var(--primary)
|
color var(--primary)
|
||||||
|
|
||||||
|
> .deleted
|
||||||
|
color var(--noteText)
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="stylus" module>
|
<style lang="stylus" module>
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export default Vue.extend({
|
|||||||
geo: null,
|
geo: null,
|
||||||
visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
|
visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
|
||||||
visibleUsers: [],
|
visibleUsers: [],
|
||||||
|
localOnly: false,
|
||||||
useCw: false,
|
useCw: false,
|
||||||
cw: null,
|
cw: null,
|
||||||
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
||||||
@@ -274,7 +275,14 @@ export default Vue.extend({
|
|||||||
compact: true
|
compact: true
|
||||||
});
|
});
|
||||||
w.$once('chosen', v => {
|
w.$once('chosen', v => {
|
||||||
this.visibility = v;
|
const m = v.match(/^local-(.+)/);
|
||||||
|
if (m) {
|
||||||
|
this.localOnly = true;
|
||||||
|
this.visibility = m[1];
|
||||||
|
} else {
|
||||||
|
this.localOnly = false;
|
||||||
|
this.visibility = v;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -320,6 +328,7 @@ export default Vue.extend({
|
|||||||
} : null,
|
} : null,
|
||||||
visibility: this.visibility,
|
visibility: this.visibility,
|
||||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||||
|
localOnly: this.localOnly,
|
||||||
viaMobile: viaMobile
|
viaMobile: viaMobile
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.$emit('posted');
|
this.$emit('posted');
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const defaultSettings = {
|
|||||||
tagTimelines: [],
|
tagTimelines: [],
|
||||||
fetchOnScroll: true,
|
fetchOnScroll: true,
|
||||||
showMaps: true,
|
showMaps: true,
|
||||||
|
remainDeletedNote: false,
|
||||||
showPostFormOnTopOfTl: false,
|
showPostFormOnTopOfTl: false,
|
||||||
suggestRecentHashtags: true,
|
suggestRecentHashtags: true,
|
||||||
showClockOnHeader: true,
|
showClockOnHeader: true,
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ props:
|
|||||||
ja-JP: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
ja-JP: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
||||||
en-US: "Whether this note sent via a mobile device"
|
en-US: "Whether this note sent via a mobile device"
|
||||||
|
|
||||||
|
localOnly:
|
||||||
|
type: "boolean"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja-JP: "ローカルのみに公開する投稿か否か"
|
||||||
|
en-US: "Whether this note is no federation"
|
||||||
|
|
||||||
text:
|
text:
|
||||||
type: "string"
|
type: "string"
|
||||||
optional: true
|
optional: true
|
||||||
|
|||||||
12
src/index.ts
12
src/index.ts
@@ -112,7 +112,12 @@ async function init(): Promise<Config> {
|
|||||||
Logger.info('Welcome to Misskey!');
|
Logger.info('Welcome to Misskey!');
|
||||||
Logger.info(`<<< Misskey v${pkg.version} >>>`);
|
Logger.info(`<<< Misskey v${pkg.version} >>>`);
|
||||||
|
|
||||||
new Logger('Deps').info(`Node.js ${process.version}`);
|
new Logger('Nodejs').info(`Version ${process.version}`);
|
||||||
|
if (lessThan(process.version.slice(1).split('.').map(x => parseInt(x, 10)), [10, 0, 0])) {
|
||||||
|
new Logger('Nodejs').error(`Node.js version is less than 10.0.0. Please upgrade it.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
await MachineInfo.show();
|
await MachineInfo.show();
|
||||||
EnvironmentInfo.show();
|
EnvironmentInfo.show();
|
||||||
|
|
||||||
@@ -135,6 +140,11 @@ async function init(): Promise<Config> {
|
|||||||
|
|
||||||
configLogger.succ('Loaded');
|
configLogger.succ('Loaded');
|
||||||
|
|
||||||
|
if (config.port == null) {
|
||||||
|
Logger.error('The port is not configured. Please configure port.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (process.platform === 'linux' && !isRoot() && config.port < 1024) {
|
if (process.platform === 'linux' && !isRoot() && config.port < 1024) {
|
||||||
Logger.error('You need root privileges to listen on port below 1024 on Linux');
|
Logger.error('You need root privileges to listen on port below 1024 on Linux');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ const handlers: { [key: string]: (window: any, token: any, mentionedRemoteUsers:
|
|||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
math({ document }, { formula }) {
|
||||||
|
const element = document.createElement('code');
|
||||||
|
element.textContent = formula;
|
||||||
|
document.body.appendChild(element);
|
||||||
|
},
|
||||||
|
|
||||||
link({ document }, { url, title }) {
|
link({ document }, { url, title }) {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export type TextElementHashtag = {
|
|||||||
hashtag: string;
|
hashtag: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string, isBegin: boolean) {
|
export default function(text: string, before: string) {
|
||||||
|
const isBegin = before == '';
|
||||||
|
|
||||||
if (!(/^\s#[^\s\.,!\?#]+/.test(text) || (isBegin && /^#[^\s\.,!\?#]+/.test(text)))) return null;
|
if (!(/^\s#[^\s\.,!\?#]+/.test(text) || (isBegin && /^#[^\s\.,!\?#]+/.test(text)))) return null;
|
||||||
const isHead = text.startsWith('#');
|
const isHead = text.startsWith('#');
|
||||||
const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
|
const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
|
||||||
|
|||||||
20
src/mfm/parse/elements/math.ts
Normal file
20
src/mfm/parse/elements/math.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Math
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type TextElementMath = {
|
||||||
|
type: 'math';
|
||||||
|
content: string;
|
||||||
|
formula: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function(text: string) {
|
||||||
|
const match = text.match(/^\$(.+?)\$/);
|
||||||
|
if (!match) return null;
|
||||||
|
const math = match[0];
|
||||||
|
return {
|
||||||
|
type: 'math',
|
||||||
|
content: math,
|
||||||
|
formula: match[1]
|
||||||
|
} as TextElementMath;
|
||||||
|
}
|
||||||
@@ -12,9 +12,10 @@ export type TextElementMention = {
|
|||||||
host: string;
|
host: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string) {
|
export default function(text: string, before: string) {
|
||||||
const match = text.match(/^@[a-z0-9_]+(?:@[a-z0-9\.\-]+[a-z0-9])?/i);
|
const match = text.match(/^@[a-z0-9_]+(?:@[a-z0-9\.\-]+[a-z0-9])?/i);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
|
if (/[a-zA-Z0-9]$/.test(before)) return null;
|
||||||
const mention = match[0];
|
const mention = match[0];
|
||||||
const { username, host } = parseAcct(mention.substr(1));
|
const { username, host } = parseAcct(mention.substr(1));
|
||||||
const canonical = host != null ? `@${username}@${toUnicode(host)}` : mention;
|
const canonical = host != null ? `@${username}@${toUnicode(host)}` : mention;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export type TextElementQuote = {
|
|||||||
quote: string;
|
quote: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string, isBegin: boolean) {
|
export default function(text: string, before: string) {
|
||||||
|
const isBegin = before == '';
|
||||||
|
|
||||||
const match = text.match(/^"([\s\S]+?)\n"/) || text.match(/^\n>([\s\S]+?)(\n\n|$)/) ||
|
const match = text.match(/^"([\s\S]+?)\n"/) || text.match(/^\n>([\s\S]+?)(\n\n|$)/) ||
|
||||||
(isBegin ? text.match(/^>([\s\S]+?)(\n\n|$)/) : null);
|
(isBegin ? text.match(/^>([\s\S]+?)(\n\n|$)/) : null);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export type TextElementTitle = {
|
|||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string, isBegin: boolean) {
|
export default function(text: string, before: string) {
|
||||||
|
const isBegin = before == '';
|
||||||
|
|
||||||
const match = isBegin ? text.match(/^(【|\[)(.+?)(】|])\n/) : text.match(/^\n(【|\[)(.+?)(】|])\n/);
|
const match = isBegin ? text.match(/^(【|\[)(.+?)(】|])\n/) : text.match(/^\n(【|\[)(.+?)(】|])\n/);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ export type TextElementUrl = {
|
|||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string) {
|
export default function(text: string, before: string) {
|
||||||
const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+/);
|
const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
const url = match[0];
|
let url = match[0];
|
||||||
|
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||||
|
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||||
|
if (url.endsWith(')') && before.endsWith('(')) url = url.substr(0, url.lastIndexOf(')'));
|
||||||
return {
|
return {
|
||||||
type: 'url',
|
type: 'url',
|
||||||
content: url,
|
content: url,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { TextElementCode } from './elements/code';
|
|||||||
import { TextElementEmoji } from './elements/emoji';
|
import { TextElementEmoji } from './elements/emoji';
|
||||||
import { TextElementHashtag } from './elements/hashtag';
|
import { TextElementHashtag } from './elements/hashtag';
|
||||||
import { TextElementInlineCode } from './elements/inline-code';
|
import { TextElementInlineCode } from './elements/inline-code';
|
||||||
|
import { TextElementMath } from './elements/math';
|
||||||
import { TextElementLink } from './elements/link';
|
import { TextElementLink } from './elements/link';
|
||||||
import { TextElementMention } from './elements/mention';
|
import { TextElementMention } from './elements/mention';
|
||||||
import { TextElementQuote } from './elements/quote';
|
import { TextElementQuote } from './elements/quote';
|
||||||
@@ -29,6 +30,7 @@ const elements = [
|
|||||||
require('./elements/hashtag'),
|
require('./elements/hashtag'),
|
||||||
require('./elements/code'),
|
require('./elements/code'),
|
||||||
require('./elements/inline-code'),
|
require('./elements/inline-code'),
|
||||||
|
require('./elements/math'),
|
||||||
require('./elements/quote'),
|
require('./elements/quote'),
|
||||||
require('./elements/emoji'),
|
require('./elements/emoji'),
|
||||||
require('./elements/search'),
|
require('./elements/search'),
|
||||||
@@ -42,6 +44,7 @@ export type TextElement = { type: 'text', content: string }
|
|||||||
| TextElementEmoji
|
| TextElementEmoji
|
||||||
| TextElementHashtag
|
| TextElementHashtag
|
||||||
| TextElementInlineCode
|
| TextElementInlineCode
|
||||||
|
| TextElementMath
|
||||||
| TextElementLink
|
| TextElementLink
|
||||||
| TextElementMention
|
| TextElementMention
|
||||||
| TextElementQuote
|
| TextElementQuote
|
||||||
@@ -49,7 +52,7 @@ export type TextElement = { type: 'text', content: string }
|
|||||||
| TextElementTitle
|
| TextElementTitle
|
||||||
| TextElementUrl
|
| TextElementUrl
|
||||||
| TextElementMotion;
|
| TextElementMotion;
|
||||||
export type TextElementProcessor = (text: string, isBegin: boolean) => TextElement | TextElement[];
|
export type TextElementProcessor = (text: string, before: string) => TextElement | TextElement[];
|
||||||
|
|
||||||
export default (source: string): TextElement[] => {
|
export default (source: string): TextElement[] => {
|
||||||
if (source == null || source == '') {
|
if (source == null || source == '') {
|
||||||
@@ -65,12 +68,10 @@ export default (source: string): TextElement[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
// パース
|
// パース
|
||||||
while (source != '') {
|
while (source != '') {
|
||||||
const parsed = elements.some(el => {
|
const parsed = elements.some(el => {
|
||||||
let _tokens = el(source, i == 0);
|
let _tokens = el(source, tokens.map(token => token.content).join(''));
|
||||||
if (_tokens) {
|
if (_tokens) {
|
||||||
if (!Array.isArray(_tokens)) {
|
if (!Array.isArray(_tokens)) {
|
||||||
_tokens = [_tokens];
|
_tokens = [_tokens];
|
||||||
@@ -88,8 +89,6 @@ export default (source: string): TextElement[] => {
|
|||||||
content: source[0]
|
content: source[0]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const combineText = (es: TextElement[]): TextElement =>
|
const combineText = (es: TextElement[]): TextElement =>
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export type INote = {
|
|||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
appId: mongo.ObjectID;
|
appId: mongo.ObjectID;
|
||||||
viaMobile: boolean;
|
viaMobile: boolean;
|
||||||
|
localOnly: boolean;
|
||||||
renoteCount: number;
|
renoteCount: number;
|
||||||
repliesCount: number;
|
repliesCount: number;
|
||||||
reactionCounts: any;
|
reactionCounts: any;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export function createHttpJob(data: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function deliver(user: ILocalUser, content: any, to: any) {
|
export function deliver(user: ILocalUser, content: any, to: any) {
|
||||||
|
if (content == null) return;
|
||||||
|
|
||||||
createHttpJob({
|
createHttpJob({
|
||||||
type: 'deliver',
|
type: 'deliver',
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
|
|||||||
cw: note.summary,
|
cw: note.summary,
|
||||||
text: text,
|
text: text,
|
||||||
viaMobile: false,
|
viaMobile: false,
|
||||||
|
localOnly: false,
|
||||||
geo: undefined,
|
geo: undefined,
|
||||||
visibility,
|
visibility,
|
||||||
visibleUsers,
|
visibleUsers,
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ router.get('/notes/:note', async (ctx, next) => {
|
|||||||
|
|
||||||
const note = await Note.findOne({
|
const note = await Note.findOne({
|
||||||
_id: new mongo.ObjectID(ctx.params.note),
|
_id: new mongo.ObjectID(ctx.params.note),
|
||||||
visibility: { $in: ['public', 'home'] }
|
visibility: { $in: ['public', 'home'] },
|
||||||
|
localOnly: { $ne: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (note === null) {
|
if (note === null) {
|
||||||
@@ -83,7 +84,8 @@ router.get('/notes/:note', async (ctx, next) => {
|
|||||||
router.get('/notes/:note/activity', async ctx => {
|
router.get('/notes/:note/activity', async ctx => {
|
||||||
const note = await Note.findOne({
|
const note = await Note.findOne({
|
||||||
_id: new mongo.ObjectID(ctx.params.note),
|
_id: new mongo.ObjectID(ctx.params.note),
|
||||||
visibility: { $in: ['public', 'home'] }
|
visibility: { $in: ['public', 'home'] },
|
||||||
|
localOnly: { $ne: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (note === null) {
|
if (note === null) {
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ export default async (ctx: Router.IRouterContext) => {
|
|||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
visibility: { $in: ['public', 'home'] }
|
visibility: { $in: ['public', 'home'] },
|
||||||
|
localOnly: { $ne: true }
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
if (sinceId) {
|
if (sinceId) {
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ export const meta = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
localOnly: {
|
||||||
|
validator: $.bool.optional,
|
||||||
|
default: false,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'ローカルのみに投稿か否か。'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
geo: {
|
geo: {
|
||||||
validator: $.obj({
|
validator: $.obj({
|
||||||
coordinates: $.arr().length(2)
|
coordinates: $.arr().length(2)
|
||||||
@@ -226,6 +234,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
|
|||||||
cw: ps.cw,
|
cw: ps.cw,
|
||||||
app,
|
app,
|
||||||
viaMobile: ps.viaMobile,
|
viaMobile: ps.viaMobile,
|
||||||
|
localOnly: ps.localOnly,
|
||||||
visibility: ps.visibility,
|
visibility: ps.visibility,
|
||||||
visibleUsers,
|
visibleUsers,
|
||||||
geo: ps.geo
|
geo: ps.geo
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ handler.on('status', event => {
|
|||||||
const parentState = parentStatuses[0].state;
|
const parentState = parentStatuses[0].state;
|
||||||
const stillFailed = parentState == 'failure' || parentState == 'error';
|
const stillFailed = parentState == 'failure' || parentState == 'error';
|
||||||
if (stillFailed) {
|
if (stillFailed) {
|
||||||
post(`**⚠️BUILD STILL FAILED⚠️**: ?[${commit.commit.message}](${commit.html_url})`);
|
post(`⚠️**BUILD STILL FAILED**⚠️: ?[${commit.commit.message}](${commit.html_url})`);
|
||||||
} else {
|
} else {
|
||||||
post(`**🚨BUILD FAILED🚨**: →→→?[${commit.commit.message}](${commit.html_url})←←←`);
|
post(`🚨**BUILD FAILED**🚨: →→→?[${commit.commit.message}](${commit.html_url})←←←`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -87,7 +87,7 @@ handler.on('status', event => {
|
|||||||
handler.on('push', event => {
|
handler.on('push', event => {
|
||||||
const ref = event.ref;
|
const ref = event.ref;
|
||||||
switch (ref) {
|
switch (ref) {
|
||||||
case 'refs/heads/master':
|
case 'refs/heads/develop':
|
||||||
const pusher = event.pusher;
|
const pusher = event.pusher;
|
||||||
const compare = event.compare;
|
const compare = event.compare;
|
||||||
const commits: any[] = event.commits;
|
const commits: any[] = event.commits;
|
||||||
@@ -96,10 +96,6 @@ handler.on('push', event => {
|
|||||||
commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'),
|
commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'),
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
break;
|
break;
|
||||||
case 'refs/heads/release':
|
|
||||||
const commit = event.commits[0];
|
|
||||||
post(`RELEASED: ${commit.message}`);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -128,6 +124,17 @@ handler.on('issue_comment', event => {
|
|||||||
post(text);
|
post(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handler.on('release', event => {
|
||||||
|
const action = event.action;
|
||||||
|
const release = event.release;
|
||||||
|
let text: string;
|
||||||
|
switch (action) {
|
||||||
|
case 'published': text = `🎁 **NEW RELEASE**: [${release.tag_name}](${release.html_url}) is out now. Enjoy!`; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
post(text);
|
||||||
|
});
|
||||||
|
|
||||||
handler.on('watch', event => {
|
handler.on('watch', event => {
|
||||||
const sender = event.sender;
|
const sender = event.sender;
|
||||||
post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false);
|
post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false);
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ type Option = {
|
|||||||
geo?: any;
|
geo?: any;
|
||||||
poll?: any;
|
poll?: any;
|
||||||
viaMobile?: boolean;
|
viaMobile?: boolean;
|
||||||
|
localOnly?: boolean;
|
||||||
cw?: string;
|
cw?: string;
|
||||||
visibility?: string;
|
visibility?: string;
|
||||||
visibleUsers?: IUser[];
|
visibleUsers?: IUser[];
|
||||||
@@ -109,6 +110,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||||||
if (data.createdAt == null) data.createdAt = new Date();
|
if (data.createdAt == null) data.createdAt = new Date();
|
||||||
if (data.visibility == null) data.visibility = 'public';
|
if (data.visibility == null) data.visibility = 'public';
|
||||||
if (data.viaMobile == null) data.viaMobile = false;
|
if (data.viaMobile == null) data.viaMobile = false;
|
||||||
|
if (data.localOnly == null) data.localOnly = false;
|
||||||
|
|
||||||
if (data.visibleUsers) {
|
if (data.visibleUsers) {
|
||||||
data.visibleUsers = erase(null, data.visibleUsers);
|
data.visibleUsers = erase(null, data.visibleUsers);
|
||||||
@@ -139,6 +141,16 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||||||
return rej('Renote target is private of others');
|
return rej('Renote target is private of others');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ローカルのみをRenoteしたらローカルのみにする
|
||||||
|
if (data.renote && data.renote.localOnly) {
|
||||||
|
data.localOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ローカルのみにリプライしたらローカルのみにする
|
||||||
|
if (data.reply && data.reply.localOnly) {
|
||||||
|
data.localOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.text) {
|
if (data.text) {
|
||||||
data.text = data.text.trim();
|
data.text = data.text.trim();
|
||||||
}
|
}
|
||||||
@@ -308,6 +320,8 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function renderActivity(data: Option, note: INote) {
|
async function renderActivity(data: Option, note: INote) {
|
||||||
|
if (data.localOnly) return null;
|
||||||
|
|
||||||
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length == 0)
|
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length == 0)
|
||||||
? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote._id}`, note)
|
? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote._id}`, note)
|
||||||
: renderCreate(await renderNote(note, false), note);
|
: renderCreate(await renderNote(note, false), note);
|
||||||
@@ -389,6 +403,7 @@ async function insertNote(user: IUser, data: Option, tags: string[], emojis: str
|
|||||||
emojis,
|
emojis,
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
viaMobile: data.viaMobile,
|
viaMobile: data.viaMobile,
|
||||||
|
localOnly: data.localOnly,
|
||||||
geo: data.geo || null,
|
geo: data.geo || null,
|
||||||
appId: data.app ? data.app._id : null,
|
appId: data.app ? data.app._id : null,
|
||||||
visibility: data.visibility,
|
visibility: data.visibility,
|
||||||
|
|||||||
89
test/mfm.ts
89
test/mfm.ts
@@ -82,7 +82,7 @@ describe('Text', () => {
|
|||||||
{ type: 'text', content: ' お腹ペコい' }
|
{ type: 'text', content: ' お腹ペコい' }
|
||||||
], tokens);
|
], tokens);
|
||||||
});
|
});
|
||||||
/*
|
|
||||||
it('ignore', () => {
|
it('ignore', () => {
|
||||||
const tokens = analyze('idolm@ster');
|
const tokens = analyze('idolm@ster');
|
||||||
assert.deepEqual([
|
assert.deepEqual([
|
||||||
@@ -91,20 +91,19 @@ describe('Text', () => {
|
|||||||
|
|
||||||
const tokens2 = analyze('@a\n@b\n@c');
|
const tokens2 = analyze('@a\n@b\n@c');
|
||||||
assert.deepEqual([
|
assert.deepEqual([
|
||||||
{ type: 'mention', content: '@a', username: 'a', host: null },
|
{ type: 'mention', content: '@a', canonical: '@a', username: 'a', host: null },
|
||||||
{ type: 'text', content: '\n' },
|
{ type: 'text', content: '\n' },
|
||||||
{ type: 'mention', content: '@b', username: 'b', host: null },
|
{ type: 'mention', content: '@b', canonical: '@b', username: 'b', host: null },
|
||||||
{ type: 'text', content: '\n' },
|
{ type: 'text', content: '\n' },
|
||||||
{ type: 'mention', content: '@c', username: 'c', host: null }
|
{ type: 'mention', content: '@c', canonical: '@c', username: 'c', host: null }
|
||||||
], tokens2);
|
], tokens2);
|
||||||
|
|
||||||
const tokens3 = analyze('**x**@a');
|
const tokens3 = analyze('**x**@a');
|
||||||
assert.deepEqual([
|
assert.deepEqual([
|
||||||
{ type: 'bold', content: '**x**', bold: 'x' },
|
{ type: 'bold', content: '**x**', bold: 'x' },
|
||||||
{ type: 'mention', content: '@a', username: 'a', host: null }
|
{ type: 'mention', content: '@a', canonical: '@a', username: 'a', host: null }
|
||||||
], tokens3);
|
], tokens3);
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hashtag', () => {
|
it('hashtag', () => {
|
||||||
@@ -159,13 +158,68 @@ describe('Text', () => {
|
|||||||
], tokens5);
|
], tokens5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('url', () => {
|
describe('url', () => {
|
||||||
const tokens = analyze('https://himasaku.net');
|
it('simple', () => {
|
||||||
assert.deepEqual([{
|
const tokens = analyze('https://example.com');
|
||||||
type: 'url',
|
assert.deepEqual([{
|
||||||
content: 'https://himasaku.net',
|
type: 'url',
|
||||||
url: 'https://himasaku.net'
|
content: 'https://example.com',
|
||||||
}], tokens);
|
url: 'https://example.com'
|
||||||
|
}], tokens);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignore trailing dot', () => {
|
||||||
|
const tokens = analyze('https://example.com.');
|
||||||
|
assert.deepEqual([{
|
||||||
|
type: 'url',
|
||||||
|
content: 'https://example.com',
|
||||||
|
url: 'https://example.com'
|
||||||
|
}, {
|
||||||
|
type: 'text', content: '.'
|
||||||
|
}], tokens);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with comma', () => {
|
||||||
|
const tokens = analyze('https://example.com/foo?bar=a,b');
|
||||||
|
assert.deepEqual([{
|
||||||
|
type: 'url',
|
||||||
|
content: 'https://example.com/foo?bar=a,b',
|
||||||
|
url: 'https://example.com/foo?bar=a,b'
|
||||||
|
}], tokens);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignore trailing comma', () => {
|
||||||
|
const tokens = analyze('https://example.com/foo, bar');
|
||||||
|
assert.deepEqual([{
|
||||||
|
type: 'url',
|
||||||
|
content: 'https://example.com/foo',
|
||||||
|
url: 'https://example.com/foo'
|
||||||
|
}, {
|
||||||
|
type: 'text', content: ', bar'
|
||||||
|
}], tokens);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with brackets', () => {
|
||||||
|
const tokens = analyze('https://example.com/foo(bar)');
|
||||||
|
assert.deepEqual([{
|
||||||
|
type: 'url',
|
||||||
|
content: 'https://example.com/foo(bar)',
|
||||||
|
url: 'https://example.com/foo(bar)'
|
||||||
|
}], tokens);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignore parent brackets', () => {
|
||||||
|
const tokens = analyze('(https://example.com/foo)');
|
||||||
|
assert.deepEqual([{
|
||||||
|
type: 'text', content: '('
|
||||||
|
}, {
|
||||||
|
type: 'url',
|
||||||
|
content: 'https://example.com/foo',
|
||||||
|
url: 'https://example.com/foo'
|
||||||
|
}, {
|
||||||
|
type: 'text', content: ')'
|
||||||
|
}], tokens);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('link', () => {
|
it('link', () => {
|
||||||
@@ -210,6 +264,15 @@ describe('Text', () => {
|
|||||||
assert.equal(tokens[0].content, '`var x = "Strawberry Pasta";`');
|
assert.equal(tokens[0].content, '`var x = "Strawberry Pasta";`');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('math', () => {
|
||||||
|
const fomula = 'x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.';
|
||||||
|
const text = `$${fomula}$`;
|
||||||
|
const tokens = analyze(text);
|
||||||
|
assert.deepEqual([
|
||||||
|
{ type: 'math', content: text, formula: fomula }
|
||||||
|
], tokens);
|
||||||
|
});
|
||||||
|
|
||||||
it('search', () => {
|
it('search', () => {
|
||||||
const tokens1 = analyze('a b c 検索');
|
const tokens1 = analyze('a b c 検索');
|
||||||
assert.deepEqual([
|
assert.deepEqual([
|
||||||
|
|||||||
Reference in New Issue
Block a user