Compare commits
	
		
			261 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					30608d3e22 | ||
| 
						 | 
					8bf4e55338 | ||
| 
						 | 
					6ead1de383 | ||
| 
						 | 
					3b628ec3c4 | ||
| 
						 | 
					0ed704d173 | ||
| 
						 | 
					87b6ef0ec5 | ||
| 
						 | 
					5184a07cf2 | ||
| 
						 | 
					dba04cc59c | ||
| 
						 | 
					f4045fb5b3 | ||
| 
						 | 
					16c36163b4 | ||
| 
						 | 
					1ac033ff18 | ||
| 
						 | 
					ccfd48232a | ||
| 
						 | 
					429bf179dc | ||
| 
						 | 
					8ba3fb13eb | ||
| 
						 | 
					11496d887e | ||
| 
						 | 
					bec48319ec | ||
| 
						 | 
					71a93b2b43 | ||
| 
						 | 
					6ed3f9e414 | ||
| 
						 | 
					dc8f592c1f | ||
| 
						 | 
					f66c31c771 | ||
| 
						 | 
					55e2ae1408 | ||
| 
						 | 
					19c72627fc | ||
| 
						 | 
					2a4c53c3a4 | ||
| 
						 | 
					1f2ebce8ed | ||
| 
						 | 
					fcea9dacb7 | ||
| 
						 | 
					908872f374 | ||
| 
						 | 
					f688ceafb8 | ||
| 
						 | 
					b47b5d6d8b | ||
| 
						 | 
					31ce3aa312 | ||
| 
						 | 
					5b22d92e99 | ||
| 
						 | 
					df148e25da | ||
| 
						 | 
					4b26df5c3a | ||
| 
						 | 
					e765be4205 | ||
| 
						 | 
					f7d2457063 | ||
| 
						 | 
					6032d803aa | ||
| 
						 | 
					0de371db38 | ||
| 
						 | 
					ce3797c4af | ||
| 
						 | 
					56dd8c298b | ||
| 
						 | 
					3533257efe | ||
| 
						 | 
					dc2f08721d | ||
| 
						 | 
					66608a4131 | ||
| 
						 | 
					2fa90131eb | ||
| 
						 | 
					a51ed28db6 | ||
| 
						 | 
					5ec290663b | ||
| 
						 | 
					1374d6e34d | ||
| 
						 | 
					45ade17c58 | ||
| 
						 | 
					c753e26187 | ||
| 
						 | 
					577929eed1 | ||
| 
						 | 
					1fde8a8fb0 | ||
| 
						 | 
					77e53cbf9e | ||
| 
						 | 
					ab83e08bc7 | ||
| 
						 | 
					2fad6e6d5f | ||
| 
						 | 
					a3604a6c95 | ||
| 
						 | 
					44f6fe6f1f | ||
| 
						 | 
					311b4e90ca | ||
| 
						 | 
					f5a937c523 | ||
| 
						 | 
					0632a3ed3f | ||
| 
						 | 
					71bada97df | ||
| 
						 | 
					62509edcbe | ||
| 
						 | 
					f97cdfaa20 | ||
| 
						 | 
					67ec10e86d | ||
| 
						 | 
					481b3f2c58 | ||
| 
						 | 
					7d599a68ea | ||
| 
						 | 
					7ccff732b8 | ||
| 
						 | 
					7587c896d5 | ||
| 
						 | 
					91297f1ab3 | ||
| 
						 | 
					d872a16fe0 | ||
| 
						 | 
					60aa35adf8 | ||
| 
						 | 
					5035b66773 | ||
| 
						 | 
					fa9da8ecab | ||
| 
						 | 
					1f9bca7188 | ||
| 
						 | 
					ffa5bdeb50 | ||
| 
						 | 
					e6bfb7398e | ||
| 
						 | 
					6def0c776f | ||
| 
						 | 
					24bae9eaed | ||
| 
						 | 
					fb5175a283 | ||
| 
						 | 
					6e49437154 | ||
| 
						 | 
					2511ed56ac | ||
| 
						 | 
					c4bfc99cf5 | ||
| 
						 | 
					4efe38440d | ||
| 
						 | 
					4a5f2c3c40 | ||
| 
						 | 
					109738ccb9 | ||
| 
						 | 
					433dbe179d | ||
| 
						 | 
					b21b33831a | ||
| 
						 | 
					020cc471da | ||
| 
						 | 
					43b47c4494 | ||
| 
						 | 
					8751d91794 | ||
| 
						 | 
					374b276f5c | ||
| 
						 | 
					6138a74231 | ||
| 
						 | 
					25438c4d64 | ||
| 
						 | 
					ae6ce19886 | ||
| 
						 | 
					e17a9bfd6f | ||
| 
						 | 
					dc2055f5bc | ||
| 
						 | 
					afeb8058b1 | ||
| 
						 | 
					9299f99ac3 | ||
| 
						 | 
					858fc7ebcc | ||
| 
						 | 
					35089c65d3 | ||
| 
						 | 
					643ca42829 | ||
| 
						 | 
					935dc4fe33 | ||
| 
						 | 
					3a9e74feb1 | ||
| 
						 | 
					92e66fbf0c | ||
| 
						 | 
					a50515f569 | ||
| 
						 | 
					2f8f47acea | ||
| 
						 | 
					dcb296db93 | ||
| 
						 | 
					0bdae9ede7 | ||
| 
						 | 
					11290c2a0f | ||
| 
						 | 
					428b8f8669 | ||
| 
						 | 
					7ced10f84e | ||
| 
						 | 
					8ac54139c9 | ||
| 
						 | 
					32afe77a26 | ||
| 
						 | 
					6db8e33662 | ||
| 
						 | 
					569561f247 | ||
| 
						 | 
					d132d82acf | ||
| 
						 | 
					9ba0db9372 | ||
| 
						 | 
					5d468b542d | ||
| 
						 | 
					32273165c7 | ||
| 
						 | 
					46fdb75bf4 | ||
| 
						 | 
					baf381814b | ||
| 
						 | 
					e90387c14e | ||
| 
						 | 
					876790d499 | ||
| 
						 | 
					8b56edda4b | ||
| 
						 | 
					33352256d6 | ||
| 
						 | 
					e368ef11fa | ||
| 
						 | 
					045f7c3185 | ||
| 
						 | 
					bf40e5a5c5 | ||
| 
						 | 
					cda3635d97 | ||
| 
						 | 
					2eb561f132 | ||
| 
						 | 
					b5f6465d61 | ||
| 
						 | 
					9725076c46 | ||
| 
						 | 
					f7228e79bb | ||
| 
						 | 
					d3e250288a | ||
| 
						 | 
					38f8043cb2 | ||
| 
						 | 
					a61320ca8c | ||
| 
						 | 
					4bc9bad34f | ||
| 
						 | 
					4392e64672 | ||
| 
						 | 
					74a0d60766 | ||
| 
						 | 
					012a2b6b00 | ||
| 
						 | 
					584bca7658 | ||
| 
						 | 
					5dcd96d926 | ||
| 
						 | 
					bd2be2815c | ||
| 
						 | 
					2a5635492a | ||
| 
						 | 
					eeea7527c1 | ||
| 
						 | 
					d943a9a2f4 | ||
| 
						 | 
					d4335f0e4d | ||
| 
						 | 
					054f7cbdaa | ||
| 
						 | 
					6ff95dab89 | ||
| 
						 | 
					4461bde5da | ||
| 
						 | 
					19152c28cb | ||
| 
						 | 
					dda2967e2d | ||
| 
						 | 
					a680bcda1f | ||
| 
						 | 
					8d24fcba6a | ||
| 
						 | 
					2a96429be8 | ||
| 
						 | 
					5c6f376f4e | ||
| 
						 | 
					1b24fad95f | ||
| 
						 | 
					87743d9ef9 | ||
| 
						 | 
					8ffd0abb1b | ||
| 
						 | 
					2fed09ec18 | ||
| 
						 | 
					6daabb35de | ||
| 
						 | 
					59e98aa06c | ||
| 
						 | 
					3601d95733 | ||
| 
						 | 
					2c57dfd528 | ||
| 
						 | 
					2348f2586c | ||
| 
						 | 
					ed11f954aa | ||
| 
						 | 
					5765a8e38e | ||
| 
						 | 
					4a925fade1 | ||
| 
						 | 
					fca86f43c4 | ||
| 
						 | 
					12005de4c0 | ||
| 
						 | 
					2e3d0d3435 | ||
| 
						 | 
					7d76887517 | ||
| 
						 | 
					bf39ecd1e5 | ||
| 
						 | 
					7ebee09f74 | ||
| 
						 | 
					952a49f749 | ||
| 
						 | 
					8f8c67ad6d | ||
| 
						 | 
					ce659f9926 | ||
| 
						 | 
					c23ec555ff | ||
| 
						 | 
					25b0a93acd | ||
| 
						 | 
					7b2b7d1456 | ||
| 
						 | 
					37444939ab | ||
| 
						 | 
					2fee2e5166 | ||
| 
						 | 
					98bd6c3cb8 | ||
| 
						 | 
					fc31e44fd2 | ||
| 
						 | 
					12f89f0e2e | ||
| 
						 | 
					cdf15fc43a | ||
| 
						 | 
					2a12af28dd | ||
| 
						 | 
					f128fceaba | ||
| 
						 | 
					26c5d66994 | ||
| 
						 | 
					bd390d424a | ||
| 
						 | 
					6b85730361 | ||
| 
						 | 
					d6176d1901 | ||
| 
						 | 
					43f336bea4 | ||
| 
						 | 
					d2ed9e965e | ||
| 
						 | 
					ea73e9d5de | ||
| 
						 | 
					36ef862fc6 | ||
| 
						 | 
					999275ca2c | ||
| 
						 | 
					78c36ec260 | ||
| 
						 | 
					c985fed3e4 | ||
| 
						 | 
					f13fe431b8 | ||
| 
						 | 
					f1d7cc08bb | ||
| 
						 | 
					e662dfbcfb | ||
| 
						 | 
					8aaf667f78 | ||
| 
						 | 
					fdfea73bdb | ||
| 
						 | 
					50161bc84d | ||
| 
						 | 
					8046a4488d | ||
| 
						 | 
					e818c37a0c | ||
| 
						 | 
					16ffa0c3c7 | ||
| 
						 | 
					6a9b839e62 | ||
| 
						 | 
					b5da01931c | ||
| 
						 | 
					99b6896cf4 | ||
| 
						 | 
					0ac05df628 | ||
| 
						 | 
					83726ddcec | ||
| 
						 | 
					be627d488c | ||
| 
						 | 
					e615a3fdf3 | ||
| 
						 | 
					f670345d45 | ||
| 
						 | 
					6032ec3823 | ||
| 
						 | 
					01ed052ae6 | ||
| 
						 | 
					88a9a7c48f | ||
| 
						 | 
					d74755f0a4 | ||
| 
						 | 
					e05871a7f8 | ||
| 
						 | 
					fed44e2f2b | ||
| 
						 | 
					2189f450df | ||
| 
						 | 
					288e8f0f75 | ||
| 
						 | 
					dfe7eaaa04 | ||
| 
						 | 
					51b166b419 | ||
| 
						 | 
					0ac9a85314 | ||
| 
						 | 
					d153297294 | ||
| 
						 | 
					ec71658087 | ||
| 
						 | 
					3f359e67b3 | ||
| 
						 | 
					3220d69a69 | ||
| 
						 | 
					385116bf30 | ||
| 
						 | 
					f3b476a348 | ||
| 
						 | 
					d0dec99222 | ||
| 
						 | 
					b3fa50d4d1 | ||
| 
						 | 
					1dbf245f76 | ||
| 
						 | 
					7c092bc04c | ||
| 
						 | 
					b8f1a8a243 | ||
| 
						 | 
					c2b235b4a3 | ||
| 
						 | 
					19a9d8d254 | ||
| 
						 | 
					c79d29bd6c | ||
| 
						 | 
					14816a1c6d | ||
| 
						 | 
					0c86f1c1aa | ||
| 
						 | 
					8f440ae633 | ||
| 
						 | 
					863a5ba872 | ||
| 
						 | 
					c87a43bdba | ||
| 
						 | 
					fa6a7186e0 | ||
| 
						 | 
					34a5adf951 | ||
| 
						 | 
					4b3ece439e | ||
| 
						 | 
					10dc97c43f | ||
| 
						 | 
					2c59da36c2 | ||
| 
						 | 
					f457fb6067 | ||
| 
						 | 
					6843019481 | ||
| 
						 | 
					17cc5a9b95 | ||
| 
						 | 
					ecf44a4fc7 | ||
| 
						 | 
					4257fed500 | ||
| 
						 | 
					299f83684b | ||
| 
						 | 
					ebeaef94e2 | ||
| 
						 | 
					2399ba05cd | ||
| 
						 | 
					8bcfa97349 | ||
| 
						 | 
					dd3e3ddcdd | ||
| 
						 | 
					2555e23b10 | ||
| 
						 | 
					1595683904 | ||
| 
						 | 
					d8dcc4da27 | 
@@ -78,7 +78,7 @@ gulp.task('build:copy', ['build:copy:views', 'build:copy:lang'], () =>
 | 
			
		||||
	]).pipe(gulp.dest('./built/'))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
gulp.task('test', ['lint', 'mocha']);
 | 
			
		||||
gulp.task('test', ['mocha']);
 | 
			
		||||
 | 
			
		||||
gulp.task('lint', () =>
 | 
			
		||||
	gulp.src('./src/**/*.ts')
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "Startseite"
 | 
			
		||||
    local: "Lokal"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "Global"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "Mitteilungen"
 | 
			
		||||
    list: "Listen"
 | 
			
		||||
    swap-left: "Nach links"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "Die Verbindung scheint zu funktionieren. Bitte lade die Seite neu."
 | 
			
		||||
  flush: "Cache leeren"
 | 
			
		||||
  set-version: "Version angeben"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Datei auswählen"
 | 
			
		||||
  upload: "Dateien von deinem PC hochladen"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "Lokal"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "Global"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "Listen"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "Make the stone color clear in reversi"
 | 
			
		||||
  verified-user: "Verified account"
 | 
			
		||||
  disable-animated-mfm: "Disable animated texts in a post"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "Always post with a warning about media attachment"
 | 
			
		||||
  show-full-acct: "Do not omit the hostname from the username"
 | 
			
		||||
  reduce-motion: "Reduce motion in UI"
 | 
			
		||||
  this-setting-is-this-device-only: "Only for this device"
 | 
			
		||||
  do-not-use-in-production: 'As this is for development, do not use this in production.'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Draw"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "Home"
 | 
			
		||||
    local: "Local"
 | 
			
		||||
    hybrid: "Social"
 | 
			
		||||
    hashtag: "Hashtag"
 | 
			
		||||
    global: "Global"
 | 
			
		||||
    mentions: "Mentions"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "Notifications"
 | 
			
		||||
    list: "Lists"
 | 
			
		||||
    swap-left: "Move to the left"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "Looks like we have a connection. Please reload the page."
 | 
			
		||||
  flush: "Clean cache"
 | 
			
		||||
  set-version: "Specify version"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "NSFW"
 | 
			
		||||
  click-to-show: "Click to show"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "Hide"
 | 
			
		||||
  show: "See more"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "Posts"
 | 
			
		||||
  users: "Users"
 | 
			
		||||
  drive: "Drive"
 | 
			
		||||
  network: "Network"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "The number of posts: increase/decrease (Combined)"
 | 
			
		||||
    local-notes: "The number of posts: increase/decrease (Local)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "Capacity used as the storage: cumulative total"
 | 
			
		||||
    drive-files: "The number of files on the storage: increase/decrease"
 | 
			
		||||
    drive-files-total: "The number of files on the storage: cumulative total"
 | 
			
		||||
    network-requests: "Requests"
 | 
			
		||||
    network-time: "Response time"
 | 
			
		||||
    network-usage: "Traffic"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Choose files"
 | 
			
		||||
  upload: "Upload files from your device"
 | 
			
		||||
@@ -778,7 +793,7 @@ desktop/views/components/settings.profile.vue:
 | 
			
		||||
  birthday: "Birthday"
 | 
			
		||||
  save: "Update profile"
 | 
			
		||||
  locked-account: "Protect your account"
 | 
			
		||||
  is-locked: "フォローを承認制にする"
 | 
			
		||||
  is-locked: "Follow request needs approval"
 | 
			
		||||
  other: "Other"
 | 
			
		||||
  is-bot: "This account is a Bot"
 | 
			
		||||
  is-cat: "This account is a Cat"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "Local"
 | 
			
		||||
  hybrid: "Social"
 | 
			
		||||
  global: "Global"
 | 
			
		||||
  mentions: "Mentions"
 | 
			
		||||
  messages: "Messages"
 | 
			
		||||
  list: "Lists"
 | 
			
		||||
  hashtag: "Hashtag"
 | 
			
		||||
  add-tag-timeline: "Add hashtag tl"
 | 
			
		||||
  add-list: "Add list"
 | 
			
		||||
  list-name: "List name"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "Welcome back,"
 | 
			
		||||
  adjective: "-san"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "Local"
 | 
			
		||||
  hybrid: "Social"
 | 
			
		||||
  global: "Global"
 | 
			
		||||
  mentions: "Mentions"
 | 
			
		||||
  messages: "Messages"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "No posts \"{}\" found."
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
@@ -1160,9 +1183,9 @@ mobile/views/pages/settings/settings.profile.vue:
 | 
			
		||||
  avatar: "Avatar"
 | 
			
		||||
  banner: "Banner"
 | 
			
		||||
  is-cat: "This account is a Cat"
 | 
			
		||||
  is-locked: "フォローを承認制にする"
 | 
			
		||||
  advanced: "その他"
 | 
			
		||||
  privacy: "プライバシー"
 | 
			
		||||
  is-locked: "Follow request needs approval"
 | 
			
		||||
  advanced: "Advanced"
 | 
			
		||||
  privacy: "Privacy"
 | 
			
		||||
  save: "Update profile"
 | 
			
		||||
  saved: "Profile updated"
 | 
			
		||||
  uploading: "Uploading"
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "Hacer el color de la piedra claro en Reversi"
 | 
			
		||||
  verified-user: "Cuenta verificada"
 | 
			
		||||
  disable-animated-mfm: "Desactivar texto animado en una publicación"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'Esto está en desarrollo, no usarlo para producción.'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Empatado"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "Inicio"
 | 
			
		||||
    local: "Local"
 | 
			
		||||
    hybrid: "Social"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "Global"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "Notificaciones"
 | 
			
		||||
    list: "Listado"
 | 
			
		||||
    swap-left: "Desplazar a la izq."
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "Parece que la conexión ha sido posible. Por favor refresca la página."
 | 
			
		||||
  flush: "Limpiar la memoria caché"
 | 
			
		||||
  set-version: "Escoge la versión"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "Publicaciones"
 | 
			
		||||
  users: "Usuarios"
 | 
			
		||||
  drive: "Unidad"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "Número de publicaciones: aumentar/disminuir (Combinado)"
 | 
			
		||||
    local-notes: "Número de publicaciones: aumentar/disminuir (Local)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "Capacidad de almacenamiento usada: Acumulativa total"
 | 
			
		||||
    drive-files: "Número de archivos almacenados: aumentar/disminuir"
 | 
			
		||||
    drive-files-total: "Número de archivos almacenados: Acumulativo total"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Escoger archivos"
 | 
			
		||||
  upload: "Cargar archivos de tu dispositivo"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "Bienvenido/a de vuelta,"
 | 
			
		||||
  adjective: "-san"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,12 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "Compte vérifié"
 | 
			
		||||
  disable-animated-mfm: "Désactiver les textes animés dans les publications"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "Réduire les animations dans l’interface utilisateur"
 | 
			
		||||
  this-setting-is-this-device-only: "Uniquement sur cet appareil"
 | 
			
		||||
  do-not-use-in-production: 'Il s’agit d’une version de développement. Ne pas utiliser dans un environnement de production.'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Partie nulle"
 | 
			
		||||
    my-turn: "C’est votre tour"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "Accueil"
 | 
			
		||||
    local: "Local"
 | 
			
		||||
    hybrid: "Social"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "Global"
 | 
			
		||||
    mentions: "Mentions"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "Notifications"
 | 
			
		||||
    list: "Liste"
 | 
			
		||||
    swap-left: "Déplacer à gauche"
 | 
			
		||||
@@ -253,9 +261,12 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "Succès de la connexion au serveur de Misskey. Veuillez recharger la page."
 | 
			
		||||
  flush: "Vider le cache"
 | 
			
		||||
  set-version: "Choisissez une version"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "Contenu sensible"
 | 
			
		||||
  click-to-show: "Cliquer pour afficher"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
  hide: "Masquer"
 | 
			
		||||
  show: "Voir plus"
 | 
			
		||||
common/views/components/messaging.vue:
 | 
			
		||||
  search-user: "Trouver un·e utilisateur·trice"
 | 
			
		||||
  you: "Vous"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "Publications"
 | 
			
		||||
  users: "Utilisateurs"
 | 
			
		||||
  drive: "Drive"
 | 
			
		||||
  network: "Réseau"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "Requêtes"
 | 
			
		||||
    network-time: "Temps de réponse"
 | 
			
		||||
    network-usage: "Traffic"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Sélection de fichiers"
 | 
			
		||||
  upload: "Téléverser des fichiers à partir de votre ordinateur"
 | 
			
		||||
@@ -665,7 +680,7 @@ desktop/views/components/settings.vue:
 | 
			
		||||
  fetch-on-scroll-desc: "Chargement automatique du contenu lors du défilement de la page."
 | 
			
		||||
  note-visibility: "Visibilité de la publication"
 | 
			
		||||
  default-note-visibility: "Visibilité par défaut"
 | 
			
		||||
  remember-note-visibility: "投稿の公開範囲を記憶する"
 | 
			
		||||
  remember-note-visibility: "Se souvenir du mode de visibilité de la publication"
 | 
			
		||||
  auto-popout: "Fenêtre contextuelle automatique"
 | 
			
		||||
  auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
 | 
			
		||||
  advanced: "Paramètres avancés"
 | 
			
		||||
@@ -778,7 +793,7 @@ desktop/views/components/settings.profile.vue:
 | 
			
		||||
  birthday: "Date de naissance"
 | 
			
		||||
  save: "Mettre à jour le profil"
 | 
			
		||||
  locked-account: "Protéger votre compte"
 | 
			
		||||
  is-locked: "フォローを承認制にする"
 | 
			
		||||
  is-locked: "Demande d’abonnement en attente d’approbation"
 | 
			
		||||
  other: "Autre"
 | 
			
		||||
  is-bot: "Ce compte est un Bot"
 | 
			
		||||
  is-cat: "Ce compte est un Chat"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "Local"
 | 
			
		||||
  hybrid: "Social"
 | 
			
		||||
  global: "Global"
 | 
			
		||||
  mentions: "Mentions"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "Listes"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "Content de vous revoir !"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "Local"
 | 
			
		||||
  hybrid: "Social"
 | 
			
		||||
  global: "Global"
 | 
			
		||||
  mentions: "Mentions"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "Pas de message avec un hashtag {} trouvé."
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
@@ -1160,9 +1183,9 @@ mobile/views/pages/settings/settings.profile.vue:
 | 
			
		||||
  avatar: "Avatar"
 | 
			
		||||
  banner: "Bannière"
 | 
			
		||||
  is-cat: "Ce compte est un Bot"
 | 
			
		||||
  is-locked: "フォローを承認制にする"
 | 
			
		||||
  advanced: "その他"
 | 
			
		||||
  privacy: "プライバシー"
 | 
			
		||||
  is-locked: "Demande d’abonnement en attente d’approbation"
 | 
			
		||||
  advanced: "Avancé"
 | 
			
		||||
  privacy: "Vie privée"
 | 
			
		||||
  save: "Mettre à jour le profil"
 | 
			
		||||
  saved: "Profil mis à jour avec succès"
 | 
			
		||||
  uploading: "En cours d'envoi"
 | 
			
		||||
@@ -1184,7 +1207,7 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  dark-mode: "Mode nuit"
 | 
			
		||||
  i-am-under-limited-internet: "J'ai un accès Internet limité"
 | 
			
		||||
  circle-icons: "Utiliser des icônes circulaires"
 | 
			
		||||
  contrasted-acct: "ユーザー名にコントラストを付ける"
 | 
			
		||||
  contrasted-acct: "Nom d’utilisateur contrasté"
 | 
			
		||||
  timeline: "Fil d'actualité"
 | 
			
		||||
  show-reply-target: "Afficher les réponses"
 | 
			
		||||
  show-my-renotes: "Afficher mes republications"
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -116,6 +116,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
 | 
			
		||||
@@ -161,7 +166,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -273,6 +281,10 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -514,6 +526,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -525,6 +538,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
@@ -900,7 +916,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
@@ -1301,6 +1323,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストをつけんで!"
 | 
			
		||||
  verified-user: "アメちゃん付きアカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内のちょろちょろ動いてんのを止める"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "おあいこ"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "うち"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動や!"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようやわ。ページを再度読み込みしてな。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえり、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿はあらへんで。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "게시물의 문자 애니메이션을 비활성화 할"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "무승부"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "홈"
 | 
			
		||||
    local: "로컬"
 | 
			
		||||
    hybrid: "소셜"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "글로벌"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "통지"
 | 
			
		||||
    list: "목록"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "Het verbinden lijkt te lukken. Herlaad de pagina."
 | 
			
		||||
  flush: "Cache leegmaken"
 | 
			
		||||
  set-version: "Versie opgeven"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Bestanden kiezen"
 | 
			
		||||
  upload: "Bestanden uploaden van je computer"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "Lokaal"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "Algemeen"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "Lijsten"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "Wyłącz animowany tekst we wpisach"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Remis"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "Strona główna"
 | 
			
		||||
    local: "Lokalne"
 | 
			
		||||
    hybrid: "Społeczność"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "Globalne"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "Powiadomienia"
 | 
			
		||||
    list: "Listy"
 | 
			
		||||
    swap-left: "Przesuń w lewo"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "Wygląda na to, że udało się połączyć. Odśwież stronę."
 | 
			
		||||
  flush: "Wyczyść pamięć podręczną"
 | 
			
		||||
  set-version: "Określ wersję"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "Wybierz plik"
 | 
			
		||||
  upload: "Wyślij pliki z Twojego komputera"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "Lokalne"
 | 
			
		||||
  hybrid: "Społeczność"
 | 
			
		||||
  global: "Globalne"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "Listy"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "Witaj ponownie,"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "Lokalne"
 | 
			
		||||
  hybrid: "Społeczność"
 | 
			
		||||
  global: "Globalne"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "Nie znaleziono wpisów zawierających „{}”."
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@ common:
 | 
			
		||||
  about-title: "Uma ⭐ do fediverso."
 | 
			
		||||
  about: "Obrigado por encontrar Misskey. Uma <b>plataforma descentralizada de microblog</b> nascida na Terra. Já que ela existe no Fediverso (um universo onde várias plataformas de mídia social são organizadas), ela é ligada com outras plataformas.Por que você não tira uma folga do agito e confusão da cidade, e mergulha em uma nova internet?"
 | 
			
		||||
  intro:
 | 
			
		||||
    title: "Misskeyって?"
 | 
			
		||||
    about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
 | 
			
		||||
    features: "特徴"
 | 
			
		||||
    rich-contents: "投稿"
 | 
			
		||||
    rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。"
 | 
			
		||||
    reaction: "リアクション"
 | 
			
		||||
    title: "O que é Misskey?"
 | 
			
		||||
    about: "Misskey é um <b>serviço de microblog descentralizado</b>. Personalização sofisticada da interface, variedade de reações a posts, armazenamento de arquivos grátis com gerenciamento integrado e outras funções avançadas estão disponíveis. Um sistema em rede chamado \"Fediverso\" permite que nos comuniquemos com usuários em outras redes sociais. Se você postar algo, por exemplo, seu post não será mandado apenas para o Misskey, mas também para o Mastodon. Apenas imagine que o planeta está enviando ondas de rádio para outros planetas para se comunicar."
 | 
			
		||||
    features: "Recursos"
 | 
			
		||||
    rich-contents: "Post"
 | 
			
		||||
    rich-contents-desc: "Apenas poste suas ideias, temas do momento e qualquer coisa que você queira compartilhar. Você pode querer decorar suas palavras, anexar suas imagens favoritas, enviar arquivos, inclusive vídeos ou criar uma enquete. Essas são as coisas que você pode fazer em Misskey."
 | 
			
		||||
    reaction: "Reações"
 | 
			
		||||
    reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
 | 
			
		||||
    ui: "インターフェース"
 | 
			
		||||
    ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "Conta verificada"
 | 
			
		||||
  disable-animated-mfm: "Desativar texto animado nas publicações"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "Empatado"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "Início"
 | 
			
		||||
    local: "Local"
 | 
			
		||||
    hybrid: "Social"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "Global"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "Notificações"
 | 
			
		||||
    list: "Listas"
 | 
			
		||||
    swap-left: "Mover para a esquerda"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "Limpar o cache"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
@@ -1152,23 +1175,23 @@ mobile/views/pages/games/reversi.vue:
 | 
			
		||||
  reversi: "リバーシ"
 | 
			
		||||
mobile/views/pages/settings/settings.profile.vue:
 | 
			
		||||
  title: "プロフィール"
 | 
			
		||||
  name: "名前"
 | 
			
		||||
  account: "アカウント"
 | 
			
		||||
  location: "場所"
 | 
			
		||||
  description: "自己紹介"
 | 
			
		||||
  birthday: "誕生日"
 | 
			
		||||
  avatar: "アイコン"
 | 
			
		||||
  banner: "バナー"
 | 
			
		||||
  is-cat: "このアカウントはCatです"
 | 
			
		||||
  is-locked: "フォローを承認制にする"
 | 
			
		||||
  advanced: "その他"
 | 
			
		||||
  privacy: "プライバシー"
 | 
			
		||||
  save: "保存"
 | 
			
		||||
  saved: "プロフィールを保存しました"
 | 
			
		||||
  uploading: "アップロード中"
 | 
			
		||||
  upload-failed: "アップロードに失敗しました"
 | 
			
		||||
  name: "Nome"
 | 
			
		||||
  account: "Conta"
 | 
			
		||||
  location: "Lugar"
 | 
			
		||||
  description: "Biografia"
 | 
			
		||||
  birthday: "Data de nascimento"
 | 
			
		||||
  avatar: "Avatar"
 | 
			
		||||
  banner: "Capa"
 | 
			
		||||
  is-cat: "Esta conta é gato"
 | 
			
		||||
  is-locked: "Pedido para seguir precisa ser aprovado"
 | 
			
		||||
  advanced: "Avançado"
 | 
			
		||||
  privacy: "Provacidade"
 | 
			
		||||
  save: "Atualizar perfil"
 | 
			
		||||
  saved: "Perfil atualizado"
 | 
			
		||||
  uploading: "Enviando"
 | 
			
		||||
  upload-failed: "Falha ao enviar"
 | 
			
		||||
mobile/views/pages/search.vue:
 | 
			
		||||
  search: "検索"
 | 
			
		||||
  search: "Pesquisar"
 | 
			
		||||
  empty: "「{}」に関する投稿は見つかりませんでした。"
 | 
			
		||||
  not-found: "「{}」に関する投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/selectdrive.vue:
 | 
			
		||||
@@ -1205,47 +1228,47 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  load-raw-images: "添付された画像を高画質で表示する"
 | 
			
		||||
  load-remote-media: "リモートサーバーのメディアを表示する"
 | 
			
		||||
  twitter: "Twitter連携"
 | 
			
		||||
  twitter-connect: "Twitterアカウントに接続する"
 | 
			
		||||
  twitter-reconnect: "再接続する"
 | 
			
		||||
  twitter-disconnect: "切断する"
 | 
			
		||||
  update: "Misskey Update"
 | 
			
		||||
  version: "バージョン:"
 | 
			
		||||
  latest-version: "最新のバージョン:"
 | 
			
		||||
  update-checking: "アップデートを確認中"
 | 
			
		||||
  check-for-updates: "アップデートを確認"
 | 
			
		||||
  no-updates: "利用可能な更新はありません"
 | 
			
		||||
  no-updates-desc: "お使いのMisskeyは最新です。"
 | 
			
		||||
  update-available: "新しいバージョンが利用可能です"
 | 
			
		||||
  update-available-desc: "ページを再度読み込みすると更新が適用されます。"
 | 
			
		||||
  settings: "設定"
 | 
			
		||||
  signout: "サインアウト"
 | 
			
		||||
  sound: "サウンド"
 | 
			
		||||
  enable-sounds: "サウンドを有効にする"
 | 
			
		||||
  twitter-connect: "Conectar à sua conta no Twitter"
 | 
			
		||||
  twitter-reconnect: "Reconectar"
 | 
			
		||||
  twitter-disconnect: "Desconectar"
 | 
			
		||||
  update: "Atualizar Misskey"
 | 
			
		||||
  version: "Versão atual;"
 | 
			
		||||
  latest-version: "Última versão:"
 | 
			
		||||
  update-checking: "Verificando atualizações"
 | 
			
		||||
  check-for-updates: "Verificar atualizações"
 | 
			
		||||
  no-updates: "Sem atualizações"
 | 
			
		||||
  no-updates-desc: "Seu Misskey está atualizado"
 | 
			
		||||
  update-available: "Uma nova versão está disponível"
 | 
			
		||||
  update-available-desc: "Atualizações vão ser aplicadas depois de recarregar a página"
 | 
			
		||||
  settings: "Configurações"
 | 
			
		||||
  signout: "Sair"
 | 
			
		||||
  sound: "Sons"
 | 
			
		||||
  enable-sounds: "Ativar sons"
 | 
			
		||||
mobile/views/pages/user.vue:
 | 
			
		||||
  follows-you: "フォローされています"
 | 
			
		||||
  following: "フォロー"
 | 
			
		||||
  followers: "フォロワー"
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  follows-you: "Te segue"
 | 
			
		||||
  following: "Seguindo"
 | 
			
		||||
  followers: "Seguidores"
 | 
			
		||||
  notes: "Posts"
 | 
			
		||||
  overview: "概要"
 | 
			
		||||
  timeline: "タイムライン"
 | 
			
		||||
  media: "メディア"
 | 
			
		||||
  is-suspended: "このユーザーは凍結されています。"
 | 
			
		||||
  timeline: "Linha do tempo"
 | 
			
		||||
  media: "Mídia"
 | 
			
		||||
  is-suspended: "Esta conta foi suspensa"
 | 
			
		||||
  is-remote: "Este é uma usuário remoto. O perfil que vê aqui pode não estar completo."
 | 
			
		||||
  view-remote: "Ver o perfil completo."
 | 
			
		||||
mobile/views/pages/user/home.vue:
 | 
			
		||||
  recent-notes: "Notas recentes"
 | 
			
		||||
  images: "Imagens"
 | 
			
		||||
  activity: "Atividade"
 | 
			
		||||
  keywords: "キーワード"
 | 
			
		||||
  domains: "頻出ドメイン"
 | 
			
		||||
  frequently-replied-users: "よく会話するユーザー"
 | 
			
		||||
  keywords: "Palavras chave"
 | 
			
		||||
  domains: "Domínios"
 | 
			
		||||
  frequently-replied-users: "Perguntas frequentes"
 | 
			
		||||
  followers-you-know: "Seguidores que você conhece"
 | 
			
		||||
  last-used-at: "Ativo pela última vez:"
 | 
			
		||||
mobile/views/pages/user/home.followers-you-know.vue:
 | 
			
		||||
  loading: "Carregando"
 | 
			
		||||
  no-users: "知り合いのユーザーはいません"
 | 
			
		||||
mobile/views/pages/user/home.friends.vue:
 | 
			
		||||
  loading: "読み込み中"
 | 
			
		||||
  loading: "Carregando"
 | 
			
		||||
  no-users: "よく会話するユーザーはいません"
 | 
			
		||||
mobile/views/pages/user/home.notes.vue:
 | 
			
		||||
  loading: "Carregando"
 | 
			
		||||
@@ -1255,14 +1278,14 @@ mobile/views/pages/user/home.photos.vue:
 | 
			
		||||
  no-photos: "Sem fotos"
 | 
			
		||||
docs:
 | 
			
		||||
  edit-this-page-on-github: "間違いや改善点を見つけましたか?"
 | 
			
		||||
  edit-this-page-on-github-link: "このページをGitHubで編集"
 | 
			
		||||
  edit-this-page-on-github-link: "Edite esta página no GitHub!"
 | 
			
		||||
  api:
 | 
			
		||||
    entities:
 | 
			
		||||
      properties: "プロパティ"
 | 
			
		||||
      properties: "Propriedades"
 | 
			
		||||
    endpoints:
 | 
			
		||||
      params: "パラメータ"
 | 
			
		||||
      no-params: "パラメータはありません"
 | 
			
		||||
      res: "レスポンス"
 | 
			
		||||
      params: "Parâmetros"
 | 
			
		||||
      no-params: "Sem parâmetros"
 | 
			
		||||
      res: "Resposta"
 | 
			
		||||
      require-credential: "このエンドポイントは認証情報が必須です。"
 | 
			
		||||
      require-permission: "このエンドポイントは{permission}の権限を必要とします。"
 | 
			
		||||
      has-limit: "レートリミットがあります。"
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,11 @@ common:
 | 
			
		||||
  use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
 | 
			
		||||
  verified-user: "公式アカウント"
 | 
			
		||||
  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
			
		||||
  always-show-nsfw: "常に閲覧注意のメディアを表示する"
 | 
			
		||||
  always-mark-nsfw: "常にメディアを閲覧注意として投稿"
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
  reversi:
 | 
			
		||||
    drawn: "引き分け"
 | 
			
		||||
@@ -150,7 +155,10 @@ common:
 | 
			
		||||
    home: "ホーム"
 | 
			
		||||
    local: "ローカル"
 | 
			
		||||
    hybrid: "ソーシャル"
 | 
			
		||||
    hashtag: "ハッシュタグ"
 | 
			
		||||
    global: "グローバル"
 | 
			
		||||
    mentions: "あなた宛て"
 | 
			
		||||
    direct: "ダイレクト投稿"
 | 
			
		||||
    notifications: "通知"
 | 
			
		||||
    list: "リスト"
 | 
			
		||||
    swap-left: "左に移動"
 | 
			
		||||
@@ -253,6 +261,9 @@ common/views/components/connect-failed.troubleshooter.vue:
 | 
			
		||||
  success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
 | 
			
		||||
  flush: "キャッシュの削除"
 | 
			
		||||
  set-version: "バージョン指定"
 | 
			
		||||
common/views/components/media-banner.vue:
 | 
			
		||||
  sensitive: "閲覧注意"
 | 
			
		||||
  click-to-show: "クリックして表示"
 | 
			
		||||
common/views/components/cw-button.vue:
 | 
			
		||||
  hide: "隠す"
 | 
			
		||||
  show: "もっと見る"
 | 
			
		||||
@@ -461,6 +472,7 @@ desktop/views/components/charts.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
@@ -472,6 +484,9 @@ desktop/views/components/charts.vue:
 | 
			
		||||
    drive-total: "ドライブ使用量の累計"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の累計"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -795,7 +810,13 @@ desktop/views/components/timeline.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
  list: "リスト"
 | 
			
		||||
  hashtag: "ハッシュタグ"
 | 
			
		||||
  add-tag-timeline: "ハッシュタグを追加"
 | 
			
		||||
  add-list: "リストを追加"
 | 
			
		||||
  list-name: "リスト名"
 | 
			
		||||
desktop/views/components/ui.header.vue:
 | 
			
		||||
  welcome-back: "おかえりなさい、"
 | 
			
		||||
  adjective: "さん"
 | 
			
		||||
@@ -1120,6 +1141,8 @@ mobile/views/pages/home.vue:
 | 
			
		||||
  local: "ローカル"
 | 
			
		||||
  hybrid: "ソーシャル"
 | 
			
		||||
  global: "グローバル"
 | 
			
		||||
  mentions: "あなた宛て"
 | 
			
		||||
  messages: "メッセージ"
 | 
			
		||||
mobile/views/pages/tag.vue:
 | 
			
		||||
  no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
 | 
			
		||||
mobile/views/pages/welcome.vue:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								package.json
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "misskey",
 | 
			
		||||
	"author": "syuilo <i@syuilo.com>",
 | 
			
		||||
	"version": "8.36.0",
 | 
			
		||||
	"clientVersion": "1.0.9637",
 | 
			
		||||
	"version": "8.53.0",
 | 
			
		||||
	"clientVersion": "1.0.9898",
 | 
			
		||||
	"codename": "nighthike",
 | 
			
		||||
	"main": "./built/index.js",
 | 
			
		||||
	"private": true,
 | 
			
		||||
@@ -20,10 +20,10 @@
 | 
			
		||||
		"format": "gulp format"
 | 
			
		||||
	},
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"@fortawesome/fontawesome": "1.1.8",
 | 
			
		||||
		"@fortawesome/fontawesome-free-brands": "5.0.13",
 | 
			
		||||
		"@fortawesome/fontawesome-free-regular": "5.0.13",
 | 
			
		||||
		"@fortawesome/fontawesome-free-solid": "5.0.13",
 | 
			
		||||
		"@fortawesome/fontawesome-svg-core": "1.2.4",
 | 
			
		||||
		"@fortawesome/free-brands-svg-icons": "5.3.1",
 | 
			
		||||
		"@fortawesome/free-regular-svg-icons": "5.3.1",
 | 
			
		||||
		"@fortawesome/free-solid-svg-icons": "5.3.1",
 | 
			
		||||
		"@koa/cors": "2.2.2",
 | 
			
		||||
		"@prezzemolo/rap": "0.1.2",
 | 
			
		||||
		"@prezzemolo/zip": "0.0.3",
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
		"@types/mocha": "5.2.3",
 | 
			
		||||
		"@types/mongodb": "3.1.7",
 | 
			
		||||
		"@types/ms": "0.7.30",
 | 
			
		||||
		"@types/node": "10.9.4",
 | 
			
		||||
		"@types/node": "10.10.1",
 | 
			
		||||
		"@types/portscanner": "2.1.0",
 | 
			
		||||
		"@types/pug": "2.0.4",
 | 
			
		||||
		"@types/qrcode": "1.2.0",
 | 
			
		||||
@@ -101,7 +101,6 @@
 | 
			
		||||
		"dompurify": "1.0.5",
 | 
			
		||||
		"double-ended-queue": "2.1.0-0",
 | 
			
		||||
		"elasticsearch": "15.1.1",
 | 
			
		||||
		"element-ui": "2.4.6",
 | 
			
		||||
		"emojilib": "2.3.0",
 | 
			
		||||
		"escape-regexp": "0.0.1",
 | 
			
		||||
		"eslint": "5.0.1",
 | 
			
		||||
@@ -132,7 +131,6 @@
 | 
			
		||||
		"insert-text-at-cursor": "0.1.1",
 | 
			
		||||
		"is-root": "2.0.0",
 | 
			
		||||
		"is-url": "1.2.4",
 | 
			
		||||
		"jquery": "3.3.1",
 | 
			
		||||
		"js-yaml": "3.12.0",
 | 
			
		||||
		"jsdom": "11.12.0",
 | 
			
		||||
		"koa": "2.5.1",
 | 
			
		||||
@@ -160,8 +158,6 @@
 | 
			
		||||
		"ms": "2.1.1",
 | 
			
		||||
		"nan": "2.11.0",
 | 
			
		||||
		"nested-property": "0.0.7",
 | 
			
		||||
		"node-sass": "4.9.3",
 | 
			
		||||
		"node-sass-json-importer": "4.0.1",
 | 
			
		||||
		"nprogress": "0.2.0",
 | 
			
		||||
		"object-assign-deep": "0.4.0",
 | 
			
		||||
		"on-build-webpack": "0.1.0",
 | 
			
		||||
@@ -179,6 +175,7 @@
 | 
			
		||||
		"redis": "2.8.0",
 | 
			
		||||
		"request": "2.88.0",
 | 
			
		||||
		"request-promise-native": "1.0.5",
 | 
			
		||||
		"request-stats": "3.0.0",
 | 
			
		||||
		"rimraf": "2.6.2",
 | 
			
		||||
		"rndstr": "1.0.0",
 | 
			
		||||
		"s-age": "1.1.2",
 | 
			
		||||
@@ -209,7 +206,7 @@
 | 
			
		||||
		"v-animate-css": "0.0.2",
 | 
			
		||||
		"vue": "2.5.17",
 | 
			
		||||
		"vue-chartjs": "3.4.0",
 | 
			
		||||
		"vue-cropperjs": "2.2.1",
 | 
			
		||||
		"vue-cropperjs": "2.2.2",
 | 
			
		||||
		"vue-js-modal": "1.3.26",
 | 
			
		||||
		"vue-json-tree-view": "2.1.4",
 | 
			
		||||
		"vue-loader": "15.4.2",
 | 
			
		||||
@@ -222,7 +219,7 @@
 | 
			
		||||
		"vuex-persistedstate": "2.5.4",
 | 
			
		||||
		"web-push": "3.3.2",
 | 
			
		||||
		"webfinger.js": "2.6.6",
 | 
			
		||||
		"webpack": "4.18.0",
 | 
			
		||||
		"webpack": "4.19.0",
 | 
			
		||||
		"webpack-cli": "3.1.0",
 | 
			
		||||
		"websocket": "1.0.26",
 | 
			
		||||
		"ws": "6.0.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								src/client/app/common/hotkey.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/client/app/common/hotkey.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
import keyCode from './keycode';
 | 
			
		||||
import { concat } from '../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
type pattern = {
 | 
			
		||||
	which: string[];
 | 
			
		||||
	ctrl?: boolean;
 | 
			
		||||
	shift?: boolean;
 | 
			
		||||
	alt?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type action = {
 | 
			
		||||
	patterns: pattern[];
 | 
			
		||||
 | 
			
		||||
	callback: Function;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getKeyMap = keymap => Object.entries(keymap).map(([patterns, callback]): action => {
 | 
			
		||||
	const result = {
 | 
			
		||||
		patterns: [],
 | 
			
		||||
		callback: callback
 | 
			
		||||
	} as action;
 | 
			
		||||
 | 
			
		||||
	result.patterns = patterns.split('|').map(part => {
 | 
			
		||||
		const pattern = {
 | 
			
		||||
			which: [],
 | 
			
		||||
			ctrl: false,
 | 
			
		||||
			alt: false,
 | 
			
		||||
			shift: false
 | 
			
		||||
		} as pattern;
 | 
			
		||||
 | 
			
		||||
		part.trim().split('+').forEach(key => {
 | 
			
		||||
			key = key.trim().toLowerCase();
 | 
			
		||||
			switch (key) {
 | 
			
		||||
				case 'ctrl': pattern.ctrl = true; break;
 | 
			
		||||
				case 'alt': pattern.alt = true; break;
 | 
			
		||||
				case 'shift': pattern.shift = true; break;
 | 
			
		||||
				default: pattern.which = keyCode(key).map(k => k.toLowerCase());
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		return pattern;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ignoreElemens = ['input', 'textarea'];
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
	install(Vue) {
 | 
			
		||||
		Vue.directive('hotkey', {
 | 
			
		||||
			bind(el, binding) {
 | 
			
		||||
				el._hotkey_global = binding.modifiers.global === true;
 | 
			
		||||
 | 
			
		||||
				const actions = getKeyMap(binding.value);
 | 
			
		||||
 | 
			
		||||
				// flatten
 | 
			
		||||
				const reservedKeys = concat(concat(actions.map(a => a.patterns.map(p => p.which))));
 | 
			
		||||
 | 
			
		||||
				el.dataset.reservedKeys = reservedKeys.map(key => `'${key}'`).join(' ');
 | 
			
		||||
 | 
			
		||||
				el._keyHandler = e => {
 | 
			
		||||
					const key = e.code.toLowerCase();
 | 
			
		||||
 | 
			
		||||
					const targetReservedKeys = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeys || '' : '';
 | 
			
		||||
					if (document.activeElement && ignoreElemens.some(el => document.activeElement.matches(el))) return;
 | 
			
		||||
 | 
			
		||||
					for (const action of actions) {
 | 
			
		||||
						if (el._hotkey_global && targetReservedKeys.includes(`'${key}'`)) break;
 | 
			
		||||
 | 
			
		||||
						const matched = action.patterns.some(pattern => {
 | 
			
		||||
							const matched = pattern.which.includes(key) &&
 | 
			
		||||
								pattern.ctrl == e.ctrlKey &&
 | 
			
		||||
								pattern.shift == e.shiftKey &&
 | 
			
		||||
								pattern.alt == e.altKey;
 | 
			
		||||
 | 
			
		||||
							if (matched) {
 | 
			
		||||
								e.preventDefault();
 | 
			
		||||
								e.stopPropagation();
 | 
			
		||||
								action.callback(e);
 | 
			
		||||
								return true;
 | 
			
		||||
							} else {
 | 
			
		||||
								return false;
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
 | 
			
		||||
						if (matched) {
 | 
			
		||||
							break;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				if (el._hotkey_global) {
 | 
			
		||||
					document.addEventListener('keydown', el._keyHandler);
 | 
			
		||||
				} else {
 | 
			
		||||
					el.addEventListener('keydown', el._keyHandler);
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			unbind(el) {
 | 
			
		||||
				if (el._hotkey_global) {
 | 
			
		||||
					document.removeEventListener('keydown', el._keyHandler);
 | 
			
		||||
				} else {
 | 
			
		||||
					el.removeEventListener('keydown', el._keyHandler);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										33
									
								
								src/client/app/common/keycode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/client/app/common/keycode.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
export default (input: string): string[] => {
 | 
			
		||||
	if (Object.keys(aliases).some(a => a.toLowerCase() == input.toLowerCase())) {
 | 
			
		||||
		const codes = aliases[input];
 | 
			
		||||
		return Array.isArray(codes) ? codes : [codes];
 | 
			
		||||
	} else {
 | 
			
		||||
		return [input];
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const aliases = {
 | 
			
		||||
	'esc': 'Escape',
 | 
			
		||||
	'enter': ['Enter', 'NumpadEnter'],
 | 
			
		||||
	'up': 'ArrowUp',
 | 
			
		||||
	'down': 'ArrowDown',
 | 
			
		||||
	'left': 'ArrowLeft',
 | 
			
		||||
	'right': 'ArrowRight',
 | 
			
		||||
	'plus': ['NumpadAdd', 'Semicolon'],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
* Programatically add the following
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// lower case chars
 | 
			
		||||
for (let i = 97; i < 123; i++) {
 | 
			
		||||
	const char = String.fromCharCode(i);
 | 
			
		||||
	aliases[char] = `Key${char.toUpperCase()}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// numbers
 | 
			
		||||
for (let i = 0; i < 10; i++) {
 | 
			
		||||
	aliases[i] = [`Numpad${i}`, `Digit${i}`];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/client/app/common/scripts/streaming/hashtag.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/client/app/common/scripts/streaming/hashtag.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import Stream from './stream';
 | 
			
		||||
import MiOS from '../../../mios';
 | 
			
		||||
 | 
			
		||||
export class HashtagStream extends Stream {
 | 
			
		||||
	constructor(os: MiOS, me, q) {
 | 
			
		||||
		super(os, 'hashtag', me ? {
 | 
			
		||||
			i: me.token,
 | 
			
		||||
			q: JSON.stringify(q)
 | 
			
		||||
		} : {
 | 
			
		||||
			q: JSON.stringify(q)
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<span class="mk-acct">
 | 
			
		||||
	<span class="name">@{{ user.username }}</span>
 | 
			
		||||
	<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="user.host || detail">@{{ user.host || host }}</span>
 | 
			
		||||
	<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="user.host || detail || $store.state.settings.showFullAcct">@{{ user.host || host }}</span>
 | 
			
		||||
</span>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -50,15 +50,15 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="player" v-if="game.isEnded">
 | 
			
		||||
		<el-button-group>
 | 
			
		||||
			<el-button type="primary" @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</el-button>
 | 
			
		||||
			<el-button type="primary" @click="logPos--" :disabled="logPos == 0">%fa:angle-left%</el-button>
 | 
			
		||||
		</el-button-group>
 | 
			
		||||
		<div>
 | 
			
		||||
			<button @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</button>
 | 
			
		||||
			<button @click="logPos--" :disabled="logPos == 0">%fa:angle-left%</button>
 | 
			
		||||
		</div>
 | 
			
		||||
		<span>{{ logPos }} / {{ logs.length }}</span>
 | 
			
		||||
		<el-button-group>
 | 
			
		||||
			<el-button type="primary" @click="logPos++" :disabled="logPos == logs.length">%fa:angle-right%</el-button>
 | 
			
		||||
			<el-button type="primary" @click="logPos = logs.length" :disabled="logPos == logs.length">%fa:angle-double-right%</el-button>
 | 
			
		||||
		</el-button-group>
 | 
			
		||||
		<div>
 | 
			
		||||
			<button @click="logPos++" :disabled="logPos == logs.length">%fa:angle-right%</button>
 | 
			
		||||
			<button @click="logPos = logs.length" :disabled="logPos == logs.length">%fa:angle-double-right%</button>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="info">
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
	<h1>%i18n:@title%</h1>
 | 
			
		||||
	<p>%i18n:@sub-title%</p>
 | 
			
		||||
	<div class="play">
 | 
			
		||||
		<!--<el-button round>フリーマッチ(準備中)</el-button>-->
 | 
			
		||||
		<form-button primary round @click="match">%i18n:@invite%</form-button>
 | 
			
		||||
		<details>
 | 
			
		||||
			<summary>%i18n:@rule%</summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -59,11 +59,6 @@
 | 
			
		||||
			</header>
 | 
			
		||||
 | 
			
		||||
			<div>
 | 
			
		||||
				<el-alert v-for="message in messages"
 | 
			
		||||
						:title="message.text"
 | 
			
		||||
						:type="message.type"
 | 
			
		||||
						:key="message.id"/>
 | 
			
		||||
 | 
			
		||||
				<template v-for="item in form">
 | 
			
		||||
					<mk-switch v-if="item.type == 'switch'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm(item)">{{ item.desc || '' }}</mk-switch>
 | 
			
		||||
 | 
			
		||||
@@ -93,7 +88,7 @@
 | 
			
		||||
						</header>
 | 
			
		||||
 | 
			
		||||
						<div>
 | 
			
		||||
							<el-input v-model="item.value" @change="onChangeForm(item)"/>
 | 
			
		||||
							<input v-model="item.value" @change="onChangeForm(item)"/>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</template>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								src/client/app/common/views/components/media-banner.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/client/app/common/views/components/media-banner.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-media-banner">
 | 
			
		||||
	<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
 | 
			
		||||
		<span class="icon">%fa:exclamation-triangle%</span>
 | 
			
		||||
		<b>%i18n:@sensitive%</b>
 | 
			
		||||
		<span>%i18n:@click-to-show%</span>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="audio" v-else-if="media.type.startsWith('audio')">
 | 
			
		||||
		<audio class="audio"
 | 
			
		||||
			:src="media.url"
 | 
			
		||||
			:title="media.name"
 | 
			
		||||
			controls
 | 
			
		||||
			ref="audio"
 | 
			
		||||
			preload="metadata" />
 | 
			
		||||
	</div>
 | 
			
		||||
	<a class="download" v-else
 | 
			
		||||
		:href="media.url"
 | 
			
		||||
		:title="media.name"
 | 
			
		||||
		:download="media.name"
 | 
			
		||||
	>
 | 
			
		||||
		<span class="icon">%fa:download%</span>
 | 
			
		||||
		<b>{{ media.name }}</b>
 | 
			
		||||
	</a>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		media: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hide: true
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
root(isDark)
 | 
			
		||||
	width 100%
 | 
			
		||||
	border-radius 4px
 | 
			
		||||
	margin-top 4px
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	> .download,
 | 
			
		||||
	> .sensitive
 | 
			
		||||
		display flex
 | 
			
		||||
		align-items center
 | 
			
		||||
		font-size 12px
 | 
			
		||||
		padding 8px 12px
 | 
			
		||||
		white-space nowrap
 | 
			
		||||
 | 
			
		||||
		> *
 | 
			
		||||
			display block
 | 
			
		||||
 | 
			
		||||
		> b
 | 
			
		||||
			overflow hidden
 | 
			
		||||
			text-overflow ellipsis
 | 
			
		||||
 | 
			
		||||
		> *:not(:last-child)
 | 
			
		||||
			margin-right .2em
 | 
			
		||||
 | 
			
		||||
		> .icon
 | 
			
		||||
			font-size 1.6em
 | 
			
		||||
 | 
			
		||||
	> .download
 | 
			
		||||
		background isDark ? #21242d : #f7f7f7
 | 
			
		||||
 | 
			
		||||
	> .sensitive
 | 
			
		||||
		background #111
 | 
			
		||||
		color #fff
 | 
			
		||||
 | 
			
		||||
	> .audio
 | 
			
		||||
		.audio
 | 
			
		||||
			display block
 | 
			
		||||
			width 100%
 | 
			
		||||
 | 
			
		||||
.mk-media-banner[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.mk-media-banner:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,18 +1,27 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-media-list">
 | 
			
		||||
	<div :data-count="mediaList.length" ref="grid">
 | 
			
		||||
		<template v-for="media in mediaList">
 | 
			
		||||
			<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')" :inline-playable="mediaList.length === 1"/>
 | 
			
		||||
			<mk-media-image :image="media" :key="media.id" v-else :raw="raw"/>
 | 
			
		||||
	<template v-for="media in mediaList.filter(media => !previewable(media))">
 | 
			
		||||
		<x-banner :media="media" :key="media.id"/>
 | 
			
		||||
	</template>
 | 
			
		||||
	<div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container">
 | 
			
		||||
		<div :data-count="mediaList.filter(media => previewable(media)).length" ref="grid">
 | 
			
		||||
			<template v-for="media in mediaList">
 | 
			
		||||
				<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')"/>
 | 
			
		||||
				<mk-media-image :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
 | 
			
		||||
			</template>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XBanner from './media-banner.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XBanner
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		mediaList: {
 | 
			
		||||
			required: true
 | 
			
		||||
@@ -22,15 +31,25 @@ export default Vue.extend({
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		// for Safari bug
 | 
			
		||||
		//#region for Safari bug
 | 
			
		||||
		if (this.$refs.grid) {
 | 
			
		||||
			this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px` : '128px';
 | 
			
		||||
		}
 | 
			
		||||
		//#endregion
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		previewable(file) {
 | 
			
		||||
			return file.type.startsWith('video') || file.type.startsWith('image');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-media-list
 | 
			
		||||
	> .gird-container
 | 
			
		||||
		width 100%
 | 
			
		||||
		margin-top 4px
 | 
			
		||||
 | 
			
		||||
		&:before
 | 
			
		||||
			content ''
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@
 | 
			
		||||
<div class="onchrpzrvnoruiaenfcqvccjfuupzzwv">
 | 
			
		||||
	<div class="backdrop" ref="backdrop" @click="close"></div>
 | 
			
		||||
	<div class="popover" :class="{ hukidasi }" ref="popover">
 | 
			
		||||
		<template v-for="item in items">
 | 
			
		||||
		<template v-for="item, i in items">
 | 
			
		||||
			<div v-if="item === null"></div>
 | 
			
		||||
			<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text"></button>
 | 
			
		||||
			<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text" :tabindex="i"></button>
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -108,7 +108,7 @@ export default Vue.extend({
 | 
			
		||||
				easing: 'easeInBack',
 | 
			
		||||
				complete: () => {
 | 
			
		||||
					this.$emit('closed');
 | 
			
		||||
					this.$destroy();
 | 
			
		||||
					this.destroyDom();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ export default Vue.extend({
 | 
			
		||||
			(this as any).api('i/pin', {
 | 
			
		||||
				noteId: this.note.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
				this.destroyDom();
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +73,7 @@ export default Vue.extend({
 | 
			
		||||
			(this as any).api('notes/delete', {
 | 
			
		||||
				noteId: this.note.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
				this.destroyDom();
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -81,13 +81,13 @@ export default Vue.extend({
 | 
			
		||||
			(this as any).api('notes/favorites/create', {
 | 
			
		||||
				noteId: this.note.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
				this.destroyDom();
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		closed() {
 | 
			
		||||
			this.$nextTick(() => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
				this.destroyDom();
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-reaction-picker">
 | 
			
		||||
<div class="mk-reaction-picker" v-hotkey.global="keymap">
 | 
			
		||||
	<div class="backdrop" ref="backdrop" @click="close"></div>
 | 
			
		||||
	<div class="popover" :class="{ compact, big }" ref="popover">
 | 
			
		||||
		<p v-if="!compact">{{ title }}</p>
 | 
			
		||||
		<div>
 | 
			
		||||
		<div ref="buttons" :class="{ showFocus }">
 | 
			
		||||
			<button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" title="%i18n:common.reactions.like%"><mk-reaction-icon reaction='like'/></button>
 | 
			
		||||
			<button @click="react('love')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="2" title="%i18n:common.reactions.love%"><mk-reaction-icon reaction='love'/></button>
 | 
			
		||||
			<button @click="react('laugh')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="3" title="%i18n:common.reactions.laugh%"><mk-reaction-icon reaction='laugh'/></button>
 | 
			
		||||
@@ -31,30 +31,84 @@ export default Vue.extend({
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		source: {
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		compact: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		cb: {
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		big: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showFocus: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		animation: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			title: placeholder
 | 
			
		||||
			title: placeholder,
 | 
			
		||||
			focus: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'esc': this.close,
 | 
			
		||||
				'enter|space|plus': this.choose,
 | 
			
		||||
				'up|k': this.focusUp,
 | 
			
		||||
				'left|h|shift+tab': this.focusLeft,
 | 
			
		||||
				'right|l|tab': this.focusRight,
 | 
			
		||||
				'down|j': this.focusDown,
 | 
			
		||||
				'1': () => this.react('like'),
 | 
			
		||||
				'2': () => this.react('love'),
 | 
			
		||||
				'3': () => this.react('laugh'),
 | 
			
		||||
				'4': () => this.react('hmm'),
 | 
			
		||||
				'5': () => this.react('surprise'),
 | 
			
		||||
				'6': () => this.react('congrats'),
 | 
			
		||||
				'7': () => this.react('angry'),
 | 
			
		||||
				'8': () => this.react('confused'),
 | 
			
		||||
				'9': () => this.react('rip'),
 | 
			
		||||
				'0': () => this.react('pudding'),
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		focus(i) {
 | 
			
		||||
			this.$refs.buttons.children[i].focus();
 | 
			
		||||
 | 
			
		||||
			if (this.showFocus) {
 | 
			
		||||
				this.title = this.$refs.buttons.children[i].title;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			this.focus = 0;
 | 
			
		||||
 | 
			
		||||
			const popover = this.$refs.popover as any;
 | 
			
		||||
 | 
			
		||||
			const rect = this.source.getBoundingClientRect();
 | 
			
		||||
@@ -76,7 +130,7 @@ export default Vue.extend({
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$refs.backdrop,
 | 
			
		||||
				opacity: 1,
 | 
			
		||||
				duration: 100,
 | 
			
		||||
				duration: this.animation ? 100 : 0,
 | 
			
		||||
				easing: 'linear'
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
@@ -84,10 +138,11 @@ export default Vue.extend({
 | 
			
		||||
				targets: this.$refs.popover,
 | 
			
		||||
				opacity: 1,
 | 
			
		||||
				scale: [0.5, 1],
 | 
			
		||||
				duration: 500
 | 
			
		||||
				duration: this.animation ? 500 : 0
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		react(reaction) {
 | 
			
		||||
			(this as any).api('notes/reactions/create', {
 | 
			
		||||
@@ -95,21 +150,25 @@ export default Vue.extend({
 | 
			
		||||
				reaction: reaction
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				if (this.cb) this.cb();
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
				this.$emit('closed');
 | 
			
		||||
				this.destroyDom();
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onMouseover(e) {
 | 
			
		||||
			this.title = e.target.title;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onMouseout(e) {
 | 
			
		||||
			this.title = placeholder;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		close() {
 | 
			
		||||
			(this.$refs.backdrop as any).style.pointerEvents = 'none';
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$refs.backdrop,
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				duration: 200,
 | 
			
		||||
				duration: this.animation ? 200 : 0,
 | 
			
		||||
				easing: 'linear'
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
@@ -118,10 +177,33 @@ export default Vue.extend({
 | 
			
		||||
				targets: this.$refs.popover,
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				scale: 0.5,
 | 
			
		||||
				duration: 200,
 | 
			
		||||
				duration: this.animation ? 200 : 0,
 | 
			
		||||
				easing: 'easeInBack',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => {
 | 
			
		||||
					this.$emit('closed');
 | 
			
		||||
					this.destroyDom();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focusUp() {
 | 
			
		||||
			this.focus = this.focus == 0 ? 9 : this.focus < 5 ? (this.focus + 4) : (this.focus - 5);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focusDown() {
 | 
			
		||||
			this.focus = this.focus == 9 ? 0 : this.focus >= 5 ? (this.focus - 4) : (this.focus + 5);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focusRight() {
 | 
			
		||||
			this.focus = this.focus == 9 ? 0 : (this.focus + 1);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focusLeft() {
 | 
			
		||||
			this.focus = this.focus == 0 ? 9 : (this.focus - 1);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		choose() {
 | 
			
		||||
			this.$refs.buttons.childNodes[this.focus].click();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
@@ -207,6 +289,21 @@ root(isDark)
 | 
			
		||||
			width 240px
 | 
			
		||||
			text-align center
 | 
			
		||||
 | 
			
		||||
			&.showFocus
 | 
			
		||||
				> button:focus
 | 
			
		||||
					z-index 1
 | 
			
		||||
 | 
			
		||||
					&:after
 | 
			
		||||
						content ""
 | 
			
		||||
						pointer-events none
 | 
			
		||||
						position absolute
 | 
			
		||||
						top 0
 | 
			
		||||
						right 0
 | 
			
		||||
						bottom 0
 | 
			
		||||
						left 0
 | 
			
		||||
						border 2px solid rgba($theme-color, 0.3)
 | 
			
		||||
						border-radius 4px
 | 
			
		||||
 | 
			
		||||
			> button
 | 
			
		||||
				padding 0
 | 
			
		||||
				width 40px
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,7 @@
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<!-- <transition-group v-else tag="div" name="chart"> -->
 | 
			
		||||
	<div>
 | 
			
		||||
	<transition-group v-else tag="div" name="chart">
 | 
			
		||||
		<div v-for="stat in stats" :key="stat.tag">
 | 
			
		||||
			<div class="tag">
 | 
			
		||||
				<router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
 | 
			
		||||
@@ -12,8 +11,7 @@
 | 
			
		||||
			</div>
 | 
			
		||||
			<x-chart class="chart" :src="stat.chart"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<!-- </transition-group> -->
 | 
			
		||||
	</transition-group>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ export default Vue.extend({
 | 
			
		||||
				this.$store.commit('device/setVisibility', visibility);
 | 
			
		||||
			}
 | 
			
		||||
			this.$emit('chosen', visibility);
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		},
 | 
			
		||||
		close() {
 | 
			
		||||
			(this.$refs.backdrop as any).style.pointerEvents = 'none';
 | 
			
		||||
@@ -119,7 +119,7 @@ export default Vue.extend({
 | 
			
		||||
				scale: 0.5,
 | 
			
		||||
				duration: 200,
 | 
			
		||||
				easing: 'easeInBack',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -167,7 +167,7 @@ class Autocomplete {
 | 
			
		||||
	private close() {
 | 
			
		||||
		if (this.suggestion == null) return;
 | 
			
		||||
 | 
			
		||||
		this.suggestion.$destroy();
 | 
			
		||||
		this.suggestion.destroyDom();
 | 
			
		||||
		this.suggestion = null;
 | 
			
		||||
 | 
			
		||||
		this.textarea.focus();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ import VueRouter from 'vue-router';
 | 
			
		||||
 | 
			
		||||
// Style
 | 
			
		||||
import './style.styl';
 | 
			
		||||
import '../../element.scss';
 | 
			
		||||
 | 
			
		||||
import init from '../init';
 | 
			
		||||
import fuckAdBlock from '../common/scripts/fuck-ad-block';
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,11 @@
 | 
			
		||||
				<option value="drive">%i18n:@charts.drive%</option>
 | 
			
		||||
				<option value="drive-total">%i18n:@charts.drive-total%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
			<optgroup label="%i18n:@network%">
 | 
			
		||||
				<option value="network-requests">%i18n:@charts.network-requests%</option>
 | 
			
		||||
				<option value="network-time">%i18n:@charts.network-time%</option>
 | 
			
		||||
				<option value="network-usage">%i18n:@charts.network-usage%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
		</select>
 | 
			
		||||
		<div>
 | 
			
		||||
			<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
 | 
			
		||||
@@ -41,7 +46,10 @@ const colors = {
 | 
			
		||||
	localPlus: 'rgb(52, 178, 118)',
 | 
			
		||||
	remotePlus: 'rgb(158, 255, 209)',
 | 
			
		||||
	localMinus: 'rgb(255, 97, 74)',
 | 
			
		||||
	remoteMinus: 'rgb(255, 149, 134)'
 | 
			
		||||
	remoteMinus: 'rgb(255, 149, 134)',
 | 
			
		||||
 | 
			
		||||
	incoming: 'rgb(52, 178, 118)',
 | 
			
		||||
	outgoing: 'rgb(255, 97, 74)',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const rgba = (color: string): string => {
 | 
			
		||||
@@ -75,6 +83,9 @@ export default Vue.extend({
 | 
			
		||||
				case 'drive-total': return this.driveTotalChart();
 | 
			
		||||
				case 'drive-files': return this.driveFilesChart();
 | 
			
		||||
				case 'drive-files-total': return this.driveFilesTotalChart();
 | 
			
		||||
				case 'network-requests': return this.networkRequestsChart();
 | 
			
		||||
				case 'network-time': return this.networkTimeChart();
 | 
			
		||||
				case 'network-usage': return this.networkUsageChart();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -89,7 +100,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	created() {
 | 
			
		||||
		(this as any).api('chart', {
 | 
			
		||||
			limit: 32
 | 
			
		||||
			limit: 35
 | 
			
		||||
		}).then(chart => {
 | 
			
		||||
			this.chart = chart;
 | 
			
		||||
		});
 | 
			
		||||
@@ -544,8 +555,96 @@ export default Vue.extend({
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		networkRequestsChart(): any {
 | 
			
		||||
			const data = this.stats.slice().reverse().map(x => ({
 | 
			
		||||
				date: new Date(x.date),
 | 
			
		||||
				requests: x.network.requests
 | 
			
		||||
			}));
 | 
			
		||||
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Requests',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: data.map(x => ({ t: x.date, y: x.requests }))
 | 
			
		||||
				}]
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		networkTimeChart(): any {
 | 
			
		||||
			const data = this.stats.slice().reverse().map(x => ({
 | 
			
		||||
				date: new Date(x.date),
 | 
			
		||||
				time: x.network.requests != 0 ? (x.network.totalTime / x.network.requests) : 0,
 | 
			
		||||
			}));
 | 
			
		||||
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Avg time (ms)',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: data.map(x => ({ t: x.date, y: x.time }))
 | 
			
		||||
				}]
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		networkUsageChart(): any {
 | 
			
		||||
			const data = this.stats.slice().reverse().map(x => ({
 | 
			
		||||
				date: new Date(x.date),
 | 
			
		||||
				incoming: x.network.incomingBytes,
 | 
			
		||||
				outgoing: x.network.outgoingBytes
 | 
			
		||||
			}));
 | 
			
		||||
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Incoming',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.incoming),
 | 
			
		||||
					borderColor: colors.incoming,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: data.map(x => ({ t: x.date, y: x.incoming }))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Outgoing',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.outgoing),
 | 
			
		||||
					borderColor: colors.outgoing,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: data.map(x => ({ t: x.date, y: x.outgoing }))
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('bytes')(value, 1);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@@ -582,6 +681,6 @@ export default Vue.extend({
 | 
			
		||||
	> div
 | 
			
		||||
		> *
 | 
			
		||||
			display block
 | 
			
		||||
			height 320px
 | 
			
		||||
			height 350px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="800px" height="500px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal width="800px" height="500px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header">
 | 
			
		||||
		<span v-html="title" :class="$style.title"></span>
 | 
			
		||||
		<span :class="$style.count" v-if="multiple && files.length > 0">({{ files.length }}%i18n:@choose-file%)</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="800px" height="500px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal width="800px" height="500px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header">
 | 
			
		||||
		<span v-html="title" :class="$style.title"></span>
 | 
			
		||||
	</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ export default Vue.extend({
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.$emit('closed');
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ export default Vue.extend({
 | 
			
		||||
				scale: 0.8,
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				easing: [ 0.5, -0.5, 1, 0.5 ],
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onBgClick() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" @closed="$destroy" width="800px" height="500px" :popout-url="popout">
 | 
			
		||||
<mk-window ref="window" @closed="destroyDom" width="800px" height="500px" :popout-url="popout">
 | 
			
		||||
	<template slot="header">
 | 
			
		||||
		<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:@used%</p>
 | 
			
		||||
		<span :class="$style.title">%fa:cloud%%i18n:@drive%</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window width="400px" height="550px" @closed="$destroy">
 | 
			
		||||
<mk-window width="400px" height="550px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">
 | 
			
		||||
		<img :src="user.avatarUrl" alt=""/>{{ '%i18n:@followers%'.replace('{}', name) }}
 | 
			
		||||
	</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window width="400px" height="550px" @closed="$destroy">
 | 
			
		||||
<mk-window width="400px" height="550px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">
 | 
			
		||||
		<img :src="user.avatarUrl" alt=""/>{{ '%i18n:@following%'.replace('{}', name) }}
 | 
			
		||||
	</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
	<p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
 | 
			
		||||
	<a class="refresh" @click="refresh">%i18n:@refresh%</a>
 | 
			
		||||
	<button class="close" @click="$destroy()" title="%i18n:@close%">%fa:times%</button>
 | 
			
		||||
	<button class="close" @click="destroyDom()" title="%i18n:@close%">%fa:times%</button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:gamepad%%i18n:@game%</span>
 | 
			
		||||
	<mk-reversi :class="$style.content" @gamed="g => game = g"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
 
 | 
			
		||||
@@ -237,6 +237,10 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		warp(date) {
 | 
			
		||||
			(this.$refs.tl as any).warp(date);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.tl as any).focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">
 | 
			
		||||
		%fa:i-cursor%{{ title }}
 | 
			
		||||
	</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ export default Vue.extend({
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				duration: 100,
 | 
			
		||||
				easing: 'linear',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide" @click="hide = false">
 | 
			
		||||
<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
 | 
			
		||||
	<div>
 | 
			
		||||
		<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
 | 
			
		||||
		<span>%i18n:@click-to-show%</span>
 | 
			
		||||
@@ -27,12 +27,13 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
		raw: {
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		hide: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hide: true
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		style(): any {
 | 
			
		||||
			return {
 | 
			
		||||
@@ -89,7 +90,7 @@ export default Vue.extend({
 | 
			
		||||
		text-align center
 | 
			
		||||
		font-size 12px
 | 
			
		||||
 | 
			
		||||
		> b
 | 
			
		||||
		> *
 | 
			
		||||
			display block
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ export default Vue.extend({
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				duration: 100,
 | 
			
		||||
				easing: 'linear',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,12 +36,13 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
		inlinePlayable: {
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		hide: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hide: true
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		imageStyle(): any {
 | 
			
		||||
			return {
 | 
			
		||||
@@ -79,7 +80,6 @@ export default Vue.extend({
 | 
			
		||||
		justify-content center
 | 
			
		||||
		align-items center
 | 
			
		||||
		font-size 3.5em
 | 
			
		||||
 | 
			
		||||
		cursor zoom-in
 | 
			
		||||
		overflow hidden
 | 
			
		||||
		background-position center
 | 
			
		||||
@@ -101,5 +101,4 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		> b
 | 
			
		||||
			display block
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:comments%%i18n:@title% {{ user | userName }}</span>
 | 
			
		||||
	<mk-messaging-room :user="user" :class="$style.content"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:comments%%i18n:@title%</span>
 | 
			
		||||
	<mk-messaging :class="$style.content" @navigate="navigate"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="note" tabindex="-1" :title="title" @keydown="onKeydown">
 | 
			
		||||
<div class="note" tabindex="-1" v-hotkey="keymap" :title="title">
 | 
			
		||||
	<div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
 | 
			
		||||
		<x-sub :note="p.reply"/>
 | 
			
		||||
	</div>
 | 
			
		||||
@@ -40,18 +40,18 @@
 | 
			
		||||
			</div>
 | 
			
		||||
			<footer>
 | 
			
		||||
				<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
 | 
			
		||||
				<button class="replyButton" @click="reply" title="%i18n:@reply%">
 | 
			
		||||
				<button class="replyButton" @click="reply()" title="%i18n:@reply%">
 | 
			
		||||
					<template v-if="p.reply">%fa:reply-all%</template>
 | 
			
		||||
					<template v-else>%fa:reply%</template>
 | 
			
		||||
					<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button class="renoteButton" @click="renote" title="%i18n:@renote%">
 | 
			
		||||
				<button class="renoteButton" @click="renote()" title="%i18n:@renote%">
 | 
			
		||||
					%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button class="reactionButton" :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:@add-reaction%">
 | 
			
		||||
				<button class="reactionButton" :class="{ reacted: p.myReaction != null }" @click="react()" ref="reactButton" title="%i18n:@add-reaction%">
 | 
			
		||||
					%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button @click="menu" ref="menuButton">
 | 
			
		||||
				<button @click="menu()" ref="menuButton">
 | 
			
		||||
					%fa:ellipsis-h%
 | 
			
		||||
				</button>
 | 
			
		||||
				<!-- <button title="%i18n:@detail">
 | 
			
		||||
@@ -111,6 +111,28 @@ export default Vue.extend({
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'r|left': () => this.reply(true),
 | 
			
		||||
				'e|a|plus': () => this.react(true),
 | 
			
		||||
				'q|right': () => this.renote(true),
 | 
			
		||||
				'ctrl+q|ctrl+right': this.renoteDirectly,
 | 
			
		||||
				'up|k|shift+tab': this.focusBefore,
 | 
			
		||||
				'down|j|tab': this.focusAfter,
 | 
			
		||||
				'm|o': () => this.menu(true),
 | 
			
		||||
				'1': () => this.reactDirectly('like'),
 | 
			
		||||
				'2': () => this.reactDirectly('love'),
 | 
			
		||||
				'3': () => this.reactDirectly('laugh'),
 | 
			
		||||
				'4': () => this.reactDirectly('hmm'),
 | 
			
		||||
				'5': () => this.reactDirectly('surprise'),
 | 
			
		||||
				'6': () => this.reactDirectly('congrats'),
 | 
			
		||||
				'7': () => this.reactDirectly('angry'),
 | 
			
		||||
				'8': () => this.reactDirectly('confused'),
 | 
			
		||||
				'9': () => this.reactDirectly('rip'),
 | 
			
		||||
				'0': () => this.reactDirectly('pudding'),
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		isRenote(): boolean {
 | 
			
		||||
			return (this.note.renote &&
 | 
			
		||||
				this.note.text == null &&
 | 
			
		||||
@@ -220,67 +242,65 @@ export default Vue.extend({
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		reply() {
 | 
			
		||||
		reply(viaKeyboard = false) {
 | 
			
		||||
			(this as any).os.new(MkPostFormWindow, {
 | 
			
		||||
				reply: this.p
 | 
			
		||||
			});
 | 
			
		||||
				reply: this.p,
 | 
			
		||||
				animation: !viaKeyboard
 | 
			
		||||
			}).$once('closed', this.focus);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		renote() {
 | 
			
		||||
		renote(viaKeyboard = false) {
 | 
			
		||||
			(this as any).os.new(MkRenoteFormWindow, {
 | 
			
		||||
				note: this.p
 | 
			
		||||
				note: this.p,
 | 
			
		||||
				animation: !viaKeyboard
 | 
			
		||||
			}).$once('closed', this.focus);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		renoteDirectly() {
 | 
			
		||||
			(this as any).api('notes/create', {
 | 
			
		||||
				renoteId: this.p.id
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		react() {
 | 
			
		||||
		react(viaKeyboard = false) {
 | 
			
		||||
			this.blur();
 | 
			
		||||
			(this as any).os.new(MkReactionPicker, {
 | 
			
		||||
				source: this.$refs.reactButton,
 | 
			
		||||
				note: this.p
 | 
			
		||||
				note: this.p,
 | 
			
		||||
				showFocus: viaKeyboard,
 | 
			
		||||
				animation: !viaKeyboard
 | 
			
		||||
			}).$once('closed', this.focus);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		reactDirectly(reaction) {
 | 
			
		||||
			(this as any).api('notes/reactions/create', {
 | 
			
		||||
				noteId: this.p.id,
 | 
			
		||||
				reaction: reaction
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		menu() {
 | 
			
		||||
		menu(viaKeyboard = false) {
 | 
			
		||||
			(this as any).os.new(MkNoteMenu, {
 | 
			
		||||
				source: this.$refs.menuButton,
 | 
			
		||||
				note: this.p
 | 
			
		||||
			});
 | 
			
		||||
				note: this.p,
 | 
			
		||||
				animation: !viaKeyboard
 | 
			
		||||
			}).$once('closed', this.focus);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onKeydown(e) {
 | 
			
		||||
			let shouldBeCancel = true;
 | 
			
		||||
		focus() {
 | 
			
		||||
			this.$el.focus();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
			switch (true) {
 | 
			
		||||
				case e.which == 38: // [↑]
 | 
			
		||||
				case e.which == 74: // [j]
 | 
			
		||||
				case e.which == 9 && e.shiftKey: // [Shift] + [Tab]
 | 
			
		||||
		blur() {
 | 
			
		||||
			this.$el.blur();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focusBefore() {
 | 
			
		||||
			focus(this.$el, e => e.previousElementSibling);
 | 
			
		||||
					break;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
				case e.which == 40: // [↓]
 | 
			
		||||
				case e.which == 75: // [k]
 | 
			
		||||
				case e.which == 9: // [Tab]
 | 
			
		||||
		focusAfter() {
 | 
			
		||||
			focus(this.$el, e => e.nextElementSibling);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case e.which == 81: // [q]
 | 
			
		||||
				case e.which == 69: // [e]
 | 
			
		||||
					this.renote();
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case e.which == 70: // [f]
 | 
			
		||||
				case e.which == 76: // [l]
 | 
			
		||||
					//this.like();
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case e.which == 82: // [r]
 | 
			
		||||
					this.reply();
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					shouldBeCancel = false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (shouldBeCancel) e.preventDefault();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,15 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<!--<transition-group name="mk-notes" class="transition">-->
 | 
			
		||||
	<div class="notes">
 | 
			
		||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes">
 | 
			
		||||
		<template v-for="(note, i) in _notes">
 | 
			
		||||
			<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
 | 
			
		||||
			<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)" ref="note"/>
 | 
			
		||||
			<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
 | 
			
		||||
				<span>%fa:angle-up%{{ note._datetext }}</span>
 | 
			
		||||
				<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
 | 
			
		||||
			</p>
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
	<!--</transition-group>-->
 | 
			
		||||
	</component>
 | 
			
		||||
 | 
			
		||||
	<footer v-if="more">
 | 
			
		||||
		<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
 | 
			
		||||
@@ -91,7 +89,7 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$el as any).children[0].focus();
 | 
			
		||||
			(this.$refs.notes as any).children[0].focus ? (this.$refs.notes as any).children[0].focus() : (this.$refs.notes as any).$el.children[0].focus();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onNoteUpdated(i, note) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,7 @@
 | 
			
		||||
<div class="mk-notifications">
 | 
			
		||||
	<div class="notifications" v-if="notifications.length != 0">
 | 
			
		||||
		<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
		<!-- <transition-group name="mk-notifications" class="transition"> -->
 | 
			
		||||
		<div>
 | 
			
		||||
		<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div">
 | 
			
		||||
			<template v-for="(notification, i) in _notifications">
 | 
			
		||||
				<div class="notification" :class="notification.type" :key="notification.id">
 | 
			
		||||
					<mk-time :time="notification.createdAt"/>
 | 
			
		||||
@@ -97,8 +96,7 @@
 | 
			
		||||
					<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
 | 
			
		||||
				</p>
 | 
			
		||||
			</template>
 | 
			
		||||
		</div>
 | 
			
		||||
		<!-- </transition-group> -->
 | 
			
		||||
		</component>
 | 
			
		||||
	</div>
 | 
			
		||||
	<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
 | 
			
		||||
		<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window class="mk-post-form-window" ref="window" is-modal @closed="$destroy">
 | 
			
		||||
<mk-window class="mk-post-form-window" ref="window" is-modal @closed="onWindowClosed" :animation="animation">
 | 
			
		||||
	<span slot="header" class="mk-post-form-window--header">
 | 
			
		||||
		<span class="icon" v-if="geo">%fa:map-marker-alt%</span>
 | 
			
		||||
		<span v-if="!reply">%i18n:@note%</span>
 | 
			
		||||
@@ -25,7 +25,19 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['reply'],
 | 
			
		||||
	props: {
 | 
			
		||||
		reply: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		animation: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			uploadings: [],
 | 
			
		||||
@@ -33,11 +45,13 @@ export default Vue.extend({
 | 
			
		||||
			geo: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			(this.$refs.form as any).focus();
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		onChangeUploadings(files) {
 | 
			
		||||
			this.uploadings = files;
 | 
			
		||||
@@ -53,6 +67,10 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
		onPosted() {
 | 
			
		||||
			(this.$refs.window as any).close();
 | 
			
		||||
		},
 | 
			
		||||
		onWindowClosed() {
 | 
			
		||||
			this.$emit('closed');
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@
 | 
			
		||||
	<button :class="{ posting }" class="submit" :disabled="!canPost" @click="post">
 | 
			
		||||
		{{ posting ? '%i18n:@posting%' : submitText }}<mk-ellipsis v-if="posting"/>
 | 
			
		||||
	</button>
 | 
			
		||||
	<input ref="file" type="file" accept="image/*" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
 | 
			
		||||
	<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
 | 
			
		||||
	<div class="dropzone" v-if="draghover"></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" :is-modal="false" :can-close="false" width="500px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" :is-modal="false" :can-close="false" width="500px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header">{{ title }}<mk-ellipsis/></span>
 | 
			
		||||
	<div :class="$style.body">
 | 
			
		||||
		<p :class="$style.init" v-if="isNaN(value)">%i18n:@waiting%<mk-ellipsis/></p>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header">%fa:envelope R% %i18n:@title%</span>
 | 
			
		||||
 | 
			
		||||
	<div class="slpqaxdoxhvglersgjukmvizkqbmbokc" :data-darkmode="$store.state.device.darkmode">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal @closed="onWindowClosed" :animation="animation">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:retweet%%i18n:@title%</span>
 | 
			
		||||
	<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled"/>
 | 
			
		||||
	<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled" v-hotkey.global="keymap"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -9,26 +9,48 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['note'],
 | 
			
		||||
	mounted() {
 | 
			
		||||
		document.addEventListener('keydown', this.onDocumentKeydown);
 | 
			
		||||
	props: {
 | 
			
		||||
		note: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		document.removeEventListener('keydown', this.onDocumentKeydown);
 | 
			
		||||
 | 
			
		||||
		animation: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'esc': this.close,
 | 
			
		||||
				'enter': this.post,
 | 
			
		||||
				'q': this.quote,
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		onDocumentKeydown(e) {
 | 
			
		||||
			if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
 | 
			
		||||
				if (e.which == 27) { // Esc
 | 
			
		||||
		post() {
 | 
			
		||||
			(this.$refs.form as any).ok();
 | 
			
		||||
		},
 | 
			
		||||
		quote() {
 | 
			
		||||
			(this.$refs.form as any).onQuote();
 | 
			
		||||
		},
 | 
			
		||||
		close() {
 | 
			
		||||
			(this.$refs.window as any).close();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		onPosted() {
 | 
			
		||||
			(this.$refs.window as any).close();
 | 
			
		||||
		},
 | 
			
		||||
		onCanceled() {
 | 
			
		||||
			(this.$refs.window as any).close();
 | 
			
		||||
		},
 | 
			
		||||
		onWindowClosed() {
 | 
			
		||||
			this.$emit('closed');
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,19 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="700px" height="550px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal width="700px" height="550px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:cog%%i18n:@settings%</span>
 | 
			
		||||
	<mk-settings @done="close"/>
 | 
			
		||||
	<mk-settings :initial-page="initialPage" @done="close"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		initialPage: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		close() {
 | 
			
		||||
			(this as any).$refs.window.close();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="root">
 | 
			
		||||
	<template v-if="!fetching">
 | 
			
		||||
		<el-progress :text-inside="true" :stroke-width="18" :percentage="Math.floor((usage / capacity) * 100)"/>
 | 
			
		||||
		<p><b>{{ capacity | bytes }}</b>%i18n:max%<b>{{ usage | bytes }}</b>%i18n:in-use%</p>
 | 
			
		||||
	</template>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
	</label>
 | 
			
		||||
	<label class="ui from group">
 | 
			
		||||
		<p>%i18n:@birthday%</p>
 | 
			
		||||
		<el-date-picker v-model="birthday" type="date" value-format="yyyy-MM-dd"/>
 | 
			
		||||
		<input type="date" v-model="birthday"/>
 | 
			
		||||
	</label>
 | 
			
		||||
	<button class="ui primary" @click="save">%i18n:@save%</button>
 | 
			
		||||
	<section>
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
		<h2>%i18n:@other%</h2>
 | 
			
		||||
		<mk-switch v-model="$store.state.i.isBot" @change="onChangeIsBot" text="%i18n:@is-bot%"/>
 | 
			
		||||
		<mk-switch v-model="$store.state.i.isCat" @change="onChangeIsCat" text="%i18n:@is-cat%"/>
 | 
			
		||||
		<mk-switch v-model="alwaysMarkNsfw" text="%i18n:common.always-mark-nsfw%"/>
 | 
			
		||||
	</section>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -46,6 +47,12 @@ export default Vue.extend({
 | 
			
		||||
			birthday: null,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		alwaysMarkNsfw: {
 | 
			
		||||
			get() { return this.$store.state.i.settings.alwaysMarkNsfw; },
 | 
			
		||||
			set(value) { (this as any).api('i/update', { alwaysMarkNsfw: value }); }
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		this.name = this.$store.state.i.name || '';
 | 
			
		||||
		this.location = this.$store.state.i.profile.location;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										65
									
								
								src/client/app/desktop/views/components/settings.tags.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/client/app/desktop/views/components/settings.tags.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="vfcitkilproprqtbnpoertpsziierwzi">
 | 
			
		||||
	<div v-for="timeline in timelines" class="timeline">
 | 
			
		||||
		<ui-input v-model="timeline.title" @change="save">
 | 
			
		||||
			<span>%i18n:@title%</span>
 | 
			
		||||
		</ui-input>
 | 
			
		||||
		<ui-textarea :value="timeline.query ? timeline.query.map(tags => tags.join(' ')).join('\n') : ''" @input="onQueryChange(timeline, $event)">
 | 
			
		||||
			<span>%i18n:@query%</span>
 | 
			
		||||
		</ui-textarea>
 | 
			
		||||
		<ui-button class="save" @click="save">%i18n:@save%</ui-button>
 | 
			
		||||
	</div>
 | 
			
		||||
	<ui-button class="add" @click="add">%i18n:@add%</ui-button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as uuid from 'uuid';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			timelines: this.$store.state.settings.tagTimelines
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		add() {
 | 
			
		||||
			this.timelines.push({
 | 
			
		||||
				id: uuid(),
 | 
			
		||||
				title: '',
 | 
			
		||||
				query: ''
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.save();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		save() {
 | 
			
		||||
			this.$store.dispatch('settings/set', { key: 'tagTimelines', value: this.timelines });
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onQueryChange(timeline, value) {
 | 
			
		||||
			timeline.query = value.split('\n').map(tags => tags.split(' '));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
 | 
			
		||||
root(isDark)
 | 
			
		||||
	> .timeline
 | 
			
		||||
		padding-bottom 16px
 | 
			
		||||
		border-bottom solid 1px rgba(#000, 0.1)
 | 
			
		||||
 | 
			
		||||
	> .add
 | 
			
		||||
		margin-top 16px
 | 
			
		||||
 | 
			
		||||
.vfcitkilproprqtbnpoertpsziierwzi[data-darkmode]
 | 
			
		||||
	root(true)
 | 
			
		||||
 | 
			
		||||
.vfcitkilproprqtbnpoertpsziierwzi:not([data-darkmode])
 | 
			
		||||
	root(false)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
		<p :class="{ active: page == 'web' }" @mousedown="page = 'web'">%fa:desktop .fw%Web</p>
 | 
			
		||||
		<p :class="{ active: page == 'notification' }" @mousedown="page = 'notification'">%fa:R bell .fw%%i18n:@notification%</p>
 | 
			
		||||
		<p :class="{ active: page == 'drive' }" @mousedown="page = 'drive'">%fa:cloud .fw%%i18n:@drive%</p>
 | 
			
		||||
		<p :class="{ active: page == 'hashtags' }" @mousedown="page = 'hashtags'">%fa:hashtag .fw%%i18n:@tags%</p>
 | 
			
		||||
		<p :class="{ active: page == 'mute' }" @mousedown="page = 'mute'">%fa:ban .fw%%i18n:@mute%</p>
 | 
			
		||||
		<p :class="{ active: page == 'apps' }" @mousedown="page = 'apps'">%fa:puzzle-piece .fw%%i18n:@apps%</p>
 | 
			
		||||
		<p :class="{ active: page == 'twitter' }" @mousedown="page = 'twitter'">%fa:B twitter .fw%Twitter</p>
 | 
			
		||||
@@ -20,7 +21,7 @@
 | 
			
		||||
 | 
			
		||||
		<section class="web" v-show="page == 'web'">
 | 
			
		||||
			<h1>%i18n:@behaviour%</h1>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll" text="%i18n:@fetch-on-scroll%">
 | 
			
		||||
			<mk-switch v-model="fetchOnScroll" text="%i18n:@fetch-on-scroll%">
 | 
			
		||||
				<span>%i18n:@fetch-on-scroll-desc%</span>
 | 
			
		||||
			</mk-switch>
 | 
			
		||||
			<mk-switch v-model="autoPopout" text="%i18n:@auto-popout%">
 | 
			
		||||
@@ -29,7 +30,7 @@
 | 
			
		||||
 | 
			
		||||
			<section>
 | 
			
		||||
				<header>%i18n:@note-visibility%</header>
 | 
			
		||||
				<mk-switch v-model="$store.state.settings.rememberNoteVisibility" @change="onChangeRememberNoteVisibility" text="%i18n:@remember-note-visibility%"/>
 | 
			
		||||
				<mk-switch v-model="rememberNoteVisibility" text="%i18n:@remember-note-visibility%"/>
 | 
			
		||||
				<section>
 | 
			
		||||
					<header>%i18n:@default-note-visibility%</header>
 | 
			
		||||
					<ui-select v-model="defaultNoteVisibility">
 | 
			
		||||
@@ -59,24 +60,27 @@
 | 
			
		||||
				<button class="ui" @click="updateWallpaper">%i18n:@choose-wallpaper%</button>
 | 
			
		||||
				<button class="ui" @click="deleteWallpaper">%i18n:@delete-wallpaper%</button>
 | 
			
		||||
				<mk-switch v-model="darkmode" text="%i18n:@dark-mode%"/>
 | 
			
		||||
				<mk-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons" text="%i18n:@circle-icons%"/>
 | 
			
		||||
				<mk-switch v-model="$store.state.settings.contrastedAcct" @change="onChangeContrastedAcct" text="%i18n:@contrasted-acct%"/>
 | 
			
		||||
				<mk-switch v-model="$store.state.settings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="%i18n:@gradient-window-header%"/>
 | 
			
		||||
				<mk-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi" text="%i18n:common.i-like-sushi%"/>
 | 
			
		||||
				<mk-switch v-model="circleIcons" text="%i18n:@circle-icons%"/>
 | 
			
		||||
				<mk-switch v-model="reduceMotion" text="%i18n:common.reduce-motion%"/>
 | 
			
		||||
				<mk-switch v-model="contrastedAcct" text="%i18n:@contrasted-acct%"/>
 | 
			
		||||
				<mk-switch v-model="showFullAcct" text="%i18n:common.show-full-acct%"/>
 | 
			
		||||
				<mk-switch v-model="gradientWindowHeader" text="%i18n:@gradient-window-header%"/>
 | 
			
		||||
				<mk-switch v-model="iLikeSushi" text="%i18n:common.i-like-sushi%"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.suggestRecentHashtags" @change="onChangeSuggestRecentHashtags" text="%i18n:@suggest-recent-hashtags%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showClockOnHeader" @change="onChangeShowClockOnHeader" text="%i18n:@show-clock-on-header%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget" text="%i18n:@show-reply-target%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes" text="%i18n:@show-my-renotes%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showLocalRenotes" @change="onChangeShowLocalRenotes" text="%i18n:@show-local-renotes%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
 | 
			
		||||
			<mk-switch v-model="showPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
 | 
			
		||||
			<mk-switch v-model="suggestRecentHashtags" text="%i18n:@suggest-recent-hashtags%"/>
 | 
			
		||||
			<mk-switch v-model="showClockOnHeader" text="%i18n:@show-clock-on-header%"/>
 | 
			
		||||
			<mk-switch v-model="alwaysShowNsfw" text="%i18n:common.always-show-nsfw%"/>
 | 
			
		||||
			<mk-switch v-model="showReplyTarget" text="%i18n:@show-reply-target%"/>
 | 
			
		||||
			<mk-switch v-model="showMyRenotes" text="%i18n:@show-my-renotes%"/>
 | 
			
		||||
			<mk-switch v-model="showRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
 | 
			
		||||
			<mk-switch v-model="showLocalRenotes" text="%i18n:@show-local-renotes%"/>
 | 
			
		||||
			<mk-switch v-model="showMaps" text="%i18n:@show-maps%">
 | 
			
		||||
				<span>%i18n:@show-maps-desc%</span>
 | 
			
		||||
			</mk-switch>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones" text="%i18n:common.use-contrast-reversi-stones%"/>
 | 
			
		||||
			<mk-switch v-model="disableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
 | 
			
		||||
			<mk-switch v-model="games_reversi_showBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
 | 
			
		||||
			<mk-switch v-model="games_reversi_useContrastStones" text="%i18n:common.use-contrast-reversi-stones%"/>
 | 
			
		||||
		</section>
 | 
			
		||||
 | 
			
		||||
		<section class="web" v-show="page == 'web'">
 | 
			
		||||
@@ -85,32 +89,31 @@
 | 
			
		||||
				<span>%i18n:@enable-sounds-desc%</span>
 | 
			
		||||
			</mk-switch>
 | 
			
		||||
			<label>%i18n:@volume%</label>
 | 
			
		||||
			<el-slider
 | 
			
		||||
			<input type="range"
 | 
			
		||||
				v-model="soundVolume"
 | 
			
		||||
				:show-input="true"
 | 
			
		||||
				:format-tooltip="v => `${v * 100}%`"
 | 
			
		||||
				:disabled="!enableSounds"
 | 
			
		||||
				:max="1"
 | 
			
		||||
				:step="0.1"
 | 
			
		||||
				max="1"
 | 
			
		||||
				step="0.1"
 | 
			
		||||
			/>
 | 
			
		||||
			<button class="ui button" @click="soundTest">%fa:volume-up% %i18n:@test%</button>
 | 
			
		||||
		</section>
 | 
			
		||||
 | 
			
		||||
		<section class="web" v-show="page == 'web'">
 | 
			
		||||
			<h1>%i18n:@mobile%</h1>
 | 
			
		||||
			<mk-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile" text="%i18n:@disable-via-mobile%"/>
 | 
			
		||||
			<mk-switch v-model="disableViaMobile" text="%i18n:@disable-via-mobile%"/>
 | 
			
		||||
		</section>
 | 
			
		||||
 | 
			
		||||
		<section class="web" v-show="page == 'web'">
 | 
			
		||||
			<h1>%i18n:@language%</h1>
 | 
			
		||||
			<el-select v-model="lang" placeholder="%i18n:@pick-language%">
 | 
			
		||||
				<el-option-group label="%i18n:@recommended%">
 | 
			
		||||
					<el-option label="%i18n:@auto%" :value="null"/>
 | 
			
		||||
				</el-option-group>
 | 
			
		||||
				<el-option-group label="%i18n:@specify-language%">
 | 
			
		||||
					<el-option v-for="x in langs" :label="x[1]" :value="x[0]" :key="x[0]"/>
 | 
			
		||||
				</el-option-group>
 | 
			
		||||
			</el-select>
 | 
			
		||||
			<select v-model="lang" placeholder="%i18n:@pick-language%">
 | 
			
		||||
				<optgroup label="%i18n:@recommended%">
 | 
			
		||||
					<option value="">%i18n:@auto%</option>
 | 
			
		||||
				</optgroup>
 | 
			
		||||
 | 
			
		||||
				<optgroup label="%i18n:@specify-language%">
 | 
			
		||||
					<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
 | 
			
		||||
				</optgroup>
 | 
			
		||||
			</select>
 | 
			
		||||
			<div class="none ui info">
 | 
			
		||||
				<p>%fa:info-circle%%i18n:@language-desc%</p>
 | 
			
		||||
			</div>
 | 
			
		||||
@@ -136,6 +139,11 @@
 | 
			
		||||
			<x-drive/>
 | 
			
		||||
		</section>
 | 
			
		||||
 | 
			
		||||
		<section class="hashtags" v-show="page == 'hashtags'">
 | 
			
		||||
			<h1>%i18n:@tags%</h1>
 | 
			
		||||
			<x-tags/>
 | 
			
		||||
		</section>
 | 
			
		||||
 | 
			
		||||
		<section class="mute" v-show="page == 'mute'">
 | 
			
		||||
			<h1>%i18n:@mute%</h1>
 | 
			
		||||
			<x-mute/>
 | 
			
		||||
@@ -205,10 +213,6 @@
 | 
			
		||||
			<mk-switch v-model="enableExperimentalFeatures" text="%i18n:@experimental%">
 | 
			
		||||
				<span>%i18n:@experimental-desc%</span>
 | 
			
		||||
			</mk-switch>
 | 
			
		||||
			<details v-if="debug">
 | 
			
		||||
				<summary>%i18n:@tools%</summary>
 | 
			
		||||
				<button class="ui button block" @click="taskmngr">%i18n:@task-manager%</button>
 | 
			
		||||
			</details>
 | 
			
		||||
		</section>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -224,9 +228,9 @@ import XApi from './settings.api.vue';
 | 
			
		||||
import XApps from './settings.apps.vue';
 | 
			
		||||
import XSignins from './settings.signins.vue';
 | 
			
		||||
import XDrive from './settings.drive.vue';
 | 
			
		||||
import XTags from './settings.tags.vue';
 | 
			
		||||
import { url, langs, version } from '../../../config';
 | 
			
		||||
import checkForUpdate from '../../../common/scripts/check-for-update';
 | 
			
		||||
import MkTaskManager from './taskmanager.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -237,11 +241,18 @@ export default Vue.extend({
 | 
			
		||||
		XApi,
 | 
			
		||||
		XApps,
 | 
			
		||||
		XSignins,
 | 
			
		||||
		XDrive
 | 
			
		||||
		XDrive,
 | 
			
		||||
		XTags
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		initialPage: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			page: 'profile',
 | 
			
		||||
			page: this.initialPage || 'profile',
 | 
			
		||||
			meta: null,
 | 
			
		||||
			version,
 | 
			
		||||
			langs,
 | 
			
		||||
@@ -250,16 +261,16 @@ export default Vue.extend({
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		reduceMotion: {
 | 
			
		||||
			get() { return this.$store.state.device.reduceMotion; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'reduceMotion', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		apiViaStream: {
 | 
			
		||||
			get() { return this.$store.state.device.apiViaStream; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'apiViaStream', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		defaultNoteVisibility: {
 | 
			
		||||
			get() { return this.$store.state.settings.defaultNoteVisibility; },
 | 
			
		||||
			set(value) { this.$store.commit('settings/set', { key: 'defaultNoteVisibility', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		autoPopout: {
 | 
			
		||||
			get() { return this.$store.state.device.autoPopout; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'autoPopout', value }); }
 | 
			
		||||
@@ -298,7 +309,112 @@ export default Vue.extend({
 | 
			
		||||
		enableExperimentalFeatures: {
 | 
			
		||||
			get() { return this.$store.state.device.enableExperimentalFeatures; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'enableExperimentalFeatures', value }); }
 | 
			
		||||
		}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		alwaysShowNsfw: {
 | 
			
		||||
			get() { return this.$store.state.device.alwaysShowNsfw; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'alwaysShowNsfw', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		fetchOnScroll: {
 | 
			
		||||
			get() { return this.$store.state.settings.fetchOnScroll; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'fetchOnScroll', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		rememberNoteVisibility: {
 | 
			
		||||
			get() { return this.$store.state.settings.rememberNoteVisibility; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'rememberNoteVisibility', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		defaultNoteVisibility: {
 | 
			
		||||
			get() { return this.$store.state.settings.defaultNoteVisibility; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showReplyTarget: {
 | 
			
		||||
			get() { return this.$store.state.settings.showReplyTarget; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showReplyTarget', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showMyRenotes: {
 | 
			
		||||
			get() { return this.$store.state.settings.showMyRenotes; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showMyRenotes', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showRenotedMyNotes: {
 | 
			
		||||
			get() { return this.$store.state.settings.showRenotedMyNotes; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showRenotedMyNotes', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showLocalRenotes: {
 | 
			
		||||
			get() { return this.$store.state.settings.showLocalRenotes; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showLocalRenotes', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showPostFormOnTopOfTl: {
 | 
			
		||||
			get() { return this.$store.state.settings.showPostFormOnTopOfTl; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showPostFormOnTopOfTl', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		suggestRecentHashtags: {
 | 
			
		||||
			get() { return this.$store.state.settings.suggestRecentHashtags; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'suggestRecentHashtags', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showClockOnHeader: {
 | 
			
		||||
			get() { return this.$store.state.settings.showClockOnHeader; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showClockOnHeader', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showMaps: {
 | 
			
		||||
			get() { return this.$store.state.settings.showMaps; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showMaps', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		circleIcons: {
 | 
			
		||||
			get() { return this.$store.state.settings.circleIcons; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'circleIcons', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		contrastedAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.contrastedAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'contrastedAcct', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showFullAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.showFullAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showFullAcct', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		iLikeSushi: {
 | 
			
		||||
			get() { return this.$store.state.settings.iLikeSushi; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'iLikeSushi', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		games_reversi_showBoardLabels: {
 | 
			
		||||
			get() { return this.$store.state.settings.games.reversi.showBoardLabels; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'games.reversi.showBoardLabels', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		games_reversi_useContrastStones: {
 | 
			
		||||
			get() { return this.$store.state.settings.games.reversi.useContrastStones; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'games.reversi.useContrastStones', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disableAnimatedMfm: {
 | 
			
		||||
			get() { return this.$store.state.settings.disableAnimatedMfm; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disableViaMobile: {
 | 
			
		||||
			get() { return this.$store.state.settings.disableViaMobile; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'disableViaMobile', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		gradientWindowHeader: {
 | 
			
		||||
			get() { return this.$store.state.settings.gradientWindowHeader; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'gradientWindowHeader', value }); }
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
@@ -306,9 +422,6 @@ export default Vue.extend({
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		taskmngr() {
 | 
			
		||||
			(this as any).os.new(MkTaskManager);
 | 
			
		||||
		},
 | 
			
		||||
		customizeHome() {
 | 
			
		||||
			this.$router.push('/i/customize-home');
 | 
			
		||||
			this.$emit('done');
 | 
			
		||||
@@ -327,125 +440,11 @@ export default Vue.extend({
 | 
			
		||||
				wallpaperId: null
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeFetchOnScroll(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'fetchOnScroll',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeRememberNoteVisibility(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'rememberNoteVisibility',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeAutoWatch(v) {
 | 
			
		||||
			(this as any).api('i/update', {
 | 
			
		||||
				autoWatch: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeDark(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'dark',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowPostFormOnTopOfTl(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showPostFormOnTopOfTl',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeSuggestRecentHashtags(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'suggestRecentHashtags',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowClockOnHeader(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showClockOnHeader',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowReplyTarget(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showReplyTarget',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowMyRenotes(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showMyRenotes',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowRenotedMyNotes(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showRenotedMyNotes',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowLocalRenotes(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showLocalRenotes',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeShowMaps(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showMaps',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeCircleIcons(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'circleIcons',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeContrastedAcct(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'contrastedAcct',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeILikeSushi(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'iLikeSushi',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeReversiBoardLabels(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'games.reversi.showBoardLabels',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeUseContrastReversiStones(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'games.reversi.useContrastStones',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeDisableAnimatedMfm(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'disableAnimatedMfm',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeGradientWindowHeader(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'gradientWindowHeader',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onChangeDisableViaMobile(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'disableViaMobile',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		checkForUpdate() {
 | 
			
		||||
			this.checkingForUpdate = true;
 | 
			
		||||
			checkForUpdate((this as any).os, true, true).then(newer => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,219 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" width="750px" height="500px" @closed="$destroy" name="TaskManager">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:stethoscope%%i18n:@title%</span>
 | 
			
		||||
	<el-tabs :class="$style.content">
 | 
			
		||||
		<el-tab-pane label="Requests">
 | 
			
		||||
			<el-table
 | 
			
		||||
				:data="os.requests"
 | 
			
		||||
				style="width: 100%"
 | 
			
		||||
				:default-sort="{prop: 'date', order: 'descending'}"
 | 
			
		||||
			>
 | 
			
		||||
				<el-table-column type="expand">
 | 
			
		||||
					<template slot-scope="props">
 | 
			
		||||
						<pre>{{ props.row.data }}</pre>
 | 
			
		||||
						<pre>{{ props.row.res }}</pre>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Requested at"
 | 
			
		||||
					prop="date"
 | 
			
		||||
					sortable
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<b style="margin-right: 8px">{{ scope.row.date.getTime() }}</b>
 | 
			
		||||
						<span>(<mk-time :time="scope.row.date"/>)</span>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Name"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<b>{{ scope.row.name }}</b>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Status"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<span>{{ scope.row.status || '(pending)' }}</span>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
			</el-table>
 | 
			
		||||
		</el-tab-pane>
 | 
			
		||||
 | 
			
		||||
		<el-tab-pane label="Streams">
 | 
			
		||||
			<el-table
 | 
			
		||||
				:data="os.connections"
 | 
			
		||||
				style="width: 100%"
 | 
			
		||||
			>
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Uptime"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<mk-timer v-if="scope.row.connectedAt" :time="scope.row.connectedAt"/>
 | 
			
		||||
						<span v-else>-</span>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Name"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<b>{{ scope.row.name == '' ? '[Home]' : scope.row.name }}</b>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="User"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<span>{{ scope.row.user || '(anonymous)' }}</span>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					prop="state"
 | 
			
		||||
					label="State"
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					prop="in"
 | 
			
		||||
					label="In"
 | 
			
		||||
				/>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					prop="out"
 | 
			
		||||
					label="Out"
 | 
			
		||||
				/>
 | 
			
		||||
			</el-table>
 | 
			
		||||
		</el-tab-pane>
 | 
			
		||||
 | 
			
		||||
		<el-tab-pane label="Streams (Inspect)">
 | 
			
		||||
			<el-tabs type="card" style="height:50%">
 | 
			
		||||
				<el-tab-pane v-for="c in os.connections" :label="c.name == '' ? '[Home]' : c.name" :key="c.id" :name="c.id" ref="connectionsTab">
 | 
			
		||||
					<div style="padding: 12px 0 0 12px">
 | 
			
		||||
					<el-button size="mini" @click="send(c)">Send</el-button>
 | 
			
		||||
					<el-button size="mini" type="warning" @click="c.isSuspended = true" v-if="!c.isSuspended">Suspend</el-button>
 | 
			
		||||
					<el-button size="mini" type="success" @click="c.isSuspended = false" v-else>Resume</el-button>
 | 
			
		||||
					<el-button size="mini" type="danger" @click="c.close">Disconnect</el-button>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
					<el-table
 | 
			
		||||
						:data="c.inout"
 | 
			
		||||
						style="width: 100%"
 | 
			
		||||
						:default-sort="{prop: 'at', order: 'descending'}"
 | 
			
		||||
					>
 | 
			
		||||
						<el-table-column type="expand">
 | 
			
		||||
							<template slot-scope="props">
 | 
			
		||||
								<pre>{{ props.row.data }}</pre>
 | 
			
		||||
							</template>
 | 
			
		||||
						</el-table-column>
 | 
			
		||||
 | 
			
		||||
						<el-table-column
 | 
			
		||||
							label="Date"
 | 
			
		||||
							prop="at"
 | 
			
		||||
							sortable
 | 
			
		||||
						>
 | 
			
		||||
							<template slot-scope="scope">
 | 
			
		||||
								<b style="margin-right: 8px">{{ scope.row.at.getTime() }}</b>
 | 
			
		||||
								<span>(<mk-time :time="scope.row.at"/>)</span>
 | 
			
		||||
							</template>
 | 
			
		||||
						</el-table-column>
 | 
			
		||||
 | 
			
		||||
						<el-table-column
 | 
			
		||||
							label="Type"
 | 
			
		||||
						>
 | 
			
		||||
							<template slot-scope="scope">
 | 
			
		||||
								<span>{{ getMessageType(scope.row.data) }}</span>
 | 
			
		||||
							</template>
 | 
			
		||||
						</el-table-column>
 | 
			
		||||
 | 
			
		||||
						<el-table-column
 | 
			
		||||
							label="Incoming / Outgoing"
 | 
			
		||||
							prop="type"
 | 
			
		||||
						/>
 | 
			
		||||
					</el-table>
 | 
			
		||||
				</el-tab-pane>
 | 
			
		||||
			</el-tabs>
 | 
			
		||||
		</el-tab-pane>
 | 
			
		||||
 | 
			
		||||
		<el-tab-pane label="Windows">
 | 
			
		||||
			<el-table
 | 
			
		||||
				:data="Array.from(os.windows.windows)"
 | 
			
		||||
				style="width: 100%"
 | 
			
		||||
			>
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Name"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<b>{{ scope.row.name || '(unknown)' }}</b>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
 | 
			
		||||
				<el-table-column
 | 
			
		||||
					label="Operations"
 | 
			
		||||
				>
 | 
			
		||||
					<template slot-scope="scope">
 | 
			
		||||
						<el-button size="mini" type="danger" @click="scope.row.close">Close</el-button>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-table-column>
 | 
			
		||||
			</el-table>
 | 
			
		||||
		</el-tab-pane>
 | 
			
		||||
	</el-tabs>
 | 
			
		||||
</mk-window>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	mounted() {
 | 
			
		||||
		(this as any).os.windows.on('added', this.onWindowsChanged);
 | 
			
		||||
		(this as any).os.windows.on('removed', this.onWindowsChanged);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		(this as any).os.windows.off('added', this.onWindowsChanged);
 | 
			
		||||
		(this as any).os.windows.off('removed', this.onWindowsChanged);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		getMessageType(data): string {
 | 
			
		||||
			return data.type ? data.type : '-';
 | 
			
		||||
		},
 | 
			
		||||
		onWindowsChanged() {
 | 
			
		||||
			this.$forceUpdate();
 | 
			
		||||
		},
 | 
			
		||||
		send(c) {
 | 
			
		||||
			(this as any).apis.input({
 | 
			
		||||
				title: 'Send a JSON message',
 | 
			
		||||
				allowEmpty: false
 | 
			
		||||
			}).then(json => {
 | 
			
		||||
				c.send(JSON.parse(json));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.header
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.content
 | 
			
		||||
	height 100%
 | 
			
		||||
	overflow auto
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
.el-tabs__header {
 | 
			
		||||
	margin-bottom: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-tabs__item {
 | 
			
		||||
	padding: 0 20px !important;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { HashtagStream } from '../../../common/scripts/streaming/hashtag';
 | 
			
		||||
 | 
			
		||||
const fetchLimit = 10;
 | 
			
		||||
 | 
			
		||||
@@ -23,6 +24,9 @@ export default Vue.extend({
 | 
			
		||||
		src: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		tagTl: {
 | 
			
		||||
			required: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -31,9 +35,17 @@ export default Vue.extend({
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			moreFetching: false,
 | 
			
		||||
			existMore: false,
 | 
			
		||||
			streamManager: null,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null,
 | 
			
		||||
			date: null
 | 
			
		||||
			date: null,
 | 
			
		||||
			baseQuery: {
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
			},
 | 
			
		||||
			query: {},
 | 
			
		||||
			endpoint: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -42,53 +54,109 @@ export default Vue.extend({
 | 
			
		||||
			return this.$store.state.i.followingCount == 0;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		stream(): any {
 | 
			
		||||
			switch (this.src) {
 | 
			
		||||
				case 'home': return (this as any).os.stream;
 | 
			
		||||
				case 'local': return (this as any).os.streams.localTimelineStream;
 | 
			
		||||
				case 'hybrid': return (this as any).os.streams.hybridTimelineStream;
 | 
			
		||||
				case 'global': return (this as any).os.streams.globalTimelineStream;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		endpoint(): string {
 | 
			
		||||
			switch (this.src) {
 | 
			
		||||
				case 'home': return 'notes/timeline';
 | 
			
		||||
				case 'local': return 'notes/local-timeline';
 | 
			
		||||
				case 'hybrid': return 'notes/hybrid-timeline';
 | 
			
		||||
				case 'global': return 'notes/global-timeline';
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		canFetchMore(): boolean {
 | 
			
		||||
			return !this.moreFetching && !this.fetching && this.existMore;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.stream.use();
 | 
			
		||||
		const prepend = note => {
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.connection.on('note', this.onNote);
 | 
			
		||||
		if (this.src == 'home') {
 | 
			
		||||
			this.connection.on('follow', this.onChangeFollowing);
 | 
			
		||||
			this.connection.on('unfollow', this.onChangeFollowing);
 | 
			
		||||
		if (this.src == 'tag') {
 | 
			
		||||
			this.endpoint = 'notes/search_by_tag';
 | 
			
		||||
			this.query = {
 | 
			
		||||
				query: this.tagTl.query
 | 
			
		||||
			};
 | 
			
		||||
			this.connection = new HashtagStream((this as any).os, this.$store.state.i, this.tagTl.query);
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.connection.close();
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'home') {
 | 
			
		||||
			this.endpoint = 'notes/timeline';
 | 
			
		||||
			const onChangeFollowing = () => {
 | 
			
		||||
				this.fetch();
 | 
			
		||||
			};
 | 
			
		||||
			this.streamManager = (this as any).os.stream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.connection.on('follow', onChangeFollowing);
 | 
			
		||||
			this.connection.on('unfollow', onChangeFollowing);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.connection.off('follow', onChangeFollowing);
 | 
			
		||||
				this.connection.off('unfollow', onChangeFollowing);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'local') {
 | 
			
		||||
			this.endpoint = 'notes/local-timeline';
 | 
			
		||||
			this.streamManager = (this as any).os.streams.localTimelineStream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'hybrid') {
 | 
			
		||||
			this.endpoint = 'notes/hybrid-timeline';
 | 
			
		||||
			this.streamManager = (this as any).os.streams.hybridTimelineStream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'global') {
 | 
			
		||||
			this.endpoint = 'notes/global-timeline';
 | 
			
		||||
			this.streamManager = (this as any).os.streams.globalTimelineStream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'mentions') {
 | 
			
		||||
			this.endpoint = 'notes/mentions';
 | 
			
		||||
			this.streamManager = (this as any).os.stream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('mention', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('mention', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'messages') {
 | 
			
		||||
			this.endpoint = 'notes/mentions';
 | 
			
		||||
			this.query = {
 | 
			
		||||
				visibility: 'specified'
 | 
			
		||||
			};
 | 
			
		||||
			const onNote = note => {
 | 
			
		||||
				if (note.visibility == 'specified') {
 | 
			
		||||
					prepend(note);
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			this.streamManager = (this as any).os.stream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('mention', onNote);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('mention', onNote);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		document.addEventListener('keydown', this.onKeydown);
 | 
			
		||||
 | 
			
		||||
		this.fetch();
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('note', this.onNote);
 | 
			
		||||
		if (this.src == 'home') {
 | 
			
		||||
			this.connection.off('follow', this.onChangeFollowing);
 | 
			
		||||
			this.connection.off('unfollow', this.onChangeFollowing);
 | 
			
		||||
		}
 | 
			
		||||
		this.stream.dispose(this.connectionId);
 | 
			
		||||
 | 
			
		||||
		document.removeEventListener('keydown', this.onKeydown);
 | 
			
		||||
		this.$emit('beforeDestroy');
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
@@ -96,13 +164,10 @@ export default Vue.extend({
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
			
		||||
				(this as any).api(this.endpoint, {
 | 
			
		||||
				(this as any).api(this.endpoint, Object.assign({
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					untilDate: this.date ? this.date.getTime() : undefined,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
				}).then(notes => {
 | 
			
		||||
					untilDate: this.date ? this.date.getTime() : undefined
 | 
			
		||||
				}, this.baseQuery, this.query)).then(notes => {
 | 
			
		||||
					if (notes.length == fetchLimit + 1) {
 | 
			
		||||
						notes.pop();
 | 
			
		||||
						this.existMore = true;
 | 
			
		||||
@@ -119,13 +184,10 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
 | 
			
		||||
			const promise = (this as any).api(this.endpoint, {
 | 
			
		||||
			const promise = (this as any).api(this.endpoint, Object.assign({
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
			});
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id
 | 
			
		||||
			}, this.baseQuery, this.query));
 | 
			
		||||
 | 
			
		||||
			promise.then(notes => {
 | 
			
		||||
				if (notes.length == fetchLimit + 1) {
 | 
			
		||||
@@ -140,15 +202,6 @@ export default Vue.extend({
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeFollowing() {
 | 
			
		||||
			this.fetch();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.timeline as any).focus();
 | 
			
		||||
		},
 | 
			
		||||
@@ -156,14 +209,6 @@ export default Vue.extend({
 | 
			
		||||
		warp(date) {
 | 
			
		||||
			this.date = date;
 | 
			
		||||
			this.fetch();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onKeydown(e) {
 | 
			
		||||
			if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
 | 
			
		||||
				if (e.which == 84) { // t
 | 
			
		||||
					this.focus();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,22 @@
 | 
			
		||||
		<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline">%fa:R comments% %i18n:@local%</span>
 | 
			
		||||
		<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline">%fa:share-alt% %i18n:@hybrid%</span>
 | 
			
		||||
		<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span>
 | 
			
		||||
		<span :data-active="src == 'tag'" @click="src = 'tag'" v-if="tagTl">%fa:hashtag% {{ tagTl.title }}</span>
 | 
			
		||||
		<span :data-active="src == 'list'" @click="src = 'list'" v-if="list">%fa:list% {{ list.title }}</span>
 | 
			
		||||
		<button @click="chooseList" title="%i18n:@list%">%fa:list%</button>
 | 
			
		||||
		<div class="buttons">
 | 
			
		||||
			<button :data-active="src == 'mentions'" @click="src = 'mentions'" title="%i18n:@mentions%">%fa:at%</button>
 | 
			
		||||
			<button :data-active="src == 'messages'" @click="src = 'messages'" title="%i18n:@messages%">%fa:envelope R%</button>
 | 
			
		||||
			<button @click="chooseTag" title="%i18n:@hashtag%" ref="tagButton">%fa:hashtag%</button>
 | 
			
		||||
			<button @click="chooseList" title="%i18n:@list%" ref="listButton">%fa:list%</button>
 | 
			
		||||
		</div>
 | 
			
		||||
	</header>
 | 
			
		||||
	<x-core v-if="src == 'home'" ref="tl" key="home" src="home"/>
 | 
			
		||||
	<x-core v-if="src == 'local'" ref="tl" key="local" src="local"/>
 | 
			
		||||
	<x-core v-if="src == 'hybrid'" ref="tl" key="hybrid" src="hybrid"/>
 | 
			
		||||
	<x-core v-if="src == 'global'" ref="tl" key="global" src="global"/>
 | 
			
		||||
	<x-core v-if="src == 'mentions'" ref="tl" key="mentions" src="mentions"/>
 | 
			
		||||
	<x-core v-if="src == 'messages'" ref="tl" key="messages" src="messages"/>
 | 
			
		||||
	<x-core v-if="src == 'tag'" ref="tl" key="tag" src="tag" :tag-tl="tagTl"/>
 | 
			
		||||
	<mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -19,7 +28,8 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XCore from './timeline.core.vue';
 | 
			
		||||
import MkUserListsWindow from './user-lists-window.vue';
 | 
			
		||||
import Menu from '../../../common/views/components/menu.vue';
 | 
			
		||||
import MkSettingsWindow from './settings-window.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
@@ -30,6 +40,7 @@ export default Vue.extend({
 | 
			
		||||
		return {
 | 
			
		||||
			src: 'home',
 | 
			
		||||
			list: null,
 | 
			
		||||
			tagTl: null,
 | 
			
		||||
			enableLocalTimeline: false
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
@@ -39,8 +50,14 @@ export default Vue.extend({
 | 
			
		||||
			this.saveSrc();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		list() {
 | 
			
		||||
		list(x) {
 | 
			
		||||
			this.saveSrc();
 | 
			
		||||
			if (x != null) this.tagTl = null;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		tagTl(x) {
 | 
			
		||||
			this.saveSrc();
 | 
			
		||||
			if (x != null) this.list = null;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +70,8 @@ export default Vue.extend({
 | 
			
		||||
			this.src = this.$store.state.device.tl.src;
 | 
			
		||||
			if (this.src == 'list') {
 | 
			
		||||
				this.list = this.$store.state.device.tl.arg;
 | 
			
		||||
			} else if (this.src == 'tag') {
 | 
			
		||||
				this.tagTl = this.$store.state.device.tl.arg;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (this.$store.state.i.followingCount == 0) {
 | 
			
		||||
			this.src = 'hybrid';
 | 
			
		||||
@@ -69,20 +88,86 @@ export default Vue.extend({
 | 
			
		||||
		saveSrc() {
 | 
			
		||||
			this.$store.commit('device/setTl', {
 | 
			
		||||
				src: this.src,
 | 
			
		||||
				arg: this.list
 | 
			
		||||
				arg: this.src == 'list' ? this.list : this.tagTl
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.tl as any).focus();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		warp(date) {
 | 
			
		||||
			(this.$refs.tl as any).warp(date);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		chooseList() {
 | 
			
		||||
			const w = (this as any).os.new(MkUserListsWindow);
 | 
			
		||||
			w.$once('choosen', list => {
 | 
			
		||||
		async chooseList() {
 | 
			
		||||
			const lists = await (this as any).api('users/lists/list');
 | 
			
		||||
 | 
			
		||||
			let menu = [{
 | 
			
		||||
				icon: '%fa:plus%',
 | 
			
		||||
				text: '%i18n:@add-list%',
 | 
			
		||||
				action: () => {
 | 
			
		||||
					(this as any).apis.input({
 | 
			
		||||
						title: '%i18n:@list-name%',
 | 
			
		||||
					}).then(async title => {
 | 
			
		||||
						const list = await (this as any).api('users/lists/create', {
 | 
			
		||||
							title
 | 
			
		||||
						});
 | 
			
		||||
 | 
			
		||||
						this.list = list;
 | 
			
		||||
						this.src = 'list';
 | 
			
		||||
				w.close();
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
 | 
			
		||||
			if (lists.length > 0) {
 | 
			
		||||
				menu.push(null);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			menu = menu.concat(lists.map(list => ({
 | 
			
		||||
				icon: '%fa:list%',
 | 
			
		||||
				text: list.title,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					this.list = list;
 | 
			
		||||
					this.src = 'list';
 | 
			
		||||
				}
 | 
			
		||||
			})));
 | 
			
		||||
 | 
			
		||||
			this.os.new(Menu, {
 | 
			
		||||
				source: this.$refs.listButton,
 | 
			
		||||
				compact: false,
 | 
			
		||||
				items: menu
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		chooseTag() {
 | 
			
		||||
			let menu = [{
 | 
			
		||||
				icon: '%fa:plus%',
 | 
			
		||||
				text: '%i18n:@add-tag-timeline%',
 | 
			
		||||
				action: () => {
 | 
			
		||||
					(this as any).os.new(MkSettingsWindow, {
 | 
			
		||||
						initialPage: 'hashtags'
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
 | 
			
		||||
			if (this.$store.state.settings.tagTimelines.length > 0) {
 | 
			
		||||
				menu.push(null);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			menu = menu.concat(this.$store.state.settings.tagTimelines.map(t => ({
 | 
			
		||||
				icon: '%fa:hashtag%',
 | 
			
		||||
				text: t.title,
 | 
			
		||||
				action: () => {
 | 
			
		||||
					this.tagTl = t;
 | 
			
		||||
					this.src = 'tag';
 | 
			
		||||
				}
 | 
			
		||||
			})));
 | 
			
		||||
 | 
			
		||||
			this.os.new(Menu, {
 | 
			
		||||
				source: this.$refs.tagButton,
 | 
			
		||||
				compact: false,
 | 
			
		||||
				items: menu
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -104,13 +189,15 @@ root(isDark)
 | 
			
		||||
		border-radius 6px 6px 0 0
 | 
			
		||||
		box-shadow 0 1px isDark ? rgba(#000, 0.15) : rgba(#000, 0.08)
 | 
			
		||||
 | 
			
		||||
		> button
 | 
			
		||||
		> .buttons
 | 
			
		||||
			position absolute
 | 
			
		||||
			z-index 2
 | 
			
		||||
			top 0
 | 
			
		||||
			right 0
 | 
			
		||||
			padding 0
 | 
			
		||||
			width 42px
 | 
			
		||||
			padding-right 8px
 | 
			
		||||
 | 
			
		||||
			> button
 | 
			
		||||
				padding 0 8px
 | 
			
		||||
				font-size 0.9em
 | 
			
		||||
				line-height 42px
 | 
			
		||||
				color isDark ? #9baec8 : #ccc
 | 
			
		||||
@@ -121,6 +208,20 @@ root(isDark)
 | 
			
		||||
				&:active
 | 
			
		||||
					color isDark ? #b2c1d5 : #999
 | 
			
		||||
 | 
			
		||||
				&[data-active]
 | 
			
		||||
					color $theme-color
 | 
			
		||||
					cursor default
 | 
			
		||||
 | 
			
		||||
					&:before
 | 
			
		||||
						content ""
 | 
			
		||||
						display block
 | 
			
		||||
						position absolute
 | 
			
		||||
						bottom 0
 | 
			
		||||
						left 0
 | 
			
		||||
						width 100%
 | 
			
		||||
						height 2px
 | 
			
		||||
						background $theme-color
 | 
			
		||||
 | 
			
		||||
		> span
 | 
			
		||||
			display inline-block
 | 
			
		||||
			padding 0 10px
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ export default Vue.extend({
 | 
			
		||||
					translateY: -64,
 | 
			
		||||
					duration: 500,
 | 
			
		||||
					easing: 'easeInElastic',
 | 
			
		||||
					complete: () => this.$destroy()
 | 
			
		||||
					complete: () => this.destroyDom()
 | 
			
		||||
				});
 | 
			
		||||
			}, 6000);
 | 
			
		||||
		});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-ui" :style="style">
 | 
			
		||||
<div class="mk-ui" :style="style" v-hotkey.global="keymap">
 | 
			
		||||
	<x-header class="header" v-show="!zenMode"/>
 | 
			
		||||
	<div class="content">
 | 
			
		||||
		<slot></slot>
 | 
			
		||||
@@ -16,11 +16,13 @@ export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XHeader
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			zenMode: false
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		style(): any {
 | 
			
		||||
			if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {};
 | 
			
		||||
@@ -28,29 +30,26 @@ export default Vue.extend({
 | 
			
		||||
				backgroundColor: this.$store.state.i.wallpaperColor && this.$store.state.i.wallpaperColor.length == 3 ? `rgb(${ this.$store.state.i.wallpaperColor.join(',') })` : null,
 | 
			
		||||
				backgroundImage: `url(${ this.$store.state.i.wallpaperUrl })`
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'p': this.post,
 | 
			
		||||
				'n': this.post,
 | 
			
		||||
				'z': this.toggleZenMode
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		document.addEventListener('keydown', this.onKeydown);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		document.removeEventListener('keydown', this.onKeydown);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		onKeydown(e) {
 | 
			
		||||
			if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA') return;
 | 
			
		||||
 | 
			
		||||
			if (e.which == 80 || e.which == 78) { // p or n
 | 
			
		||||
				e.preventDefault();
 | 
			
		||||
		post() {
 | 
			
		||||
			(this as any).apis.post();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
			if (e.which == 90) { // z
 | 
			
		||||
				e.preventDefault();
 | 
			
		||||
		toggleZenMode() {
 | 
			
		||||
			this.zenMode = !this.zenMode;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
 | 
			
		||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="destroyDom">
 | 
			
		||||
	<span slot="header">%fa:list% %i18n:@title%</span>
 | 
			
		||||
 | 
			
		||||
	<div class="xkxvokkjlptzyewouewmceqcxhpgzprp" :data-darkmode="$store.state.device.darkmode">
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ export default Vue.extend({
 | 
			
		||||
				'margin-top': '-8px',
 | 
			
		||||
				duration: 200,
 | 
			
		||||
				easing: 'easeOutQuad',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,11 @@ export default Vue.extend({
 | 
			
		||||
		name: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			default: null
 | 
			
		||||
		},
 | 
			
		||||
		animation: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +111,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.preventMount) {
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -142,7 +147,7 @@ export default Vue.extend({
 | 
			
		||||
				anime({
 | 
			
		||||
					targets: bg,
 | 
			
		||||
					opacity: 1,
 | 
			
		||||
					duration: 100,
 | 
			
		||||
					duration: this.animation ? 100 : 0,
 | 
			
		||||
					easing: 'linear'
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
@@ -152,7 +157,7 @@ export default Vue.extend({
 | 
			
		||||
				targets: main,
 | 
			
		||||
				opacity: 1,
 | 
			
		||||
				scale: [1.1, 1],
 | 
			
		||||
				duration: 200,
 | 
			
		||||
				duration: this.animation ? 200 : 0,
 | 
			
		||||
				easing: 'easeOutQuad'
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +165,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
			setTimeout(() => {
 | 
			
		||||
				this.$emit('opened');
 | 
			
		||||
			}, 300);
 | 
			
		||||
			}, this.animation ? 300 : 0);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		close() {
 | 
			
		||||
@@ -174,7 +179,7 @@ export default Vue.extend({
 | 
			
		||||
				anime({
 | 
			
		||||
					targets: bg,
 | 
			
		||||
					opacity: 0,
 | 
			
		||||
					duration: 300,
 | 
			
		||||
					duration: this.animation ? 300 : 0,
 | 
			
		||||
					easing: 'linear'
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
@@ -185,14 +190,14 @@ export default Vue.extend({
 | 
			
		||||
				targets: main,
 | 
			
		||||
				opacity: 0,
 | 
			
		||||
				scale: 0.8,
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				duration: this.animation ? 300 : 0,
 | 
			
		||||
				easing: [0.5, -0.5, 1, 0.5]
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			setTimeout(() => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
				this.$emit('closed');
 | 
			
		||||
			}, 300);
 | 
			
		||||
				this.destroyDom();
 | 
			
		||||
			}, this.animation ? 300 : 0);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		popout() {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,9 @@
 | 
			
		||||
<x-tl-column v-else-if="column.type == 'hybrid'" :column="column" :is-stacked="isStacked"/>
 | 
			
		||||
<x-tl-column v-else-if="column.type == 'global'" :column="column" :is-stacked="isStacked"/>
 | 
			
		||||
<x-tl-column v-else-if="column.type == 'list'" :column="column" :is-stacked="isStacked"/>
 | 
			
		||||
<x-tl-column v-else-if="column.type == 'hashtag'" :column="column" :is-stacked="isStacked"/>
 | 
			
		||||
<x-mentions-column v-else-if="column.type == 'mentions'" :column="column" :is-stacked="isStacked"/>
 | 
			
		||||
<x-direct-column v-else-if="column.type == 'direct'" :column="column" :is-stacked="isStacked"/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
@@ -13,12 +16,16 @@ import Vue from 'vue';
 | 
			
		||||
import XTlColumn from './deck.tl-column.vue';
 | 
			
		||||
import XNotificationsColumn from './deck.notifications-column.vue';
 | 
			
		||||
import XWidgetsColumn from './deck.widgets-column.vue';
 | 
			
		||||
import XMentionsColumn from './deck.mentions-column.vue';
 | 
			
		||||
import XDirectColumn from './deck.direct-column.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XTlColumn,
 | 
			
		||||
		XNotificationsColumn,
 | 
			
		||||
		XWidgetsColumn
 | 
			
		||||
		XWidgetsColumn,
 | 
			
		||||
		XMentionsColumn,
 | 
			
		||||
		XDirectColumn
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
<template>
 | 
			
		||||
<x-column :name="name" :column="column" :is-stacked="isStacked">
 | 
			
		||||
	<span slot="header">%fa:envelope R%{{ name }}</span>
 | 
			
		||||
 | 
			
		||||
	<x-direct/>
 | 
			
		||||
</x-column>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XColumn from './deck.column.vue';
 | 
			
		||||
import XDirect from './deck.direct.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XDirect
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		name(): string {
 | 
			
		||||
			if (this.column.name) return this.column.name;
 | 
			
		||||
			return '%i18n:common.deck.direct%';
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										97
									
								
								src/client/app/desktop/views/pages/deck/deck.direct.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/client/app/desktop/views/pages/deck/deck.direct.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<x-notes ref="timeline" :more="existMore ? more : null"/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XNotes from './deck.notes.vue';
 | 
			
		||||
 | 
			
		||||
const fetchLimit = 10;
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XNotes
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			moreFetching: false,
 | 
			
		||||
			existMore: false,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('mention', this.onNote);
 | 
			
		||||
 | 
			
		||||
		this.fetch();
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('mention', this.onNote);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch() {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
			
		||||
				(this as any).api('notes/mentions', {
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
			
		||||
					visibility: 'specified'
 | 
			
		||||
				}).then(notes => {
 | 
			
		||||
					if (notes.length == fetchLimit + 1) {
 | 
			
		||||
						notes.pop();
 | 
			
		||||
						this.existMore = true;
 | 
			
		||||
					}
 | 
			
		||||
					res(notes);
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
					this.$emit('loaded');
 | 
			
		||||
				}, rej);
 | 
			
		||||
			}));
 | 
			
		||||
		},
 | 
			
		||||
		more() {
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
 | 
			
		||||
			const promise = (this as any).api('notes/mentions', {
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
			
		||||
				visibility: 'specified'
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			promise.then(notes => {
 | 
			
		||||
				if (notes.length == fetchLimit + 1) {
 | 
			
		||||
					notes.pop();
 | 
			
		||||
				} else {
 | 
			
		||||
					this.existMore = false;
 | 
			
		||||
				}
 | 
			
		||||
				notes.forEach(n => (this.$refs.timeline as any).append(n));
 | 
			
		||||
				this.moreFetching = false;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			if (note.visibility == 'specified') {
 | 
			
		||||
				(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										117
									
								
								src/client/app/desktop/views/pages/deck/deck.hashtag-tl.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/client/app/desktop/views/pages/deck/deck.hashtag-tl.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XNotes from './deck.notes.vue';
 | 
			
		||||
import { HashtagStream } from '../../../../common/scripts/streaming/hashtag';
 | 
			
		||||
 | 
			
		||||
const fetchLimit = 10;
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XNotes
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		tagTl: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		mediaOnly: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		mediaView: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			moreFetching: false,
 | 
			
		||||
			existMore: false,
 | 
			
		||||
			connection: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		mediaOnly() {
 | 
			
		||||
			this.fetch();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.connection) this.connection.close();
 | 
			
		||||
		this.connection = new HashtagStream((this as any).os, this.$store.state.i, this.tagTl.query);
 | 
			
		||||
		this.connection.on('note', this.onNote);
 | 
			
		||||
 | 
			
		||||
		this.fetch();
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.close();
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch() {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
			
		||||
				(this as any).api('notes/search_by_tag', {
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					withFiles: this.mediaOnly,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
			
		||||
					query: this.tagTl.query
 | 
			
		||||
				}).then(notes => {
 | 
			
		||||
					if (notes.length == fetchLimit + 1) {
 | 
			
		||||
						notes.pop();
 | 
			
		||||
						this.existMore = true;
 | 
			
		||||
					}
 | 
			
		||||
					res(notes);
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
					this.$emit('loaded');
 | 
			
		||||
				}, rej);
 | 
			
		||||
			}));
 | 
			
		||||
		},
 | 
			
		||||
		more() {
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
 | 
			
		||||
			const promise = (this as any).api('notes/search_by_tag', {
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				withFiles: this.mediaOnly,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
			
		||||
				query: this.tagTl.query
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			promise.then(notes => {
 | 
			
		||||
				if (notes.length == fetchLimit + 1) {
 | 
			
		||||
					notes.pop();
 | 
			
		||||
				} else {
 | 
			
		||||
					this.existMore = false;
 | 
			
		||||
				}
 | 
			
		||||
				notes.forEach(n => (this.$refs.timeline as any).append(n));
 | 
			
		||||
				this.moreFetching = false;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			if (this.mediaOnly && note.files.length == 0) return;
 | 
			
		||||
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
<template>
 | 
			
		||||
<x-column :name="name" :column="column" :is-stacked="isStacked">
 | 
			
		||||
	<span slot="header">%fa:at%{{ name }}</span>
 | 
			
		||||
 | 
			
		||||
	<x-mentions/>
 | 
			
		||||
</x-column>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XColumn from './deck.column.vue';
 | 
			
		||||
import XMentions from './deck.mentions.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XMentions
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
		column: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		isStacked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		name(): string {
 | 
			
		||||
			if (this.column.name) return this.column.name;
 | 
			
		||||
			return '%i18n:common.deck.mentions%';
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										93
									
								
								src/client/app/desktop/views/pages/deck/deck.mentions.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/client/app/desktop/views/pages/deck/deck.mentions.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<x-notes ref="timeline" :more="existMore ? more : null"/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XNotes from './deck.notes.vue';
 | 
			
		||||
 | 
			
		||||
const fetchLimit = 10;
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XNotes
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			moreFetching: false,
 | 
			
		||||
			existMore: false,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('mention', this.onNote);
 | 
			
		||||
 | 
			
		||||
		this.fetch();
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('mention', this.onNote);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch() {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
			
		||||
				(this as any).api('notes/mentions', {
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
				}).then(notes => {
 | 
			
		||||
					if (notes.length == fetchLimit + 1) {
 | 
			
		||||
						notes.pop();
 | 
			
		||||
						this.existMore = true;
 | 
			
		||||
					}
 | 
			
		||||
					res(notes);
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
					this.$emit('loaded');
 | 
			
		||||
				}, rej);
 | 
			
		||||
			}));
 | 
			
		||||
		},
 | 
			
		||||
		more() {
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
 | 
			
		||||
			const promise = (this as any).api('notes/mentions', {
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			promise.then(notes => {
 | 
			
		||||
				if (notes.length == fetchLimit + 1) {
 | 
			
		||||
					notes.pop();
 | 
			
		||||
				} else {
 | 
			
		||||
					this.existMore = false;
 | 
			
		||||
				}
 | 
			
		||||
				notes.forEach(n => (this.$refs.timeline as any).append(n));
 | 
			
		||||
				this.moreFetching = false;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="oxynyeqmfvracxnglgulyqfgqxnxmehl">
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<!--<transition-group name="mk-notifications" class="transition notifications">-->
 | 
			
		||||
	<div class="notifications">
 | 
			
		||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications">
 | 
			
		||||
		<template v-for="(notification, i) in _notifications">
 | 
			
		||||
			<x-notification class="notification" :notification="notification" :key="notification.id"/>
 | 
			
		||||
			<p class="date" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date" :key="notification.id + '-time'">
 | 
			
		||||
@@ -10,8 +9,7 @@
 | 
			
		||||
				<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
 | 
			
		||||
			</p>
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
	<!--</transition-group>-->
 | 
			
		||||
	</component>
 | 
			
		||||
	<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
 | 
			
		||||
		<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
 | 
			
		||||
	</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
		<template v-if="column.type == 'hybrid'">%fa:share-alt%</template>
 | 
			
		||||
		<template v-if="column.type == 'global'">%fa:globe%</template>
 | 
			
		||||
		<template v-if="column.type == 'list'">%fa:list%</template>
 | 
			
		||||
		<template v-if="column.type == 'hashtag'">%fa:hashtag%</template>
 | 
			
		||||
		<span>{{ name }}</span>
 | 
			
		||||
	</span>
 | 
			
		||||
 | 
			
		||||
@@ -14,6 +15,7 @@
 | 
			
		||||
		<mk-switch v-model="column.isMediaView" @change="onChangeSettings" text="%i18n:@is-media-view%"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<x-list-tl v-if="column.type == 'list'" :list="column.list" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
 | 
			
		||||
	<x-hashtag-tl v-if="column.type == 'hashtag'" :tag-tl="$store.state.settings.tagTimelines.find(x => x.id == column.tagTlId)" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
 | 
			
		||||
	<x-tl v-else :src="column.type" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
 | 
			
		||||
</x-column>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -23,12 +25,14 @@ import Vue from 'vue';
 | 
			
		||||
import XColumn from './deck.column.vue';
 | 
			
		||||
import XTl from './deck.tl.vue';
 | 
			
		||||
import XListTl from './deck.list-tl.vue';
 | 
			
		||||
import XHashtagTl from './deck.hashtag-tl.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XColumn,
 | 
			
		||||
		XTl,
 | 
			
		||||
		XListTl
 | 
			
		||||
		XListTl,
 | 
			
		||||
		XHashtagTl
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	props: {
 | 
			
		||||
@@ -65,6 +69,7 @@ export default Vue.extend({
 | 
			
		||||
				case 'hybrid': return '%i18n:common.deck.hybrid%';
 | 
			
		||||
				case 'global': return '%i18n:common.deck.global%';
 | 
			
		||||
				case 'list': return this.column.list.title;
 | 
			
		||||
				case 'hashtag': return this.$store.state.settings.tagTimelines.find(x => x.id == this.column.tagTlId).title;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 
 | 
			
		||||
@@ -138,6 +138,24 @@ export default Vue.extend({
 | 
			
		||||
							type: 'global'
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: '%fa:at%',
 | 
			
		||||
					text: '%i18n:common.deck.mentions%',
 | 
			
		||||
					action: () => {
 | 
			
		||||
						this.$store.dispatch('settings/addDeckColumn', {
 | 
			
		||||
							id: uuid(),
 | 
			
		||||
							type: 'mentions'
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: '%fa:envelope R%',
 | 
			
		||||
					text: '%i18n:common.deck.direct%',
 | 
			
		||||
					action: () => {
 | 
			
		||||
						this.$store.dispatch('settings/addDeckColumn', {
 | 
			
		||||
							id: uuid(),
 | 
			
		||||
							type: 'direct'
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: '%fa:list%',
 | 
			
		||||
					text: '%i18n:common.deck.list%',
 | 
			
		||||
@@ -152,6 +170,20 @@ export default Vue.extend({
 | 
			
		||||
							w.close();
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: '%fa:hashtag%',
 | 
			
		||||
					text: '%i18n:common.deck.hashtag%',
 | 
			
		||||
					action: () => {
 | 
			
		||||
						(this as any).apis.input({
 | 
			
		||||
							title: '%i18n:@enter-hashtag-tl-title%'
 | 
			
		||||
						}).then(title => {
 | 
			
		||||
							this.$store.dispatch('settings/addDeckColumn', {
 | 
			
		||||
								id: uuid(),
 | 
			
		||||
								type: 'hashtag',
 | 
			
		||||
								tagTlId: this.$store.state.settings.tagTimelines.find(x => x.title == title).id
 | 
			
		||||
							});
 | 
			
		||||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}, {
 | 
			
		||||
					icon: '%fa:bell R%',
 | 
			
		||||
					text: '%i18n:common.deck.notifications%',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-ui>
 | 
			
		||||
	<mk-home :mode="mode" @loaded="loaded"/>
 | 
			
		||||
	<mk-home :mode="mode" @loaded="loaded" ref="home" v-hotkey.global="keymap"/>
 | 
			
		||||
</mk-ui>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -15,6 +15,13 @@ export default Vue.extend({
 | 
			
		||||
			default: 'timeline'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		keymap(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				't': this.focus
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		document.title = (this as any).os.instanceName;
 | 
			
		||||
 | 
			
		||||
@@ -23,6 +30,9 @@ export default Vue.extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		loaded() {
 | 
			
		||||
			Progress.done();
 | 
			
		||||
		},
 | 
			
		||||
		focus() {
 | 
			
		||||
			this.$refs.home.focus();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ export default Vue.extend({
 | 
			
		||||
	> .stats
 | 
			
		||||
		display flex
 | 
			
		||||
		justify-content center
 | 
			
		||||
		margin-bottom 16px
 | 
			
		||||
		margin 0 auto 16px auto
 | 
			
		||||
		padding 32px
 | 
			
		||||
		background #fff
 | 
			
		||||
		box-shadow 0 2px 8px rgba(#000, 0.1)
 | 
			
		||||
@@ -60,5 +60,6 @@ export default Vue.extend({
 | 
			
		||||
				font-size 70%
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		max-width 850px
 | 
			
		||||
		max-width 950px
 | 
			
		||||
		margin 0 auto
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
		<main>
 | 
			
		||||
			<div class="main">
 | 
			
		||||
				<x-header :user="user"/>
 | 
			
		||||
				<mk-note-detail v-if="user.pinnedNote" :note="user.pinnedNote" :compact="true"/>
 | 
			
		||||
				<mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/>
 | 
			
		||||
				<x-timeline class="timeline" ref="tl" :user="user"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="side">
 | 
			
		||||
@@ -28,7 +28,6 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import parseAcct from '../../../../../../misc/acct/parse';
 | 
			
		||||
import getUserName from '../../../../../../misc/get-user-name';
 | 
			
		||||
import Progress from '../../../../common/scripts/loading';
 | 
			
		||||
import XHeader from './user.header.vue';
 | 
			
		||||
import XTimeline from './user.timeline.vue';
 | 
			
		||||
 
 | 
			
		||||
@@ -7,29 +7,20 @@ import Vuex from 'vuex';
 | 
			
		||||
import VueRouter from 'vue-router';
 | 
			
		||||
import * as TreeView from 'vue-json-tree-view';
 | 
			
		||||
import VAnimateCss from 'v-animate-css';
 | 
			
		||||
import Element from 'element-ui';
 | 
			
		||||
import ElementLocaleEn from 'element-ui/lib/locale/lang/en';
 | 
			
		||||
import ElementLocaleJa from 'element-ui/lib/locale/lang/ja';
 | 
			
		||||
import VModal from 'vue-js-modal';
 | 
			
		||||
import VueHotkey from './common/hotkey';
 | 
			
		||||
 | 
			
		||||
import App from './app.vue';
 | 
			
		||||
import checkForUpdate from './common/scripts/check-for-update';
 | 
			
		||||
import MiOS, { API } from './mios';
 | 
			
		||||
import { version, codename, lang } from './config';
 | 
			
		||||
 | 
			
		||||
let elementLocale;
 | 
			
		||||
switch (lang) {
 | 
			
		||||
	case 'ja-JP': elementLocale = ElementLocaleJa; break;
 | 
			
		||||
	case 'en-US': elementLocale = ElementLocaleEn; break;
 | 
			
		||||
	default: elementLocale = ElementLocaleEn; break;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vue.use(Vuex);
 | 
			
		||||
Vue.use(VueRouter);
 | 
			
		||||
Vue.use(TreeView);
 | 
			
		||||
Vue.use(VAnimateCss);
 | 
			
		||||
Vue.use(Element, { locale: elementLocale });
 | 
			
		||||
Vue.use(VModal);
 | 
			
		||||
Vue.use(VueHotkey);
 | 
			
		||||
 | 
			
		||||
// Register global directives
 | 
			
		||||
require('./common/views/directives');
 | 
			
		||||
@@ -42,11 +33,15 @@ require('./common/views/widgets');
 | 
			
		||||
require('./common/views/filters');
 | 
			
		||||
 | 
			
		||||
Vue.mixin({
 | 
			
		||||
	destroyed(this: any) {
 | 
			
		||||
	methods: {
 | 
			
		||||
		destroyDom() {
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
 | 
			
		||||
			if (this.$el.parentNode) {
 | 
			
		||||
				this.$el.parentNode.removeChild(this.$el);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ import VueRouter from 'vue-router';
 | 
			
		||||
 | 
			
		||||
// Style
 | 
			
		||||
import './style.styl';
 | 
			
		||||
import '../../element.scss';
 | 
			
		||||
 | 
			
		||||
import init from '../init';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ export default Vue.extend({
 | 
			
		||||
				scale: 0.8,
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				easing: [ 0.5, -0.5, 1, 0.5 ],
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onBgClick() {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,15 +31,15 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
		onSelected(file) {
 | 
			
		||||
			this.$emit('selected', file);
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		},
 | 
			
		||||
		cancel() {
 | 
			
		||||
			this.$emit('canceled');
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		},
 | 
			
		||||
		ok() {
 | 
			
		||||
			this.$emit('selected', this.files);
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -19,11 +19,11 @@ export default Vue.extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		cancel() {
 | 
			
		||||
			this.$emit('canceled');
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		},
 | 
			
		||||
		ok() {
 | 
			
		||||
			this.$emit('selected', (this.$refs.browser as any).folder);
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ export default Vue.extend({
 | 
			
		||||
			this.fetch();
 | 
			
		||||
		},
 | 
			
		||||
		close() {
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
			this.destroyDom();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="qjewsnkgzzxlxtzncydssfbgjibiehcy" v-if="image.isSensitive && hide" @click="hide = false">
 | 
			
		||||
<div class="qjewsnkgzzxlxtzncydssfbgjibiehcy" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
 | 
			
		||||
	<div>
 | 
			
		||||
		<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
 | 
			
		||||
		<span>%i18n:@click-to-show%</span>
 | 
			
		||||
@@ -19,12 +19,13 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
		raw: {
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		hide: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hide: true
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
	computed: {
 | 
			
		||||
		style(): any {
 | 
			
		||||
			let url = `url(${this.image.thumbnailUrl})`;
 | 
			
		||||
@@ -65,7 +66,7 @@ export default Vue.extend({
 | 
			
		||||
		text-align center
 | 
			
		||||
		font-size 12px
 | 
			
		||||
 | 
			
		||||
		> b
 | 
			
		||||
		> *
 | 
			
		||||
			display block
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,25 +15,28 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue'
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		video: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		hide: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hide: true
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		imageStyle(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'background-image': `url(${this.video.url})`
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
	},})
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,7 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<!-- <transition-group name="mk-notes" class="transition"> -->
 | 
			
		||||
	<div class="transition">
 | 
			
		||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="transition" tag="div">
 | 
			
		||||
		<template v-for="(note, i) in _notes">
 | 
			
		||||
			<mk-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
 | 
			
		||||
			<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
 | 
			
		||||
@@ -23,8 +22,7 @@
 | 
			
		||||
				<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
 | 
			
		||||
			</p>
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
	<!-- </transition-group> -->
 | 
			
		||||
	</component>
 | 
			
		||||
 | 
			
		||||
	<footer v-if="more">
 | 
			
		||||
		<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-notifications">
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<!-- <transition-group name="mk-notifications" class="transition notifications"> -->
 | 
			
		||||
	<div class="transition notifications">
 | 
			
		||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications">
 | 
			
		||||
		<template v-for="(notification, i) in _notifications">
 | 
			
		||||
			<mk-notification :notification="notification" :key="notification.id"/>
 | 
			
		||||
			<p class="date" :key="notification.id + '_date'" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date">
 | 
			
		||||
@@ -10,8 +9,7 @@
 | 
			
		||||
				<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
 | 
			
		||||
			</p>
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
	<!-- </transition-group> -->
 | 
			
		||||
	</component>
 | 
			
		||||
 | 
			
		||||
	<button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
 | 
			
		||||
		<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ export default Vue.extend({
 | 
			
		||||
					[this.pos]: `-${this.$el.offsetHeight}px`,
 | 
			
		||||
					duration: 500,
 | 
			
		||||
					easing: 'easeOutQuad',
 | 
			
		||||
					complete: () => this.$destroy()
 | 
			
		||||
					complete: () => this.destroyDom()
 | 
			
		||||
				});
 | 
			
		||||
			}, 6000);
 | 
			
		||||
		});
 | 
			
		||||
@@ -45,7 +45,7 @@ export default Vue.extend({
 | 
			
		||||
	$height = 78px
 | 
			
		||||
 | 
			
		||||
	position fixed
 | 
			
		||||
	z-index 1024
 | 
			
		||||
	z-index 10000
 | 
			
		||||
	left 0
 | 
			
		||||
	right 0
 | 
			
		||||
	width 100%
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,7 @@ export default Vue.extend({
 | 
			
		||||
				translateY: 16,
 | 
			
		||||
				duration: 300,
 | 
			
		||||
				easing: 'easeOutQuad',
 | 
			
		||||
				complete: () => this.$destroy()
 | 
			
		||||
				complete: () => this.destroyDom()
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@
 | 
			
		||||
					<span v-if="visibility === 'private'">%fa:lock%</span>
 | 
			
		||||
				</button>
 | 
			
		||||
			</footer>
 | 
			
		||||
			<input ref="file" class="file" type="file" accept="image/*" multiple="multiple" @change="onChangeFile"/>
 | 
			
		||||
			<input ref="file" class="file" type="file" multiple="multiple" @change="onChangeFile"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="hashtags" v-if="recentHashtags.length > 0 && $store.state.settings.suggestRecentHashtags">
 | 
			
		||||
@@ -59,7 +59,7 @@ import MkVisibilityChooser from '../../../common/views/components/visibility-cho
 | 
			
		||||
import getFace from '../../../common/scripts/get-face';
 | 
			
		||||
import parse from '../../../../../mfm/parse';
 | 
			
		||||
import { host } from '../../../config';
 | 
			
		||||
import { erase } from '../../../../../prelude/array';
 | 
			
		||||
import { erase, unique } from '../../../../../prelude/array';
 | 
			
		||||
import { length } from 'stringz';
 | 
			
		||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { HashtagStream } from '../../../common/scripts/streaming/hashtag';
 | 
			
		||||
 | 
			
		||||
const fetchLimit = 10;
 | 
			
		||||
 | 
			
		||||
@@ -21,6 +22,9 @@ export default Vue.extend({
 | 
			
		||||
		src: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		tagTl: {
 | 
			
		||||
			required: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -29,10 +33,18 @@ export default Vue.extend({
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			moreFetching: false,
 | 
			
		||||
			existMore: false,
 | 
			
		||||
			streamManager: null,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null,
 | 
			
		||||
			unreadCount: 0,
 | 
			
		||||
			date: null
 | 
			
		||||
			date: null,
 | 
			
		||||
			baseQuery: {
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
			},
 | 
			
		||||
			query: {},
 | 
			
		||||
			endpoint: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -41,49 +53,109 @@ export default Vue.extend({
 | 
			
		||||
			return this.$store.state.i.followingCount == 0;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		stream(): any {
 | 
			
		||||
			switch (this.src) {
 | 
			
		||||
				case 'home': return (this as any).os.stream;
 | 
			
		||||
				case 'local': return (this as any).os.streams.localTimelineStream;
 | 
			
		||||
				case 'hybrid': return (this as any).os.streams.hybridTimelineStream;
 | 
			
		||||
				case 'global': return (this as any).os.streams.globalTimelineStream;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		endpoint(): string {
 | 
			
		||||
			switch (this.src) {
 | 
			
		||||
				case 'home': return 'notes/timeline';
 | 
			
		||||
				case 'local': return 'notes/local-timeline';
 | 
			
		||||
				case 'hybrid': return 'notes/hybrid-timeline';
 | 
			
		||||
				case 'global': return 'notes/global-timeline';
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		canFetchMore(): boolean {
 | 
			
		||||
			return !this.moreFetching && !this.fetching && this.existMore;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.stream.use();
 | 
			
		||||
		const prepend = note => {
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.connection.on('note', this.onNote);
 | 
			
		||||
		if (this.src == 'home') {
 | 
			
		||||
			this.connection.on('follow', this.onChangeFollowing);
 | 
			
		||||
			this.connection.on('unfollow', this.onChangeFollowing);
 | 
			
		||||
		if (this.src == 'tag') {
 | 
			
		||||
			this.endpoint = 'notes/search_by_tag';
 | 
			
		||||
			this.query = {
 | 
			
		||||
				query: this.tagTl.query
 | 
			
		||||
			};
 | 
			
		||||
			this.connection = new HashtagStream((this as any).os, this.$store.state.i, this.tagTl.query);
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.connection.close();
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'home') {
 | 
			
		||||
			this.endpoint = 'notes/timeline';
 | 
			
		||||
			const onChangeFollowing = () => {
 | 
			
		||||
				this.fetch();
 | 
			
		||||
			};
 | 
			
		||||
			this.streamManager = (this as any).os.stream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.connection.on('follow', onChangeFollowing);
 | 
			
		||||
			this.connection.on('unfollow', onChangeFollowing);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.connection.off('follow', onChangeFollowing);
 | 
			
		||||
				this.connection.off('unfollow', onChangeFollowing);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'local') {
 | 
			
		||||
			this.endpoint = 'notes/local-timeline';
 | 
			
		||||
			this.streamManager = (this as any).os.streams.localTimelineStream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'hybrid') {
 | 
			
		||||
			this.endpoint = 'notes/hybrid-timeline';
 | 
			
		||||
			this.streamManager = (this as any).os.streams.hybridTimelineStream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'global') {
 | 
			
		||||
			this.endpoint = 'notes/global-timeline';
 | 
			
		||||
			this.streamManager = (this as any).os.streams.globalTimelineStream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('note', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('note', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'mentions') {
 | 
			
		||||
			this.endpoint = 'notes/mentions';
 | 
			
		||||
			this.streamManager = (this as any).os.stream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('mention', prepend);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('mention', prepend);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (this.src == 'messages') {
 | 
			
		||||
			this.endpoint = 'notes/mentions';
 | 
			
		||||
			this.query = {
 | 
			
		||||
				visibility: 'specified'
 | 
			
		||||
			};
 | 
			
		||||
			const onNote = note => {
 | 
			
		||||
				if (note.visibility == 'specified') {
 | 
			
		||||
					prepend(note);
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			this.streamManager = (this as any).os.stream;
 | 
			
		||||
			this.connection = this.streamManager.getConnection();
 | 
			
		||||
			this.connectionId = this.streamManager.use();
 | 
			
		||||
			this.connection.on('mention', onNote);
 | 
			
		||||
			this.$once('beforeDestroy', () => {
 | 
			
		||||
				this.connection.off('mention', onNote);
 | 
			
		||||
				this.streamManager.dispose(this.connectionId);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.fetch();
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('note', this.onNote);
 | 
			
		||||
		if (this.src == 'home') {
 | 
			
		||||
			this.connection.off('follow', this.onChangeFollowing);
 | 
			
		||||
			this.connection.off('unfollow', this.onChangeFollowing);
 | 
			
		||||
		}
 | 
			
		||||
		this.stream.dispose(this.connectionId);
 | 
			
		||||
		this.$emit('beforeDestroy');
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
@@ -91,13 +163,10 @@ export default Vue.extend({
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
			
		||||
				(this as any).api(this.endpoint, {
 | 
			
		||||
				(this as any).api(this.endpoint, Object.assign({
 | 
			
		||||
					limit: fetchLimit + 1,
 | 
			
		||||
					untilDate: this.date ? this.date.getTime() : undefined,
 | 
			
		||||
					includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
				}).then(notes => {
 | 
			
		||||
					untilDate: this.date ? this.date.getTime() : undefined
 | 
			
		||||
				}, this.baseQuery, this.query)).then(notes => {
 | 
			
		||||
					if (notes.length == fetchLimit + 1) {
 | 
			
		||||
						notes.pop();
 | 
			
		||||
						this.existMore = true;
 | 
			
		||||
@@ -114,13 +183,10 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
 | 
			
		||||
			const promise = (this as any).api(this.endpoint, {
 | 
			
		||||
			const promise = (this as any).api(this.endpoint, Object.assign({
 | 
			
		||||
				limit: fetchLimit + 1,
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
			
		||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
			
		||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
			
		||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
			
		||||
			});
 | 
			
		||||
				untilId: (this.$refs.timeline as any).tail().id
 | 
			
		||||
			}, this.baseQuery, this.query));
 | 
			
		||||
 | 
			
		||||
			promise.then(notes => {
 | 
			
		||||
				if (notes.length == fetchLimit + 1) {
 | 
			
		||||
@@ -135,15 +201,6 @@ export default Vue.extend({
 | 
			
		||||
			return promise;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onNote(note) {
 | 
			
		||||
			// Prepend a note
 | 
			
		||||
			(this.$refs.timeline as any).prepend(note);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeFollowing() {
 | 
			
		||||
			this.fetch();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		focus() {
 | 
			
		||||
			(this.$refs.timeline as any).focus();
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,10 @@
 | 
			
		||||
			<span v-if="src == 'local'">%fa:R comments%%i18n:@local%</span>
 | 
			
		||||
			<span v-if="src == 'hybrid'">%fa:share-alt%%i18n:@hybrid%</span>
 | 
			
		||||
			<span v-if="src == 'global'">%fa:globe%%i18n:@global%</span>
 | 
			
		||||
			<span v-if="src == 'mentions'">%fa:at%%i18n:@mentions%</span>
 | 
			
		||||
			<span v-if="src == 'messages'">%fa:envelope R%%i18n:@messages%</span>
 | 
			
		||||
			<span v-if="src == 'list'">%fa:list%{{ list.title }}</span>
 | 
			
		||||
			<span v-if="src == 'tag'">%fa:hashtag%{{ tagTl.title }}</span>
 | 
			
		||||
		</span>
 | 
			
		||||
		<span style="margin-left:8px">
 | 
			
		||||
			<template v-if="!showNav">%fa:angle-down%</template>
 | 
			
		||||
@@ -21,15 +24,22 @@
 | 
			
		||||
	<main :data-darkmode="$store.state.device.darkmode">
 | 
			
		||||
		<div class="nav" v-if="showNav">
 | 
			
		||||
			<div class="bg" @click="showNav = false"></div>
 | 
			
		||||
			<div class="pointer"></div>
 | 
			
		||||
			<div class="body">
 | 
			
		||||
				<div>
 | 
			
		||||
					<span :data-active="src == 'home'" @click="src = 'home'">%fa:home% %i18n:@home%</span>
 | 
			
		||||
					<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline">%fa:R comments% %i18n:@local%</span>
 | 
			
		||||
					<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline">%fa:share-alt% %i18n:@hybrid%</span>
 | 
			
		||||
					<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span>
 | 
			
		||||
					<div class="hr"></div>
 | 
			
		||||
					<span :data-active="src == 'mentions'" @click="src = 'mentions'">%fa:at% %i18n:@mentions%</span>
 | 
			
		||||
					<span :data-active="src == 'messages'" @click="src = 'messages'">%fa:envelope R% %i18n:@messages%</span>
 | 
			
		||||
					<template v-if="lists">
 | 
			
		||||
						<div class="hr" v-if="lists.length > 0"></div>
 | 
			
		||||
						<span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id">%fa:list% {{ l.title }}</span>
 | 
			
		||||
					</template>
 | 
			
		||||
					<div class="hr" v-if="$store.state.settings.tagTimelines && $store.state.settings.tagTimelines.length > 0"></div>
 | 
			
		||||
					<span v-for="tl in $store.state.settings.tagTimelines" :data-active="src == 'tag' && tagTl == tl" @click="src = 'tag'; tagTl = tl" :key="tl.id">%fa:hashtag% {{ tl.title }}</span>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
@@ -39,6 +49,9 @@
 | 
			
		||||
			<x-tl v-if="src == 'local'" ref="tl" key="local" src="local"/>
 | 
			
		||||
			<x-tl v-if="src == 'hybrid'" ref="tl" key="hybrid" src="hybrid"/>
 | 
			
		||||
			<x-tl v-if="src == 'global'" ref="tl" key="global" src="global"/>
 | 
			
		||||
			<x-tl v-if="src == 'mentions'" ref="tl" key="mentions" src="mentions"/>
 | 
			
		||||
			<x-tl v-if="src == 'messages'" ref="tl" key="messages" src="messages"/>
 | 
			
		||||
			<x-tl v-if="src == 'tag'" ref="tl" key="tag" src="tag" :tag-tl="tagTl"/>
 | 
			
		||||
			<mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</main>
 | 
			
		||||
@@ -60,6 +73,7 @@ export default Vue.extend({
 | 
			
		||||
			src: 'home',
 | 
			
		||||
			list: null,
 | 
			
		||||
			lists: null,
 | 
			
		||||
			tagTl: null,
 | 
			
		||||
			showNav: false,
 | 
			
		||||
			enableLocalTimeline: false
 | 
			
		||||
		};
 | 
			
		||||
@@ -71,9 +85,16 @@ export default Vue.extend({
 | 
			
		||||
			this.saveSrc();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		list() {
 | 
			
		||||
		list(x) {
 | 
			
		||||
			this.showNav = false;
 | 
			
		||||
			this.saveSrc();
 | 
			
		||||
			if (x != null) this.tagTl = null;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		tagTl(x) {
 | 
			
		||||
			this.showNav = false;
 | 
			
		||||
			this.saveSrc();
 | 
			
		||||
			if (x != null) this.list = null;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showNav(v) {
 | 
			
		||||
@@ -94,6 +115,8 @@ export default Vue.extend({
 | 
			
		||||
			this.src = this.$store.state.device.tl.src;
 | 
			
		||||
			if (this.src == 'list') {
 | 
			
		||||
				this.list = this.$store.state.device.tl.arg;
 | 
			
		||||
			} else if (this.src == 'tag') {
 | 
			
		||||
				this.tagTl = this.$store.state.device.tl.arg;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (this.$store.state.i.followingCount == 0) {
 | 
			
		||||
			this.src = 'hybrid';
 | 
			
		||||
@@ -118,7 +141,7 @@ export default Vue.extend({
 | 
			
		||||
		saveSrc() {
 | 
			
		||||
			this.$store.commit('device/setTl', {
 | 
			
		||||
				src: this.src,
 | 
			
		||||
				arg: this.list
 | 
			
		||||
				arg: this.src == 'list' ? this.list : this.tagTl
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -134,6 +157,26 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
root(isDark)
 | 
			
		||||
	> .nav
 | 
			
		||||
		> .pointer
 | 
			
		||||
			position fixed
 | 
			
		||||
			z-index 10002
 | 
			
		||||
			top 56px
 | 
			
		||||
			left 0
 | 
			
		||||
			right 0
 | 
			
		||||
 | 
			
		||||
			$size = 16px
 | 
			
		||||
 | 
			
		||||
			&:after
 | 
			
		||||
				content ""
 | 
			
		||||
				display block
 | 
			
		||||
				position absolute
 | 
			
		||||
				top -($size * 2)
 | 
			
		||||
				left s('calc(50% - %s)', $size)
 | 
			
		||||
				border-top solid $size transparent
 | 
			
		||||
				border-left solid $size transparent
 | 
			
		||||
				border-right solid $size transparent
 | 
			
		||||
				border-bottom solid $size isDark ? #272f3a : #fff
 | 
			
		||||
 | 
			
		||||
		> .bg
 | 
			
		||||
			position fixed
 | 
			
		||||
			z-index 10000
 | 
			
		||||
@@ -150,28 +193,22 @@ root(isDark)
 | 
			
		||||
			left 0
 | 
			
		||||
			right 0
 | 
			
		||||
			width 300px
 | 
			
		||||
			max-height calc(100% - 70px)
 | 
			
		||||
			margin 0 auto
 | 
			
		||||
			overflow auto
 | 
			
		||||
			-webkit-overflow-scrolling touch
 | 
			
		||||
			background isDark ? #272f3a : #fff
 | 
			
		||||
			border-radius 8px
 | 
			
		||||
			box-shadow 0 0 16px rgba(#000, 0.1)
 | 
			
		||||
 | 
			
		||||
			$balloon-size = 16px
 | 
			
		||||
 | 
			
		||||
			&:after
 | 
			
		||||
				content ""
 | 
			
		||||
				display block
 | 
			
		||||
				position absolute
 | 
			
		||||
				top -($balloon-size * 2) + 1.5px
 | 
			
		||||
				left s('calc(50% - %s)', $balloon-size)
 | 
			
		||||
				border-top solid $balloon-size transparent
 | 
			
		||||
				border-left solid $balloon-size transparent
 | 
			
		||||
				border-right solid $balloon-size transparent
 | 
			
		||||
				border-bottom solid $balloon-size isDark ? #272f3a : #fff
 | 
			
		||||
 | 
			
		||||
			> div
 | 
			
		||||
				padding 8px 0
 | 
			
		||||
 | 
			
		||||
				> *
 | 
			
		||||
				> .hr
 | 
			
		||||
					margin 8px 0
 | 
			
		||||
					border-top solid 1px isDark ? rgba(#000, 0.3) : rgba(#000, 0.1)
 | 
			
		||||
 | 
			
		||||
				> *:not(.hr)
 | 
			
		||||
					display block
 | 
			
		||||
					padding 8px 16px
 | 
			
		||||
					color isDark ? #cdd0d8 : #666
 | 
			
		||||
 
 | 
			
		||||
@@ -12,21 +12,24 @@
 | 
			
		||||
 | 
			
		||||
				<section>
 | 
			
		||||
					<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.contrastedAcct" @change="onChangeContrastedAcct">%i18n:@contrasted-acct%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="circleIcons">%i18n:@circle-icons%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="reduceMotion">%i18n:common.reduce-motion% (%i18n:common.this-setting-is-this-device-only%)</ui-switch>
 | 
			
		||||
					<ui-switch v-model="contrastedAcct">%i18n:@contrasted-acct%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="showFullAcct">%i18n:common.show-full-acct%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="iLikeSushi">%i18n:common.i-like-sushi%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="disableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="alwaysShowNsfw">%i18n:common.always-show-nsfw% (%i18n:common.this-setting-is-this-device-only%)</ui-switch>
 | 
			
		||||
					<ui-switch v-model="games_reversi_showBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
 | 
			
		||||
				</section>
 | 
			
		||||
 | 
			
		||||
				<section>
 | 
			
		||||
					<header>%i18n:@timeline%</header>
 | 
			
		||||
					<div>
 | 
			
		||||
						<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="$store.state.settings.showLocalRenotes" @change="onChangeShowLocalRenotes">%i18n:@show-local-renotes%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="showReplyTarget">%i18n:@show-reply-target%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="showMyRenotes">%i18n:@show-my-renotes%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="showRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
 | 
			
		||||
						<ui-switch v-model="showLocalRenotes">%i18n:@show-local-renotes%</ui-switch>
 | 
			
		||||
					</div>
 | 
			
		||||
				</section>
 | 
			
		||||
 | 
			
		||||
@@ -47,16 +50,16 @@
 | 
			
		||||
				<div slot="title">%fa:cog% %i18n:@behavior%</div>
 | 
			
		||||
 | 
			
		||||
				<section>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="fetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="disableViaMobile">%i18n:@disable-via-mobile%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="loadRawImages">%i18n:@load-raw-images%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="loadRemoteMedia">%i18n:@load-remote-media%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</ui-switch>
 | 
			
		||||
				</section>
 | 
			
		||||
 | 
			
		||||
				<section>
 | 
			
		||||
					<header>%i18n:@note-visibility%</header>
 | 
			
		||||
					<ui-switch v-model="$store.state.settings.rememberNoteVisibility" @change="onChangeRememberNoteVisibility">%i18n:@remember-note-visibility%</ui-switch>
 | 
			
		||||
					<ui-switch v-model="rememberNoteVisibility">%i18n:@remember-note-visibility%</ui-switch>
 | 
			
		||||
					<section>
 | 
			
		||||
						<header>%i18n:@default-note-visibility%</header>
 | 
			
		||||
						<ui-select v-model="defaultNoteVisibility">
 | 
			
		||||
@@ -166,6 +169,16 @@ export default Vue.extend({
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'darkmode', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		reduceMotion: {
 | 
			
		||||
			get() { return this.$store.state.device.reduceMotion; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'reduceMotion', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		alwaysShowNsfw: {
 | 
			
		||||
			get() { return this.$store.state.device.alwaysShowNsfw; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'alwaysShowNsfw', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		postStyle: {
 | 
			
		||||
			get() { return this.$store.state.device.postStyle; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'postStyle', value }); }
 | 
			
		||||
@@ -176,11 +189,6 @@ export default Vue.extend({
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		defaultNoteVisibility: {
 | 
			
		||||
			get() { return this.$store.state.settings.defaultNoteVisibility; },
 | 
			
		||||
			set(value) { this.$store.commit('settings/set', { key: 'defaultNoteVisibility', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		lightmode: {
 | 
			
		||||
			get() { return this.$store.state.device.lightmode; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'lightmode', value }); }
 | 
			
		||||
@@ -200,6 +208,86 @@ export default Vue.extend({
 | 
			
		||||
			get() { return this.$store.state.device.enableSounds; },
 | 
			
		||||
			set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		fetchOnScroll: {
 | 
			
		||||
			get() { return this.$store.state.settings.fetchOnScroll; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'fetchOnScroll', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		rememberNoteVisibility: {
 | 
			
		||||
			get() { return this.$store.state.settings.rememberNoteVisibility; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'rememberNoteVisibility', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disableViaMobile: {
 | 
			
		||||
			get() { return this.$store.state.settings.disableViaMobile; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'disableViaMobile', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		loadRemoteMedia: {
 | 
			
		||||
			get() { return this.$store.state.settings.loadRemoteMedia; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'loadRemoteMedia', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		circleIcons: {
 | 
			
		||||
			get() { return this.$store.state.settings.circleIcons; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'circleIcons', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		contrastedAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.contrastedAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'contrastedAcct', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showFullAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.showFullAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showFullAcct', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		iLikeSushi: {
 | 
			
		||||
			get() { return this.$store.state.settings.iLikeSushi; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'iLikeSushi', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		games_reversi_showBoardLabels: {
 | 
			
		||||
			get() { return this.$store.state.settings.games.reversi.showBoardLabels; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'games.reversi.showBoardLabels', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		games_reversi_useContrastStones: {
 | 
			
		||||
			get() { return this.$store.state.settings.games.reversi.useContrastStones; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'games.reversi.useContrastStones', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disableAnimatedMfm: {
 | 
			
		||||
			get() { return this.$store.state.settings.disableAnimatedMfm; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showReplyTarget: {
 | 
			
		||||
			get() { return this.$store.state.settings.showReplyTarget; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showReplyTarget', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showMyRenotes: {
 | 
			
		||||
			get() { return this.$store.state.settings.showMyRenotes; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showMyRenotes', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showRenotedMyNotes: {
 | 
			
		||||
			get() { return this.$store.state.settings.showRenotedMyNotes; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showRenotedMyNotes', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showLocalRenotes: {
 | 
			
		||||
			get() { return this.$store.state.settings.showLocalRenotes; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showLocalRenotes', value }); }
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		defaultNoteVisibility: {
 | 
			
		||||
			get() { return this.$store.state.settings.defaultNoteVisibility; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
@@ -211,104 +299,6 @@ export default Vue.extend({
 | 
			
		||||
			(this as any).os.signout();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeFetchOnScroll(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'fetchOnScroll',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeRememberNoteVisibility(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'rememberNoteVisibility',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeDisableViaMobile(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'disableViaMobile',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeLoadRemoteMedia(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'loadRemoteMedia',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeCircleIcons(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'circleIcons',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeContrastedAcct(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'contrastedAcct',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeILikeSushi(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'iLikeSushi',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeReversiBoardLabels(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'games.reversi.showBoardLabels',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeUseContrastReversiStones(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'games.reversi.useContrastStones',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeDisableAnimatedMfm(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'disableAnimatedMfm',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeShowReplyTarget(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showReplyTarget',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeShowMyRenotes(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showMyRenotes',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeShowRenotedMyNotes(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showRenotedMyNotes',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeShowLocalRenotes(v) {
 | 
			
		||||
			this.$store.dispatch('settings/set', {
 | 
			
		||||
				key: 'showLocalRenotes',
 | 
			
		||||
				value: v
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		checkForUpdate() {
 | 
			
		||||
			this.checkingForUpdate = true;
 | 
			
		||||
			checkForUpdate((this as any).os, true, true).then(newer => {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user