Compare commits

..

143 Commits

Author SHA1 Message Date
87091a2e03 8.61.0 2018-09-24 16:32:51 +09:00
59d67d3140 ピン留めを解除することができるようにしたり 2018-09-24 16:26:12 +09:00
7b4c307c46 🎨 2018-09-24 16:07:23 +09:00
92484be87f Resolve #2762 2018-09-24 16:02:01 +09:00
56b8f8b07d 8.60.0 2018-09-24 08:04:45 +09:00
722de35037 Add stats link 2018-09-24 08:02:07 +09:00
d93f76c1af ブロッカーへの警告はログイン時のみ表示するように 2018-09-24 08:00:10 +09:00
cba0dd5e17 Fix AP request error message is to long (#2760) 2018-09-24 07:35:39 +09:00
a2e2d5ba77 非ログイン時のユーザーページにインスタンスの紹介を表示するように 2018-09-23 19:55:15 +09:00
61e05cb50e 8.59.0 2018-09-23 16:13:33 +09:00
49e82adc6c mentionsを読み込むときも既読にするように 2018-09-23 16:05:46 +09:00
e4e668b327 Improve performance 2018-09-23 16:05:26 +09:00
8028c85c67 ダークモードのキーボードショートカット追加 2018-09-23 07:59:48 +09:00
28cb9cae51 8.58.0 2018-09-22 20:44:05 +09:00
7f2eb64131 fix(package): update @types/node to version 10.10.3 (#2753) 2018-09-22 20:42:05 +09:00
3e5330a92b 🎨 2018-09-22 20:39:12 +09:00
93e5e4afc0 🎨 2018-09-22 20:11:13 +09:00
aa5528d11e 🎨 2018-09-22 19:59:37 +09:00
251629ab61 🎨 2018-09-22 15:58:11 +09:00
82d94b5963 Fix #2747 2018-09-22 08:49:14 +09:00
8240901332 Merge pull request #2749 from syuilo/l10n_develop
New Crowdin translations
2018-09-22 05:12:06 +09:00
0a870b8e7e fix(package): update @types/node to version 10.10.2 (#2750) 2018-09-22 05:11:53 +09:00
88dd653fa5 New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 03:31:27 +09:00
b712b70330 New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 03:22:07 +09:00
a018c2f09f New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 03:12:07 +09:00
04c16e53a5 New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 03:01:43 +09:00
5e89e73f76 New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 02:51:52 +09:00
2c9432d7a9 New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 02:41:58 +09:00
19d1775b36 New translations ja-JP.yml (Japanese, Kansai) 2018-09-22 02:31:58 +09:00
ecc235c545 Fix bug 2018-09-21 16:43:46 +09:00
382b1d2250 8.57.1 2018-09-21 08:40:12 +09:00
629693355a Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-09-21 08:37:39 +09:00
00a3f8d392 Fix #2741 2018-09-21 08:37:26 +09:00
80b6e8090e fix(package): update @types/bcryptjs to version 2.4.2 (#2742) 2018-09-21 08:35:06 +09:00
a5f817d896 Fix #2744 2018-09-21 08:33:24 +09:00
51b0244cf2 fix(package): update websocket to version 1.0.28 (#2746)
Closes #2743
2018-09-21 04:30:29 +09:00
01131e2606 8.57.0 2018-09-20 17:23:55 +09:00
6283b7668e fix(package): update @types/koa-router to version 7.0.32 (#2740) 2018-09-20 17:23:26 +09:00
d058ecc4ea Resolve #2698 2018-09-20 17:21:16 +09:00
77a0450b5d 🎨 2018-09-20 16:21:51 +09:00
1dd1b9084f Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-09-20 16:16:08 +09:00
6341807d02 🎨 2018-09-20 16:16:01 +09:00
51a1f30225 fix(package): update @types/webpack to version 4.4.12 (#2739) 2018-09-20 06:29:03 +09:00
5422482696 Resolve #1153 2018-09-20 06:27:41 +09:00
cd7f8b080e Fix #2738 2018-09-20 04:56:24 +09:00
faf29b768f Make admin can delete any note 2018-09-19 17:29:03 +09:00
7576569dc9 8.56.0 2018-09-19 14:24:40 +09:00
ea3bcbbc37 キャッシュの設定を調整 2018-09-19 14:22:46 +09:00
d9f0e158a3 Implement #2736 2018-09-19 14:18:34 +09:00
195f676500 8.55.0 2018-09-19 08:38:18 +09:00
a9a2f4820b Add keyboard shortcut doc 2018-09-19 08:36:06 +09:00
8414db57f0 Specify AP Cache-Control (#2735) 2018-09-19 07:17:19 +09:00
609d68933e Add new shortcut 2018-09-19 02:51:06 +09:00
a23b8cebbc 8.54.0 2018-09-19 02:41:09 +09:00
89f6b03cd6 fix(package): update web-push to version 3.3.3 (#2733) 2018-09-19 02:39:57 +09:00
7bc9de03a6 fix(package): update webpack to version 4.19.1 (#2732) 2018-09-19 02:39:15 +09:00
3c865d6054 Add new shortcut 2018-09-19 02:35:32 +09:00
fd770b008e Add new shortcut 2018-09-19 02:32:44 +09:00
b0d60ef2c2 Add new shortcut 2018-09-19 02:27:19 +09:00
7b9cea06ef Fix 2018-09-19 02:26:06 +09:00
30608d3e22 8.53.0 2018-09-18 16:45:55 +09:00
8bf4e55338 Improve keyboard shortcuts 2018-09-18 16:45:20 +09:00
6ead1de383 Improve readability 2018-09-18 15:02:26 +09:00
3b628ec3c4 将来的にバグに繋がりかねない挙動を修正 2018-09-18 15:02:15 +09:00
0ed704d173 8.52.0 2018-09-18 14:54:01 +09:00
87b6ef0ec5 Improve keyboard shortcut 2018-09-18 14:53:17 +09:00
5184a07cf2 Improve usability 2018-09-18 14:50:13 +09:00
dba04cc59c Improve keyboard shortcuts 2018-09-18 14:43:54 +09:00
f4045fb5b3 Improve keyboard shortcuts 2018-09-18 14:39:18 +09:00
16c36163b4 Fix bug 2018-09-18 14:35:46 +09:00
1ac033ff18 Improve keyboard shortcut 2018-09-18 14:30:50 +09:00
ccfd48232a 8.51.0 2018-09-18 13:14:42 +09:00
429bf179dc Refactor: Better type annotations 2018-09-18 13:14:17 +09:00
8ba3fb13eb Fix bug 2018-09-18 13:12:41 +09:00
11496d887e Publish pinned notes (#2731) 2018-09-18 13:08:27 +09:00
bec48319ec 8.50.0 2018-09-18 12:43:24 +09:00
71a93b2b43 Refactor & Usability improvements 2018-09-18 12:42:56 +09:00
6ed3f9e414 リファクタリングなど 2018-09-18 12:34:41 +09:00
dc8f592c1f 8.49.0 2018-09-18 09:21:02 +09:00
f66c31c771 Improve usability & refactoring 2018-09-18 09:20:06 +09:00
55e2ae1408 Improve usability 2018-09-18 09:11:52 +09:00
19c72627fc Improve keyboard shortcut 2018-09-18 08:19:45 +09:00
2a4c53c3a4 8.48.0 2018-09-18 06:30:52 +09:00
1f2ebce8ed Resolve #1302 2018-09-18 06:29:47 +09:00
fcea9dacb7 Clean up: Remove unused import 2018-09-18 06:20:49 +09:00
908872f374 8.47.0 2018-09-18 05:36:14 +09:00
f688ceafb8 Merge pull request #2729 from syuilo/greenkeeper/@types/node-10.10.1
Update @types/node to the latest version 🚀
2018-09-18 05:35:49 +09:00
b47b5d6d8b Merge pull request #2728 from syuilo/l10n_develop
New Crowdin translations
2018-09-18 05:35:39 +09:00
31ce3aa312 キーボードショートカットを強化するなど 2018-09-18 05:35:06 +09:00
5b22d92e99 New translations ja-JP.yml (English) 2018-09-18 03:51:42 +09:00
df148e25da fix(package): update @types/node to version 10.10.1 2018-09-17 17:24:15 +00:00
4b26df5c3a New translations ja-JP.yml (English) 2018-09-18 02:19:32 +09:00
e765be4205 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-09-18 02:15:36 +09:00
f7d2457063 New translations ja-JP.yml (Norwegian) 2018-09-18 02:15:27 +09:00
6032d803aa New translations ja-JP.yml (Dutch) 2018-09-18 02:15:25 +09:00
0de371db38 New translations ja-JP.yml (Japanese, Kansai) 2018-09-18 02:15:22 +09:00
ce3797c4af 8.46.0 2018-09-18 02:15:19 +09:00
56dd8c298b New translations ja-JP.yml (Spanish) 2018-09-18 02:15:19 +09:00
3533257efe New translations ja-JP.yml (Russian) 2018-09-18 02:15:16 +09:00
dc2f08721d New translations ja-JP.yml (Portuguese) 2018-09-18 02:15:14 +09:00
66608a4131 New translations ja-JP.yml (Polish) 2018-09-18 02:15:11 +09:00
2fa90131eb New translations ja-JP.yml (Korean) 2018-09-18 02:15:08 +09:00
a51ed28db6 New translations ja-JP.yml (Italian) 2018-09-18 02:15:06 +09:00
5ec290663b New translations ja-JP.yml (German) 2018-09-18 02:15:03 +09:00
1374d6e34d New translations ja-JP.yml (French) 2018-09-18 02:15:00 +09:00
45ade17c58 New translations ja-JP.yml (English) 2018-09-18 02:14:57 +09:00
c753e26187 New translations ja-JP.yml (Chinese Simplified) 2018-09-18 02:14:54 +09:00
577929eed1 New translations ja-JP.yml (Catalan) 2018-09-18 02:14:51 +09:00
1fde8a8fb0 Merge pull request #2727 from syuilo/greenkeeper/@types/node-10.10.0
Update @types/node to the latest version 🚀
2018-09-18 02:14:32 +09:00
77e53cbf9e Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-09-18 02:14:16 +09:00
ab83e08bc7 メッセージタイムラインを追加 2018-09-18 02:14:12 +09:00
2fad6e6d5f Refactor 2018-09-18 02:13:42 +09:00
a3604a6c95 Merge pull request #2722 from syuilo/l10n_develop
New Crowdin translations
2018-09-17 23:12:54 +09:00
44f6fe6f1f Refactor: Extract shouldMuteThisNote function 2018-09-17 23:07:15 +09:00
311b4e90ca No lint when test 2018-09-17 22:51:25 +09:00
f5a937c523 Better hashtag parsing 2018-09-17 22:51:10 +09:00
0632a3ed3f fix(package): update @types/node to version 10.10.0 2018-09-17 08:10:08 +00:00
71bada97df 8.45.1 2018-09-17 12:19:54 +09:00
62509edcbe Refactor 2018-09-17 12:18:59 +09:00
f97cdfaa20 Fix #2725 2018-09-17 11:59:24 +09:00
67ec10e86d Add untilId param 2018-09-17 11:43:53 +09:00
481b3f2c58 New translations ja-JP.yml (English) 2018-09-17 09:11:27 +09:00
7d599a68ea pong 2018-09-17 09:07:46 +09:00
7ccff732b8 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-09-17 09:07:05 +09:00
7587c896d5 8.45.0 2018-09-17 09:06:56 +09:00
91297f1ab3 Merge pull request #2717 from syuilo/l10n_develop
New Crowdin translations
2018-09-17 09:06:25 +09:00
d872a16fe0 🎨 2018-09-17 09:05:51 +09:00
60aa35adf8 New translations ja-JP.yml (Norwegian) 2018-09-17 09:01:59 +09:00
5035b66773 New translations ja-JP.yml (Dutch) 2018-09-17 09:01:57 +09:00
fa9da8ecab New translations ja-JP.yml (Japanese, Kansai) 2018-09-17 09:01:54 +09:00
1f9bca7188 New translations ja-JP.yml (Spanish) 2018-09-17 09:01:52 +09:00
ffa5bdeb50 New translations ja-JP.yml (Russian) 2018-09-17 09:01:49 +09:00
e6bfb7398e New translations ja-JP.yml (Portuguese) 2018-09-17 09:01:47 +09:00
6def0c776f New translations ja-JP.yml (Polish) 2018-09-17 09:01:45 +09:00
24bae9eaed New translations ja-JP.yml (Korean) 2018-09-17 09:01:43 +09:00
fb5175a283 New translations ja-JP.yml (Italian) 2018-09-17 09:01:40 +09:00
6e49437154 New translations ja-JP.yml (German) 2018-09-17 09:01:38 +09:00
2511ed56ac New translations ja-JP.yml (French) 2018-09-17 09:01:35 +09:00
c4bfc99cf5 New translations ja-JP.yml (English) 2018-09-17 09:01:33 +09:00
4efe38440d New translations ja-JP.yml (Chinese Simplified) 2018-09-17 09:01:30 +09:00
4a5f2c3c40 New translations ja-JP.yml (Catalan) 2018-09-17 09:01:27 +09:00
109738ccb9 ハッシュタグタイムラインを実装 2018-09-17 09:00:20 +09:00
25438c4d64 New translations ja-JP.yml (French) 2018-09-17 00:01:10 +09:00
144 changed files with 2848 additions and 819 deletions

View File

@ -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')

View File

@ -155,8 +155,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -155,8 +155,10 @@ common:
home: "Startseite"
local: "Lokal"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "Global"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "Mitteilungen"
list: "Listen"
swap-left: "Nach links"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "Global"
mentions: "あなた宛て"
messages: "メッセージ"
list: "Listen"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -110,7 +110,7 @@ common:
verified-user: "Verified account"
disable-animated-mfm: "Disable animated texts in a post"
always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-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"
@ -155,8 +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"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
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"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "Social"
global: "Global"
mentions: "Mentions"
messages: "Messages"
mobile/views/pages/tag.vue:
no-posts-found: "No posts \"{}\" found."
mobile/views/pages/welcome.vue:

View File

@ -155,8 +155,10 @@ common:
home: "Inicio"
local: "Local"
hybrid: "Social"
hashtag: "ハッシュタグ"
global: "Global"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "Notificaciones"
list: "Listado"
swap-left: "Desplazar a la izq."
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
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"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -112,7 +112,7 @@ common:
always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
show-full-acct: "ユーザー名のホストを省略しない"
reduce-motion: "UIの動きを減らす"
reduce-motion: "Réduire les animations dans linterface utilisateur"
this-setting-is-this-device-only: "Uniquement sur cet appareil"
do-not-use-in-production: 'Il sagit dune version de développement. Ne pas utiliser dans un environnement de production.'
reversi:
@ -155,8 +155,10 @@ common:
home: "Accueil"
local: "Local"
hybrid: "Social"
hashtag: "ハッシュタグ"
global: "Global"
mentions: "あなた宛て"
mentions: "Mentions"
direct: "ダイレクト投稿"
notifications: "Notifications"
list: "Liste"
swap-left: "Déplacer à gauche"
@ -260,8 +262,8 @@ common/views/components/connect-failed.troubleshooter.vue:
flush: "Vider le cache"
set-version: "Choisissez une version"
common/views/components/media-banner.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
sensitive: "Contenu sensible"
click-to-show: "Cliquer pour afficher"
common/views/components/cw-button.vue:
hide: "Masquer"
show: "Voir plus"
@ -484,7 +486,7 @@ desktop/views/components/charts.vue:
drive-files-total: "ドライブのファイル数の累計"
network-requests: "Requêtes"
network-time: "Temps de réponse"
network-usage: "通信量"
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"
@ -791,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 dabonnement en attente dapprobation"
other: "Autre"
is-bot: "Ce compte est un Bot"
is-cat: "Ce compte est un Chat"
@ -808,8 +810,13 @@ desktop/views/components/timeline.vue:
local: "Local"
hybrid: "Social"
global: "Global"
mentions: "あなた宛て"
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: "さん"
@ -1134,7 +1141,8 @@ mobile/views/pages/home.vue:
local: "Local"
hybrid: "Social"
global: "Global"
mentions: "あなた宛て"
mentions: "Mentions"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "Pas de message avec un hashtag {} trouvé."
mobile/views/pages/welcome.vue:
@ -1175,7 +1183,7 @@ mobile/views/pages/settings/settings.profile.vue:
avatar: "Avatar"
banner: "Bannière"
is-cat: "Ce compte est un Bot"
is-locked: "フォローを承認制にする"
is-locked: "Demande dabonnement en attente dapprobation"
advanced: "Avancé"
privacy: "Vie privée"
save: "Mettre à jour le profil"

View File

@ -155,8 +155,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -166,8 +166,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -326,6 +328,7 @@ common/views/components/note-menu.vue:
copy-link: "リンクをコピー"
favorite: "お気に入り"
pin: "ピン留め"
unpin: "ピン留め解除"
delete: "削除"
delete-confirm: "この投稿を削除しますか?"
remote: "投稿元で見る"
@ -777,6 +780,8 @@ desktop/views/components/settings.vue:
choose-wallpaper: "壁紙を選択"
delete-wallpaper: "壁紙を削除"
dark-mode: "ダークモード"
use-shadow: "UIに影を使用"
rounded-corners: "UIの角を丸める"
circle-icons: "円形のアイコンを使用"
contrasted-acct: "ユーザー名にコントラストを付ける"
gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用"
@ -915,7 +920,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
@ -1317,6 +1327,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"

View File

@ -13,12 +13,12 @@ common:
rich-contents: "投稿"
rich-contents-desc: "思っとること、タイガースの実況、他に言いたいことがあればなんでも言ってええで。いろんな構文あるから、好きにつこうてくれや。画像や動画、アンケートも添付できるで。"
reaction: "リアクション"
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクション付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
ui: "インターフェス"
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
reaction-desc: "「何思っとるか言うてみ?」言われても、わからんわ!リアクション使うて、エモーションをダイレクトに伝えるんや!Misskeyは、他のユーザーの投稿にいろんなリアクション付けられるんや。もう「いいね」とかいうもんだけのSNSには戻れへんわな。551の豚まん食うてみもう他の豚まん食えへんで"
ui: "インターフェス"
ui-desc: "このUIええ言うてたで、知らんけど。あんたの好みのUIなんて知ったこっちゃない。Misskeyは好きにいじれるからな、レイアウトやデザイン変えたり、色んなウィジェットひっつけたりして、あんただけのMisskey作って楽しんでな"
drive: "ドライブ"
drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんか?もしくは、アップロードしたファイルフォルダ分けして整理したくなったことはありませんかMisskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。"
outro: "他にもMisskeyにしかない機能はまだまだあるので、ぜひあなた自身の目で確かめてください。Misskeyは分散型SNSなので、このインスタンスが気に入らなければ他のインスタンスを試すこともできます。それでは、GLHF!"
drive-desc: "「こないだの画像、どこやったかな…また投稿したいんやけど…」「さっきのファイルあのフォルダに直しといて」そんなこと言わんとって。Misskeyはもとからドライブ機能持っとるさかい、心配あらへん。ファイルの「わけわけ」したってな。"
outro: "Misskeyの機能は無限大や知らんけど。知らん言うとるやんけ、あんたが見に行けやMisskeyは分散型SNSやから、ここがあかんくても他がある。阪神でもオリックスでもワイは応援するで"
adblock:
detected: "広告ブロッカーを無効にしてや"
warning: "<strong>Misskeyは広告を掲載してへん</strong>けど、広告をブロックしはる機能がおると一部の機能が利用できんくなったり、不具合が発生するかも分からん。知らんけど。"
@ -84,11 +84,11 @@ common:
note-visibility:
public: "公開"
home: "ホーム"
home-desc: "ホームタイムラインにのみ公開"
home-desc: "ホームタイムライン以外に見せんとって"
followers: "フォロワー"
followers-desc: "自分のフォロワーにのみ公開"
followers-desc: "自分のフォロワー以外に見せんとって"
specified: "ダイレクト"
specified-desc: "指定したユーザーにのみ公開"
specified-desc: "今から言うユーザー以外に見せんとってや"
private: "非公開"
note-placeholders:
a: "今なにしてん?"
@ -109,12 +109,12 @@ common:
use-contrast-reversi-stones: "リバーシのアイコンにコントラストをつけんで!"
verified-user: "アメちゃん付きアカウント"
disable-animated-mfm: "投稿内のちょろちょろ動いてんのを止める"
always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
show-full-acct: "ユーザー名のホストを省略しない"
reduce-motion: "UI動きを減らす"
always-show-nsfw: "閲覧注意?見せたらあかん?そんなん知らんわ、見せろや!"
always-mark-nsfw: "わからんからとりあえずメディアは見せたらあかん"
show-full-acct: "ユーザー名のホストも出したる"
reduce-motion: "UI動き過ぎや、静かにしてや"
this-setting-is-this-device-only: "このデバイスのみ"
do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
do-not-use-in-production: '開発ビルド。本番環境で使わんといて!知らんで!'
reversi:
drawn: "おあいこ"
my-turn: "あんさんのターンや"
@ -155,8 +155,10 @@ common:
home: "うち"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あた宛て"
mentions: "あた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動や!"
@ -260,11 +262,11 @@ common/views/components/connect-failed.troubleshooter.vue:
flush: "キャッシュの削除"
set-version: "バージョン指定"
common/views/components/media-banner.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
sensitive: "見せたらあかん"
click-to-show: "押してみ、見せたるわ"
common/views/components/cw-button.vue:
hide: "隠す"
show: "もっと見る"
hide: "見せへんわ"
show: "もっとあるやろ!"
common/views/components/messaging.vue:
search-user: "ユーザーを探す"
you: "あんさん"
@ -301,7 +303,7 @@ common/views/components/note-menu.vue:
pin: "ピン留め"
delete: "ほかす"
delete-confirm: "この投稿を削除してもええか?"
remote: "投稿元で見る"
remote: "投稿元に行ってみよか"
common/views/components/poll.vue:
vote-to: "「{}」に投票や!"
vote-count: "{}票"
@ -316,7 +318,7 @@ common/views/components/poll-editor.vue:
add: "+選択肢を追加"
destroy: "アンケートをほかそ"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
choose-reaction: "リアクション、どれにするんや?"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -328,7 +330,7 @@ common/views/components/signin.vue:
login-failed: "なんかログインできんかったわ。ユーザー名とパスワードとかを確認してや。"
common/views/components/signup.vue:
invitation-code: "招待コード"
invitation-info: "招待コードをお持ちでない方は、<a href=\"{}\">管理者</a>まで連絡ください。"
invitation-info: "招待コードをもっとらんのやったら、<a href=\"{}\">管理者</a>まで連絡してや。"
username: "ユーザー名"
checking: "確認中や…"
available: "使えるで"
@ -336,7 +338,7 @@ common/views/components/signup.vue:
error: "通信あかんわ"
invalid-format: "a~z、A~Z、0~9、_が使えるで"
too-short: "1文字以上やで"
too-long: "20文字以内でお願いします"
too-long: "20文字以内で"
password: "パスワード"
password-placeholder: "8文字以上にしときや"
weak-password: "へぼいパスワード"
@ -350,8 +352,8 @@ common/views/components/signup.vue:
create: "アカウント作成"
some-error: "何かよう分からんけど、アカウントの作成に失敗してしもたわ。すまんがもっぺん試してくれへんか?"
common/views/components/special-message.vue:
new-year: "Happy New Year!"
christmas: "Merry Christmas!"
new-year: "おおきに。今年もよろしゅう。"
christmas: "メリークリスマス!"
common/views/components/stream-indicator.vue:
connecting: "つないどるで"
reconnecting: "つなぎ直すで"
@ -368,19 +370,19 @@ common/views/components/uploader.vue:
common/views/components/visibility-chooser.vue:
public: "公開"
home: "ホーム"
home-desc: "ホームタイムラインにのみ公開"
home-desc: "ホームタイムライン以外に見せんとって"
followers: "フォロワー"
followers-desc: "自分のフォロワーにのみ公開"
followers-desc: "自分のフォロワー以外に見せんとって"
specified: "ダイレクト"
specified-desc: "指定したユーザーにのみ公開"
specified-desc: "今から言うユーザー以外に見せんとってや"
private: "非公開"
common/views/components/trends.vue:
count: "{}人が投稿"
empty: "トレンドなし"
empty: "流行は自分で作るんや"
common/views/widgets/broadcast.vue:
fetching: "見てみるわ…"
no-broadcasts: "お知らせはあらへんで"
have-a-nice-day: "良い一日を"
have-a-nice-day: "おおきに"
next: "次"
common/views/widgets/calendar.vue:
year: "{}年"
@ -434,21 +436,21 @@ common/views/widgets/tips.vue:
tips-line25: "対応ブラウザやったらMisskeyを開いとらんでも通知を受け取れんで"
common/views/pages/follow.vue:
signed-in-as: "{}としてサインイン中"
following: "フォロー"
following: "フォローしとる"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-request: "フォロー申請"
request-pending: "フォローの許し待っとる"
follow-request: "フォロー許してくれや!言うてみる"
desktop:
banner-crop-title: "バナーとして表示する部分を選択"
banner-crop-title: "どこバナーとして出す?"
banner: "バナー"
uploading-banner: "新しいバナーをアップロードしとるで"
banner-updated: "バナーを更新したで"
choose-banner: "バナーにする画像選んでや"
avatar-crop-title: "どこアバターとして出しとく?"
avatar: "アバター"
uploading-avatar: "新しいアバターをアップロードしています"
avatar-updated: "アバターを更新しました"
choose-avatar: "アバターにする画像を選択"
uploading-avatar: "新しいアバターをアップロードしとるで"
avatar-updated: "アバターを更新した"
choose-avatar: "アバターにする画像選んでや"
invalid-filetype: "この形式のファイル無理やねん"
desktop/views/components/activity.chart.vue:
total: "Black ... Total"
@ -457,7 +459,7 @@ desktop/views/components/activity.chart.vue:
renotes: "Green ... Renotes"
desktop/views/components/activity.vue:
title: "アクティビティ"
toggle: "表示を切り替え"
toggle: "表示変える"
desktop/views/components/calendar.vue:
title: "{1}年 {2} 月"
prev: "前の月"
@ -472,10 +474,10 @@ desktop/views/components/charts.vue:
drive: "ドライブ"
network: "ネットワーク"
charts:
notes: "投稿の増減 (統合)"
notes: "投稿の増減(統合)"
local-notes: "投稿の増減 (ローカル)"
remote-notes: "投稿の増減 (リモート)"
notes-total: "投稿の累計"
notes-total: "全部の投稿"
users: "ユーザーの増減"
users-total: "ユーザーの累計"
drive: "ドライブ使用量の増減"
@ -486,21 +488,21 @@ desktop/views/components/charts.vue:
network-time: "応答時間"
network-usage: "通信量"
desktop/views/components/choose-file-from-drive-window.vue:
choose-file: "ファイル選択"
upload: "PCからドライブにファイルをアップロード"
choose-file: "ファイル選択しとる"
upload: "PCからドライブにファイル上げる"
cancel: "やめとくわ"
ok: "決定"
choose-prompt: "ファイルを選択"
ok: "そうする"
choose-prompt: "ファイル選んでや"
desktop/views/components/choose-folder-from-drive-window.vue:
cancel: "やめとくわ"
ok: "決定"
choose-prompt: "フォルダを選択"
ok: "そうする"
choose-prompt: "フォルダ選んでや"
desktop/views/components/crop-window.vue:
skip: "クロップをスキップ"
skip: "クロップせーへんわ"
cancel: "やめとくわ"
ok: "決定"
ok: "そうする"
desktop/views/components/drive-window.vue:
used: "使用中"
used: "使うとる"
drive: "ドライブ"
desktop/views/components/drive.file.vue:
avatar: "アイコン"
@ -536,17 +538,17 @@ desktop/views/components/drive.nav-folder.vue:
desktop/views/components/drive.vue:
search: "検索"
load-more: "もっとあらへんのか!"
empty-draghover: "ドロップですか?いいですよ、ボクはカワイイですからね"
empty-draghover: "ドロップするにゃ!お魚以外なら何でもいいにゃ!"
empty-drive: "ドライブには何もあらへんで。"
empty-drive-description: "右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできます。"
empty-folder: "このフォルダーは空です"
empty-drive-description: "右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできんねん。"
empty-folder: "このフォルダーは空"
unable-to-process: "あかん、無理やわ"
circular-reference-detected: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。"
circular-reference-detected: "移動先のフォルダーは、移動するフォルダーのサブフォルダー。"
unhandled-error: "ようわからん"
url-upload: "URLアップロード"
url-of-file: "このURLのファイルをアップロードしたいねん"
url-upload-requested: "アップロードしたい言うといたで"
may-take-time: "アップロードが完了するまで時間かかる場合があります。"
may-take-time: "アップロード終わるまで時間かかるわ、知らんけど。たこ焼き何個食べれるやろか…"
create-folder: "フォルダー作成"
folder-name: "フォルダー名"
contextmenu:
@ -577,7 +579,7 @@ desktop/views/components/friends-maker.vue:
empty: "おもろいユーザー居らんかったわ"
fetching: "読みこんどるで…"
refresh: "もっとあるやろ!"
close: "閉じる"
close: "さいなら"
desktop/views/components/game-window.vue:
game: "ゲーム"
desktop/views/components/home.vue:
@ -604,9 +606,9 @@ desktop/views/components/notes.note.vue:
reply: "返す"
renote: "Renote"
add-reaction: "リアクション"
detail: "詳細"
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
detail: "もっと"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
desktop/views/components/notes.vue:
error: "あかん、読み込めへんわ"
retry: "もっぺん"
@ -654,11 +656,11 @@ desktop/views/components/renote-form.vue:
quote: "持ってくる…"
cancel: "やめとくわ"
renote: "Renote"
reposting: "しています..."
success: "Renoteしました!"
failure: "Renoteに失敗しました"
reposting: "やっとります..."
success: "Renoteした"
failure: "Renoteでけへん"
desktop/views/components/renote-form-window.vue:
title: "この投稿をRenoteしますか?"
title: "この投稿をRenoteしてもええか?"
desktop/views/components/settings-window.vue:
settings: "設定"
desktop/views/components/settings.vue:
@ -667,27 +669,27 @@ desktop/views/components/settings.vue:
apps: "アプリ"
mute: "ミュート"
drive: "ドライブ"
security: "セキュリティ"
signin: "サインイン履歴"
security: "守護神セキュリティ"
signin: "こんな感じでサインインしたらしいで"
password: "パスワード"
2fa: "二段階認証"
other: "その他"
license: "ライセンス"
behaviour: "動"
fetch-on-scroll: "スクロールで自動読み込み"
fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。"
behaviour: "動"
fetch-on-scroll: "スクロールしたらもっと見せてや"
fetch-on-scroll-desc: "ページを下までスクロールしたときに自動でもっとコンテンツを読み込むで。"
note-visibility: "投稿の公開範囲"
default-note-visibility: "デフォルトの公開範囲"
remember-note-visibility: "投稿の公開範囲を記憶する"
default-note-visibility: "もとからの公開範囲"
remember-note-visibility: "投稿の公開範囲おぼえといて"
auto-popout: "ウィンドウの自動ポップアウト"
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
advanced: "詳細設定"
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトすんで。この設定はブラウザに記憶されんで。"
advanced: "もっと設定"
api-via-stream: "ストリームを経由したAPIリクエスト"
api-via-stream-desc: "この設定をオンにすると、WebSocket接続を経由してAPIリクエストが行われんで(パフォーマンス向上するかも、知らんけど)。オフにすると、ネイティブの fetch API が利用されるで。この設定はこのデバイスのみ有効やで。"
display: "デザインと表示"
display: "見た感じ"
customize: "ホームをカスタマイズ"
choose-wallpaper: "壁紙を選択"
delete-wallpaper: "壁紙を削除"
choose-wallpaper: "壁紙選ぶ"
delete-wallpaper: "壁紙ほかす"
dark-mode: "夜にすんで"
circle-icons: "アイコンもタコ焼きも丸いやんな?"
contrasted-acct: "ユーザー名ようわからんし見やすしといて"
@ -720,39 +722,39 @@ desktop/views/components/settings.vue:
cache-cleared: "キャッシュお掃除したで"
cache-cleared-desc: "もっぺんページ読みこみ直してくれや"
auto-watch: "投稿勝手にウォッチしといてや"
auto-watch-desc: "リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。"
about: "Misskeyについて"
operator: "このサーバーの運営者"
auto-watch-desc: "リアクションしたり返信したりした投稿に関する通知を勝手に受け取るようにすんで。"
about: "Misskeyってなんや?"
operator: "このサーバー誰のや"
update: "Misskey Update"
version: "バージョン:"
latest-version: "最新のバージョン:"
update-checking: "アップデートを確認中"
do-update: "アップデートを確認"
update-settings: "詳細設定"
prevent-update: "アップデートを延期する(非推奨)"
prevent-update-desc: "この設定をオンにしてもアップデートが反映される場合があります。この設定はこのデバイスのみ有効で。"
no-updates: "利用可能な更新はありません"
update-checking: "アップデートはあらへんか…"
do-update: "アップデートあるか見てみる"
update-settings: "もっと設定"
prevent-update: "アップデートしたないわ、また今度や(やめときや)"
prevent-update-desc: "この設定をオンにしとってもアップデートが反映される場合があるかも分からん、知らんけど気ぃつけてや。この設定はこのデバイスのみ有効で。"
no-updates: "使える更新はあらへん"
no-updates-desc: "つこてるMisskeyは最新や"
update-available: "新しいバージョンが利用可能や"
update-available-desc: "ページを再度読み込みすると更新が適用されるで。"
advanced-settings: "高度な設定"
debug-mode: "デバッグモードを有効にする"
debug-mode-desc: "この設定はブラウザに記憶されます。"
experimental: "実験的機能を有効にする"
experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。"
advanced-settings: "ワイにはわからん設定"
debug-mode: "デバッグモードにしてみる"
debug-mode-desc: "この設定はブラウザに記憶されんで。"
experimental: "お試し機能使うてみる"
experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になるかも分からん、知らんけど気ぃつけてや。この設定はブラウザに記憶されんで。"
tools: "ツール"
task-manager: "タスクマネージャ"
third-parties: "サードパーティ"
desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
intro: "二段階認証を設定すると、サインイン時にパスワードだけとちゃうくて、予め登録しておいた物理的なデバイス(例えばあんさんのスマートフォンなど)も必要になり、よりセキュリティが向上すんで。"
detail: "詳細..."
url: "https://www.google.co.jp/intl/ja/landing/2step/"
caution: "登録したデバイスを紛失するなどした場合、Misskeyにサインインできくなりますのでご注意ください。"
register: "デバイス登録する"
already-registered: "既に設定は完了しています。"
unregister: "設定を解除"
unregistered: "二段階認証が無効になりました。"
enter-password: "パスワードを入力してください"
caution: "登録したデバイスを紛失してもうたら、もうMisskeyにサインインできくなるで。"
register: "デバイス登録する"
already-registered: "もう設定終わっとるわ"
unregister: "設定をほかす"
unregistered: "二段階認証もうせーへんで"
enter-password: "パスワードを入れてや"
authenticator: "まず、Google Authenticatorとかのをつこてるデバイスにインストールしてや:"
howtoinstall: "インストール方法はここやで"
scan: "んで、ここに出とるQRコードをスキャンしてな:"
@ -777,28 +779,28 @@ desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはおらんで"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
enter-new-password: "新しいパスワードを入力してください"
enter-new-password-again: "もう一度新しいパスワードを入力してください"
not-match: "新しいパスワードが一致しません"
changed: "パスワードを変更しました"
enter-current-password: "のパスワードを入れてや"
enter-new-password: "さらのパスワード入れてや"
enter-new-password-again: "もういっぺんさらのパスワードを入れてや"
not-match: "パスワードがおうとらん"
changed: "パスワード変えたわ"
desktop/views/components/settings.profile.vue:
avatar: "アイコン"
choice-avatar: "画像を選択"
choice-avatar: "画像選んでや"
name: "名前"
location: "場所"
description: "自己紹介"
description: "ワイのこと"
birthday: "誕生日"
save: "保存"
locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする"
locked-account: "アカウント守る"
is-locked: "他人のフォローは許してからや!"
other: "その他"
is-bot: "このアカウントはBotで"
is-cat: "このアカウントはCatで"
profile-updated: "プロフィールを更新しました"
is-bot: "このアカウントはBotで"
is-cat: "このアカウントはCatで"
profile-updated: "プロフィールを更新した"
desktop/views/components/sub-note-content.vue:
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
media-count: "{}つのメディア"
poll: "アンケート"
desktop/views/components/taskmanager.vue:
@ -808,22 +810,27 @@ desktop/views/components/timeline.vue:
local: "ローカル"
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あた宛て"
mentions: "あた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグ増やす"
add-list: "リストに入れる"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえり、"
adjective: "ん"
adjective: "ん"
desktop/views/components/ui.header.account.vue:
profile: "プロフィール"
drive: "ドライブ"
favorites: "お気に入り"
lists: "リスト"
follow-requests: "フォロー申請"
follow-requests: "フォロー許してくれや!言うてみる"
customize: "ホームをカスタマイズ"
admin: "管理"
settings: "設定"
signout: "サインアウト"
dark: "闇に飲まれる"
signout: "さいなら"
dark: "ナイトゲームじゃ!"
desktop/views/components/ui.header.nav.vue:
home: "ホーム"
deck: "デッキ"
@ -836,9 +843,9 @@ desktop/views/components/ui.header.post.vue:
desktop/views/components/ui.header.search.vue:
placeholder: "検索"
desktop/views/components/received-follow-requests-window.vue:
title: "フォロー申請"
accept: "承認"
reject: "拒否"
title: "フォロー許してくれや!言うてみる"
accept: "許す"
reject: "許さん"
desktop/views/components/user-lists-window.vue:
title: "リスト"
create-list: "新しいリストを作成"
@ -849,14 +856,14 @@ desktop/views/components/user-preview.vue:
followers: "フォロワー"
desktop/views/components/users-list.vue:
all: "すべて"
iknow: "知り合い"
iknow: "知っとる"
load-more: "もっと"
fetching: "読み込んでいます"
fetching: "読みこんどるで…"
desktop/views/components/users-list-item.vue:
followed: "フォローされています"
followed: "フォローされとるで"
desktop/views/components/window.vue:
popout: "ポップアウト"
close: "閉じる"
close: "さいなら"
desktop/views/pages/admin/admin.vue:
dashboard: "ダッシュボード"
drive: "ドライブ"
@ -864,15 +871,15 @@ desktop/views/pages/admin/admin.vue:
update: "更新"
desktop/views/pages/admin/admin.dashboard.vue:
dashboard: "ダッシュボード"
all-users: "全てのユーザー"
original-users: "このインスタンスのユーザー"
all-users: "知り合い全員や"
original-users: "ここの人らだけ"
all-notes: "全ての投稿"
original-notes: "このインスタンスの投稿"
invite: "招待"
invite: "来てや"
desktop/views/pages/admin/admin.suspend-user.vue:
suspend-user: "ユーザーの凍結"
suspend: "凍結"
suspended: "凍結しました"
suspended: "凍結した"
desktop/views/pages/admin/admin.unsuspend-user.vue:
unsuspend-user: "ユーザーの凍結の解除"
unsuspend: "凍結の解除"
@ -882,33 +889,33 @@ desktop/views/pages/admin/admin.verify-user.vue:
verify: "公式アカウントにする"
verified: "公式アカウントにしたで"
desktop/views/pages/admin/admin.unverify-user.vue:
unverify-user: "ユーザーの公式アカウント解除"
unverify: "公式アカウントを解除する"
unverified: "公式アカウントを解除しました"
unverify-user: "ユーザーの公式アカウントにせーへん"
unverify: "公式アカウントにはさせへんで"
unverified: "公式アカウントを解除した"
desktop/views/pages/deck/deck.tl-column.vue:
is-media-only: "メディア投稿のみ"
is-media-only: "メディア投稿だけや"
is-media-view: "メディアビュー"
edit: "オプション"
desktop/views/pages/deck/deck.note.vue:
reposted-by: "{}がRenote"
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
desktop/views/pages/stats/stats.vue:
all-users: "全てのユーザー"
original-users: "このインスタンスのユーザー"
original-users: "ここの人らだけ"
all-notes: "全ての投稿"
original-notes: "このインスタンスの投稿"
desktop/views/pages/welcome.vue:
about: "詳しく..."
gotit: "わかった"
about: "もっと…"
gotit: "ほい"
signin: "サインイン"
signup: "サインアップ"
signin-button: "サインイン中…"
signup-button: "サインアップ"
timeline: "タイムライン"
announcements: "お知らせ"
announcements: "知っときや"
photos: "最近の画像"
powered-by-misskey: "Powered by <b>Misskey</b>."
powered-by-misskey: "<b>Misskey</b>のおかげや"
info: "情報"
desktop/views/pages/drive.vue:
title: "ドライブ"
@ -917,41 +924,41 @@ desktop/views/pages/favorites.vue:
desktop/views/pages/home-customize.vue:
title: "ホームをカスタマイズ"
desktop/views/pages/note.vue:
prev: "前の投稿"
next: "次の投稿"
prev: "前のやつ"
next: "次のやつ"
desktop/views/pages/selectdrive.vue:
title: "ファイルを選択してください"
title: "ファイルを選択して"
ok: "決定"
cancel: "やめとくわ"
upload: "PCからドライブにファイルをアップロード"
upload: "PCからドライブにファイル上げる"
desktop/views/pages/search.vue:
not-available: "検索機能はインスタンスの設定で無効になっています。"
not-found: "「{}」に関する投稿は見つかりませんでした。"
not-available: "検索機能は使えへんわ。管理者がそう言うとる。"
not-found: "「{}」に関する投稿はあらへん。"
desktop/views/pages/share.vue:
share-with: "{}で共有"
desktop/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
no-posts-found: "ハッシュタグ「{}」が付けられた投稿はあらへん。"
desktop/views/pages/user-list.users.vue:
users: "ユーザー"
add-user: "ユーザーを追加"
add-user: "ユーザー増やす"
username: "ユーザー名"
desktop/views/pages/user/user.followers-you-know.vue:
title: "知り合いのフォロワー"
loading: "読み込み中"
no-users: "知り合いのフォロワーはいません"
title: "知っとるフォロワー"
loading: "読み込んどる…"
no-users: "フォロワー全員知らんわ"
desktop/views/pages/user/user.friends.vue:
title: "よ話すユーザー"
loading: "読み込み中"
no-users: "よ話すユーザーはいません"
title: "よ話すツレ"
loading: "読み込んどる…"
no-users: "よ話すツレは居らん"
desktop/views/pages/user/user.vue:
is-suspended: "このユーザーは凍結されています。"
is-remote: "このユーザーはリモートユーザーです。"
view-remote: "正確な情報を見る"
is-suspended: "このユーザーはあかんわ。凍結されとる。"
is-remote: "このユーザーはリモートユーザー。"
view-remote: "ちゃんとした情報を見る"
desktop/views/pages/user/user.home.vue:
last-used-at: "最終アクセス"
last-used-at: "最後いつ来た?"
desktop/views/pages/user/user.photos.vue:
title: "写真"
loading: "読み込み中"
loading: "読み込んどる…"
no-photos: "写真はあらへんで"
desktop/views/pages/user/user.profile.vue:
follows-you: "フォローされとるで"
@ -967,12 +974,12 @@ desktop/views/pages/user/user.header.vue:
posts: "投稿"
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
is-bot: "このアカウントはBot"
desktop/views/pages/user/user.timeline.vue:
default: "投稿"
with-replies: "投稿と返信"
with-media: "メディア"
empty: "このユーザーはまだ何も投稿していないようです。"
empty: "このユーザーはまだ何も投稿しとらんようや。"
desktop/views/widgets/messaging.vue:
title: "メッセージ"
desktop/views/widgets/notifications.vue:
@ -986,76 +993,76 @@ desktop/views/widgets/post-form.vue:
title: "投稿"
note: "投稿"
desktop/views/widgets/profile.vue:
update-banner: "クリックバナー編集"
update-avatar: "クリックアバター編集"
update-banner: "クリックしてバナー編集"
update-avatar: "クリックしてアバター編集"
desktop/views/widgets/trends.vue:
title: "トレンド"
title: "流行"
refresh: "他を見る"
nothing: "ありません!"
nothing: "あらへん!"
desktop/views/widgets/users.vue:
title: "おすすめユーザー"
refresh: "他を見る"
no-one: "いません!"
no-one: "おらん!"
mobile/views/components/drive.vue:
drive: "ドライブ"
used: "使用中"
used: "使うとる"
folder-count: "フォルダ"
count-separator: "、"
file-count: "ファイル"
load-more: "もっと読み込む"
load-more: "もっとあらへんのか!"
nothing-in-drive: "ドライブには何もあらへんで。"
folder-is-empty: "このフォルダは空です"
prompt: "何をしますか(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
folder-is-empty: "このフォルダ何もないわ"
prompt: "何すんの(数字を入れてや): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
deletion-alert: "フォルダの削除は未実装やねん...。堪忍な!"
folder-name: "フォルダー名"
root-rename-alert: "現在る場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
root-move-alert: "現在る場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
url-prompt: "アップロードしたいファイルのURL"
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
root-rename-alert: "現在る場所はルートで、フォルダとちゃうから名前の変更はできん。名前を変更したいフォルダに移動してからやって。"
root-move-alert: "現在る場所はルートで、フォルダとちゃうから移動はできん。移動したいフォルダに移動してからやって。"
url-prompt: "このURLのファイルをアップロードしたいねん"
uploading: "アップロードをリクエストした。アップロードが完了するまで時間がかかるかも分からん、知らんけど。"
mobile/views/components/drive-file-detail.vue:
rename: "名前を変"
rename: "名前を変えるで"
mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択"
select-file: "ファイル選んでや"
mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択"
select-folder: "フォルダ選んでや"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
nsfw: "ちょっと見せられへんわ"
mobile/views/components/drive.file-detail.vue:
download: "ダウンロード"
rename: "名前を変"
rename: "名前を変えるで"
move: "移動"
hash: "ハッシュ (md5)"
hash: "ハッシュ(md5)"
exif: "EXIF"
nsfw: "閲覧注意"
nsfw: "ちょっと見せられへんわ"
mobile/views/components/media-image.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
sensitive: "見たらあかんで"
click-to-show: "押してみ、見せたるわ"
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
sensitive: "ちょっと見せられへんわ"
click-to-show: "押してみ、見せたるわ"
mobile/views/components/follow-button.vue:
following: "フォロー"
following: "フォローしとる"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-request: "フォロー申請"
request-pending: "フォローの許し待っとる"
follow-request: "フォロー許してくれや!言うてみる"
mobile/views/components/friends-maker.vue:
title: "気になるユーザーをフォロー"
empty: "おすすめのユーザーは見つかりませんでした。"
fetching: "読み込んでいます"
refresh: "もっと見る"
close: "閉じる"
title: "おもろそうやな"
empty: "おすすめのユーザーはおらん。"
fetching: "読みこんどるで…"
refresh: "もっとあるやろ!"
close: "さいなら"
mobile/views/components/note.vue:
reposted-by: "{}がRenote"
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
location: "位置情報"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
location: "ここおるで:"
mobile/views/components/note-detail.vue:
reply: "返"
reply: "返"
reaction: "リアクション"
reposted-by: "{}がRenote"
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
location: "位置情報"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
location: "ここおるで:"
mobile/views/components/note-preview.vue:
admin: "admin"
bot: "bot"
@ -1065,55 +1072,55 @@ mobile/views/components/note-sub.vue:
bot: "bot"
cat: "cat"
mobile/views/components/notes.vue:
failed: "読み込みに失敗しました。"
retry: "リトライ"
failed: "あかん、読み込めへんわ"
retry: "もっぺん"
mobile/views/components/notifications.vue:
more: "もっと見る"
empty: "ありません!"
more: "もっとあるやろ!"
empty: "あらへん!"
mobile/views/components/post-form.vue:
add-visible-user: "ユーザーを追加"
add-visible-user: "ユーザー増やす"
submit: "投稿"
reply: "返"
reply: "返"
renote: "Renote"
quote-placeholder: "この投稿を引用... (オプション)"
quote-placeholder: "この投稿を持ってくる(オプション)"
reply-placeholder: "この投稿への返信..."
cw-placeholder: "内容への注釈 (オプション)"
location-alert: "あんさんのつことる端末は位置情報に対応しとらんみたいやわ、知らんけど。"
error: "エラー"
username-prompt: "ユーザー名を入力してや"
mobile/views/components/sub-note-content.vue:
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
media-count: "{}つのメディア"
poll: "アンケート"
mobile/views/components/timeline.vue:
empty: "投稿がありません"
empty: "投稿はあらへん"
load-more: "もっと"
mobile/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "ん"
welcome-back: "おかえり、"
adjective: "ん"
mobile/views/components/ui.nav.vue:
timeline: "タイムライン"
notifications: "通知"
messaging: "メッセージ"
follow-requests: "フォロー申請"
follow-requests: "フォロー許してくれや!言うてみる"
search: "検索"
drive: "ドライブ"
favorites: "お気に入り"
user-lists: "リスト"
widgets: "ウィジェット"
game: "ゲーム"
darkmode: "ダークモード"
darkmode: "ナイトゲームや"
settings: "設定"
admin: "管理"
about: "Misskeyについて"
about: "Misskeyってなんや?"
mobile/views/components/user-timeline.vue:
no-notes: "このユーザーは投稿していないようです。"
no-notes-with-media: "メディア付き投稿はありません。"
no-notes: "このユーザーは投稿しとらんようや。"
no-notes-with-media: "メディア付き投稿はあらへん。"
load-more: "もっと"
mobile/views/components/users-list.vue:
all: "すべて"
known: "知り合い"
known: "知っとる"
load-more: "もっと"
mobile/views/pages/favorites.vue:
title: "お気に入り"
@ -1122,9 +1129,9 @@ mobile/views/pages/user-lists.vue:
enter-list-name: "リスト名を入力してや"
mobile/views/pages/drive.vue:
drive: "ドライブ"
more: "もっと見る"
more: "もっとあるやろ!"
mobile/views/pages/signup.vue:
lets-start: "📦 始めましょう"
lets-start: "📦 始めようや"
mobile/views/pages/followers.vue:
followers-of: "{}のフォロワー"
mobile/views/pages/following.vue:
@ -1134,35 +1141,36 @@ mobile/views/pages/home.vue:
local: "ローカル"
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あた宛て"
mentions: "あた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿はあらへんで。"
mobile/views/pages/welcome.vue:
signup: "新規登録"
mobile/views/pages/widgets.vue:
dashboard: "ダッシュボード"
widgets-hints: "ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。"
add-widget: "追加"
widgets-hints: "ウィジェットを追加/削除したり並べ替えたりできんで。ウィジェットを移動するんやったら「三」をドラッグしてや。ウィジェットを削除するんやったら「x」をタップしてや。いくつかのウィジェットはタップしたったら表示を変更できるかも分からん、知らんけど。"
add-widget: "増やす"
customization-tips: "カスタマイズのヒント"
mobile/views/pages/widgets/activity.vue:
activity: "アクティビティ"
activity: "やっとること"
mobile/views/pages/share.vue:
share-with: "{}で共有"
share-with: "{}で「わけわけ」"
mobile/views/pages/messaging.vue:
messaging: "メッセージ"
mobile/views/pages/messaging-room.vue:
messaging: "メッセージ"
mobile/views/pages/received-follow-requests.vue:
title: "フォロー申請"
accept: "承認"
reject: "拒否"
title: "フォロー許してくれや!"
accept: "許す"
reject: "許さん"
mobile/views/pages/note.vue:
title: "投稿"
prev: "前の投稿"
next: "次の投稿"
prev: "前のやつ"
next: "次のやつ"
mobile/views/pages/notifications.vue:
notifications: "通知"
read-all: "すべての通知を既読にしますか?"
read-all: "通知全部読んだか?"
mobile/views/pages/games/reversi.vue:
reversi: "リバーシ"
mobile/views/pages/settings/settings.profile.vue:
@ -1170,12 +1178,12 @@ mobile/views/pages/settings/settings.profile.vue:
name: "名前"
account: "アカウント"
location: "場所"
description: "自己紹介"
description: "ワイのこと"
birthday: "誕生日"
avatar: "アイコン"
banner: "バナー"
is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする"
is-cat: "このアカウントはCat"
is-locked: "他人のフォローは許してからや!"
advanced: "その他"
privacy: "プライバシー"
save: "保存"

View File

@ -155,8 +155,10 @@ common:
home: "홈"
local: "로컬"
hybrid: "소셜"
hashtag: "ハッシュタグ"
global: "글로벌"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "통지"
list: "목록"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -155,8 +155,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "Algemeen"
mentions: "あなた宛て"
messages: "メッセージ"
list: "Lijsten"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -155,8 +155,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -155,8 +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"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
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: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
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:

View File

@ -155,8 +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"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -155,8 +155,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -155,8 +155,10 @@ common:
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
@ -809,7 +811,12 @@ desktop/views/components/timeline.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
list: "リスト"
hashtag: "ハッシュタグ"
add-tag-timeline: "ハッシュタグを追加"
add-list: "リストを追加"
list-name: "リスト名"
desktop/views/components/ui.header.vue:
welcome-back: "おかえりなさい、"
adjective: "さん"
@ -1135,6 +1142,7 @@ mobile/views/pages/home.vue:
hybrid: "ソーシャル"
global: "グローバル"
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue:

View File

@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "8.44.1",
"clientVersion": "1.0.9813",
"version": "8.61.0",
"clientVersion": "1.0.9958",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@ -27,7 +27,7 @@
"@koa/cors": "2.2.2",
"@prezzemolo/rap": "0.1.2",
"@prezzemolo/zip": "0.0.3",
"@types/bcryptjs": "2.4.1",
"@types/bcryptjs": "2.4.2",
"@types/dateformat": "1.0.1",
"@types/debug": "0.0.30",
"@types/deep-equal": "1.0.1",
@ -51,7 +51,7 @@
"@types/koa-logger": "3.1.0",
"@types/koa-mount": "3.0.1",
"@types/koa-multer": "1.0.0",
"@types/koa-router": "7.0.31",
"@types/koa-router": "7.0.32",
"@types/koa-send": "4.1.1",
"@types/koa-views": "2.0.3",
"@types/koa__cors": "2.2.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.3",
"@types/portscanner": "2.1.0",
"@types/pug": "2.0.4",
"@types/qrcode": "1.2.0",
@ -77,7 +77,7 @@
"@types/systeminformation": "3.23.0",
"@types/tmp": "0.0.33",
"@types/uuid": "3.4.4",
"@types/webpack": "4.4.11",
"@types/webpack": "4.4.12",
"@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40",
"@types/ws": "6.0.1",
@ -217,11 +217,11 @@
"vuewordcloud": "18.7.11",
"vuex": "3.0.1",
"vuex-persistedstate": "2.5.4",
"web-push": "3.3.2",
"web-push": "3.3.3",
"webfinger.js": "2.6.6",
"webpack": "4.19.0",
"webpack": "4.19.1",
"webpack-cli": "3.1.0",
"websocket": "1.0.26",
"websocket": "1.0.28",
"ws": "6.0.0",
"xev": "2.0.1"
},

View File

@ -1,3 +1,32 @@
<template>
<router-view id="app"></router-view>
<router-view id="app" v-hotkey.global="keymap"></router-view>
</template>
<script lang="ts">
import Vue from 'vue';
import { url, lang } from './config';
export default Vue.extend({
computed: {
keymap(): any {
return {
'h|slash': this.help,
'd': this.dark
};
}
},
methods: {
help() {
window.open(`${url}/docs/${lang}/keyboard-shortcut`, '_blank');
},
dark() {
this.$store.commit('device/set', {
key: 'darkmode',
value: !this.$store.state.device.darkmode
});
}
}
});
</script>

View 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);
}
}
});
}
};

View 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}`];
}

View File

@ -1,8 +1,8 @@
require('fuckadblock');
declare const fuckAdBlock: any;
export default (os) => {
require('fuckadblock');
function adBlockDetected() {
os.apis.dialog({
title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%',

View 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)
});
}
}

View File

@ -50,6 +50,30 @@ export class HomeStream extends Stream {
});
});
this.on('unreadMention', () => {
os.store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
this.on('readAllUnreadMentions', () => {
os.store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
this.on('unreadSpecifiedNote', () => {
os.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
this.on('readAllUnreadSpecifiedNotes', () => {
os.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
this.on('clientSettingUpdated', x => {
os.store.commit('settings/set', {
key: x.key,

View File

@ -1,5 +1,6 @@
import Vue from 'vue';
import instance from './instance.vue';
import cwButton from './cw-button.vue';
import tagCloud from './tag-cloud.vue';
import trends from './trends.vue';
@ -43,6 +44,7 @@ import uiSelect from './ui/select.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
Vue.component('mk-instance', instance);
Vue.component('mk-cw-button', cwButton);
Vue.component('mk-tag-cloud', tagCloud);
Vue.component('mk-trends', trends);

View File

@ -0,0 +1,57 @@
<template>
<div class="nhasjydimbopojusarffqjyktglcuxjy" v-if="meta">
<div class="banner" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"></div>
<h1>{{ meta.name }}</h1>
<p v-html="meta.description || '%i18n:common.about%'"></p>
<router-link to="/">%i18n:@start%</router-link>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
data() {
return {
meta: null
}
},
created() {
(this as any).os.getMeta().then(meta => {
this.meta = meta;
});
}
});
</script>
<style lang="stylus" scoped>
root(isDark)
color isDark ? #fff : #5b646f
background isDark ? #21242f : #fff
text-align center
> .banner
height 100px
background-position center
background-size cover
> h1
margin 16px
font-size 16px
> p
margin 16px
font-size 14px
> a
display block
padding-bottom 16px
.nhasjydimbopojusarffqjyktglcuxjy[data-darkmode]
root(true)
.nhasjydimbopojusarffqjyktglcuxjy:not([data-darkmode])
root(false)
</style>

View File

@ -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>

View File

@ -2,6 +2,8 @@
<span class="mk-nav">
<a :href="aboutUrl">%i18n:@about%</a>
<i></i>
<a href="/stats">%i18n:@stats%</a>
<i></i>
<a :href="repositoryUrl">%i18n:@repository%</a>
<i></i>
<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a>

View File

@ -28,17 +28,29 @@ export default Vue.extend({
}];
if (this.note.userId == this.$store.state.i.id) {
items.push({
icon: '%fa:thumbtack%',
text: '%i18n:@pin%',
action: this.pin
});
if (this.$store.state.i.pinnedNoteIds.includes(this.note.id)) {
items.push({
icon: '%fa:thumbtack%',
text: '%i18n:@unpin%',
action: this.unpin
});
} else {
items.push({
icon: '%fa:thumbtack%',
text: '%i18n:@pin%',
action: this.pin
});
}
}
if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
items.push({
icon: '%fa:trash-alt R%',
text: '%i18n:@delete%',
action: this.del
});
}
if (this.note.uri) {
items.push({
icon: '%fa:external-link-square-alt%',
@ -48,9 +60,11 @@ export default Vue.extend({
}
});
}
return items;
}
},
methods: {
detail() {
this.$router.push(`/notes/${ this.note.id }`);
@ -68,6 +82,14 @@ export default Vue.extend({
});
},
unpin() {
(this as any).api('i/unpin', {
noteId: this.note.id
}).then(() => {
this.destroyDom();
});
},
del() {
if (!window.confirm('%i18n:@delete-confirm%')) return;
(this as any).api('notes/delete', {

View File

@ -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.$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.destroyDom()
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

View File

@ -1,25 +1,30 @@
<template>
<div class="anltbovirfeutcigvwgmgxipejaeozxi"
:data-found="announcements && announcements.length != 0"
:data-melt="props.design == 1"
:data-mobile="platform == 'mobile'"
>
<div class="icon">
<svg height="32" version="1.1" viewBox="0 0 32 32" width="32">
<path class="tower" d="M16.04,11.24c1.79,0,3.239-1.45,3.239-3.24S17.83,4.76,16.04,4.76c-1.79,0-3.24,1.45-3.24,3.24 C12.78,9.78,14.24,11.24,16.04,11.24z M16.04,13.84c-0.82,0-1.66-0.2-2.4-0.6L7.34,29.98h2.98l1.72-2h8l1.681,2H24.7L18.42,13.24 C17.66,13.64,16.859,13.84,16.04,13.84z M16.02,14.8l2.02,7.2h-4L16.02,14.8z M12.04,25.98l2-2h4l2,2H12.04z"></path>
<path class="wave a" d="M4.66,1.04c-0.508-0.508-1.332-0.508-1.84,0c-1.86,1.92-2.8,4.44-2.8,6.94c0,2.52,0.94,5.04,2.8,6.96 c0.5,0.52,1.32,0.52,1.82,0s0.5-1.36,0-1.88C3.28,11.66,2.6,9.82,2.6,7.98S3.28,4.3,4.64,2.9C5.157,2.391,5.166,1.56,4.66,1.04z"></path>
<path class="wave b" d="M9.58,12.22c0.5-0.5,0.5-1.34,0-1.84C8.94,9.72,8.62,8.86,8.62,8s0.32-1.72,0.96-2.38c0.5-0.52,0.5-1.34,0-1.84 C9.346,3.534,9.02,3.396,8.68,3.4c-0.32,0-0.66,0.12-0.9,0.38C6.64,4.94,6.08,6.48,6.08,8s0.58,3.06,1.7,4.22 C8.28,12.72,9.1,12.72,9.58,12.22z"></path>
<path class="wave c" d="M22.42,3.78c-0.5,0.5-0.5,1.34,0,1.84c0.641,0.66,0.96,1.52,0.96,2.38s-0.319,1.72-0.96,2.38c-0.5,0.52-0.5,1.34,0,1.84 c0.487,0.497,1.285,0.505,1.781,0.018c0.007-0.006,0.013-0.012,0.02-0.018c1.139-1.16,1.699-2.7,1.699-4.22s-0.561-3.06-1.699-4.22 c-0.494-0.497-1.297-0.5-1.794-0.007C22.424,3.775,22.422,3.778,22.42,3.78z"></path>
<path class="wave d" d="M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z"></path>
</svg>
</div>
<p class="fetching" v-if="fetching">%i18n:@fetching%<mk-ellipsis/></p>
<h1 v-if="!fetching">{{ announcements.length == 0 ? '%i18n:@no-broadcasts%' : announcements[i].title }}</h1>
<p v-if="!fetching">
<span v-if="announcements.length != 0" v-html="announcements[i].text"></span>
<template v-if="announcements.length == 0">%i18n:@have-a-nice-day%</template>
</p>
<a v-if="announcements.length > 1" @click="next">%i18n:@next% &gt;&gt;</a>
<div class="anltbovirfeutcigvwgmgxipejaeozxi">
<mk-widget-container :show-header="false" :naked="props.design == 1">
<div class="anltbovirfeutcigvwgmgxipejaeozxi-body"
:data-found="announcements && announcements.length != 0"
:data-melt="props.design == 1"
:data-mobile="platform == 'mobile'"
:data-darkmode="$store.state.device.darkmode"
>
<div class="icon">
<svg height="32" version="1.1" viewBox="0 0 32 32" width="32">
<path class="tower" d="M16.04,11.24c1.79,0,3.239-1.45,3.239-3.24S17.83,4.76,16.04,4.76c-1.79,0-3.24,1.45-3.24,3.24 C12.78,9.78,14.24,11.24,16.04,11.24z M16.04,13.84c-0.82,0-1.66-0.2-2.4-0.6L7.34,29.98h2.98l1.72-2h8l1.681,2H24.7L18.42,13.24 C17.66,13.64,16.859,13.84,16.04,13.84z M16.02,14.8l2.02,7.2h-4L16.02,14.8z M12.04,25.98l2-2h4l2,2H12.04z"></path>
<path class="wave a" d="M4.66,1.04c-0.508-0.508-1.332-0.508-1.84,0c-1.86,1.92-2.8,4.44-2.8,6.94c0,2.52,0.94,5.04,2.8,6.96 c0.5,0.52,1.32,0.52,1.82,0s0.5-1.36,0-1.88C3.28,11.66,2.6,9.82,2.6,7.98S3.28,4.3,4.64,2.9C5.157,2.391,5.166,1.56,4.66,1.04z"></path>
<path class="wave b" d="M9.58,12.22c0.5-0.5,0.5-1.34,0-1.84C8.94,9.72,8.62,8.86,8.62,8s0.32-1.72,0.96-2.38c0.5-0.52,0.5-1.34,0-1.84 C9.346,3.534,9.02,3.396,8.68,3.4c-0.32,0-0.66,0.12-0.9,0.38C6.64,4.94,6.08,6.48,6.08,8s0.58,3.06,1.7,4.22 C8.28,12.72,9.1,12.72,9.58,12.22z"></path>
<path class="wave c" d="M22.42,3.78c-0.5,0.5-0.5,1.34,0,1.84c0.641,0.66,0.96,1.52,0.96,2.38s-0.319,1.72-0.96,2.38c-0.5,0.52-0.5,1.34,0,1.84 c0.487,0.497,1.285,0.505,1.781,0.018c0.007-0.006,0.013-0.012,0.02-0.018c1.139-1.16,1.699-2.7,1.699-4.22s-0.561-3.06-1.699-4.22 c-0.494-0.497-1.297-0.5-1.794-0.007C22.424,3.775,22.422,3.778,22.42,3.78z"></path>
<path class="wave d" d="M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z"></path>
</svg>
</div>
<p class="fetching" v-if="fetching">%i18n:@fetching%<mk-ellipsis/></p>
<h1 v-if="!fetching">{{ announcements.length == 0 ? '%i18n:@no-broadcasts%' : announcements[i].title }}</h1>
<p v-if="!fetching">
<span v-if="announcements.length != 0" v-html="announcements[i].text"></span>
<template v-if="announcements.length == 0">%i18n:@have-a-nice-day%</template>
</p>
<a v-if="announcements.length > 1" @click="next">%i18n:@next% &gt;&gt;</a>
</div>
</mk-widget-container>
</div>
</template>
@ -68,11 +73,10 @@ export default define({
<style lang="stylus" scoped>
root(isDark)
padding 10px
border solid 1px #4078c0
border-radius 6px
background isDark ? #253a50 : #f3f9ff
&[data-melt]
border none
background transparent
&[data-found]
padding-left 50px
@ -133,7 +137,7 @@ root(isDark)
z-index 1
margin 0
font-size 0.7em
color isDark ? #fff : #555
color isDark ? #fff : #57616f
&.fetching
text-align center
@ -146,10 +150,10 @@ root(isDark)
> p
color #fff
.anltbovirfeutcigvwgmgxipejaeozxi[data-darkmode]
.anltbovirfeutcigvwgmgxipejaeozxi-body[data-darkmode]
root(true)
.anltbovirfeutcigvwgmgxipejaeozxi:not([data-darkmode])
.anltbovirfeutcigvwgmgxipejaeozxi-body:not([data-darkmode])
root(false)
</style>

View File

@ -87,10 +87,12 @@ init(async (launch) => {
updateBanner: updateBanner(os)
}));
/**
* Fuck AD Block
*/
fuckAdBlock(os);
if (os.store.getters.isSignedIn) {
/**
* Fuck AD Block
*/
fuckAdBlock(os);
}
/**
* Init Notification

View File

@ -133,8 +133,8 @@ export default Vue.extend({
root(isDark)
color isDark ? #c5ced6 : #777
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
&[data-melt]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -237,6 +237,10 @@ export default Vue.extend({
warp(date) {
(this.$refs.tl as any).warp(date);
},
focus() {
(this.$refs.tl as any).focus();
}
}
});
@ -336,7 +340,7 @@ root(isDark)
display flex
justify-content center
margin 0 auto
max-width 1220px
max-width 1240px
> *
.customize-container
@ -351,13 +355,13 @@ root(isDark)
> .main
padding 16px
width calc(100% - 275px * 2)
width calc(100% - 280px * 2)
order 2
> .form
margin-bottom 16px
border solid 1px rgba(#000, 0.075)
border-radius 4px
box-shadow var(--shadow)
border-radius var(--round)
@media (max-width 700px)
padding 0
@ -367,7 +371,7 @@ root(isDark)
border-radius 0
> *:not(.main)
width 275px
width 280px
padding 16px 0 16px 0
> *:not(:last-child)

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -231,8 +231,8 @@ root(isDark)
overflow hidden
text-align left
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.1)
border-radius 8px
box-shadow var(--shadow)
border-radius var(--round)
> .read-more
display block

View File

@ -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,30 @@ 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,
'esc': this.blur,
'm|o': () => this.menu(true),
's': this.toggleShowContent,
'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 &&
@ -189,10 +213,14 @@ export default Vue.extend({
methods: {
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send({
const data = {
type: 'capture',
id: this.p.id
});
} as any;
if ((this.p.visibleUserIds || []).includes(this.$store.state.i.id) || (this.p.mentions || []).includes(this.$store.state.i.id)) {
data.read = true;
}
this.connection.send(data);
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},
@ -220,67 +248,69 @@ 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;
toggleShowContent() {
this.showContent = !this.showContent;
},
switch (true) {
case e.which == 38: // [↑]
case e.which == 74: // [j]
case e.which == 9 && e.shiftKey: // [Shift] + [Tab]
focus(this.$el, e => e.previousElementSibling);
break;
focus() {
this.$el.focus();
},
case e.which == 40: // [↓]
case e.which == 75: // [k]
case e.which == 9: // [Tab]
focus(this.$el, e => e.nextElementSibling);
break;
blur() {
this.$el.blur();
},
case e.which == 81: // [q]
case e.which == 69: // [e]
this.renote();
break;
focusBefore() {
focus(this.$el, e => e.previousElementSibling);
},
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();
focusAfter() {
focus(this.$el, e => e.nextElementSibling);
}
}
});

View File

@ -10,9 +10,9 @@
</div>
<!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div">
<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>
@ -89,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) {

View File

@ -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: false
},
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();
}
}
});

View File

@ -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>

View File

@ -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">

View File

@ -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
},
animation: {
type: Boolean,
required: false,
default: true
}
},
beforeDestroy() {
document.removeEventListener('keydown', this.onDocumentKeydown);
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
(this.$refs.window as any).close();
}
}
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();
}
}
});

View File

@ -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();

View 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>

View File

@ -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>
@ -59,6 +60,8 @@
<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="useShadow" text="%i18n:@use-shadow%"/>
<mk-switch v-model="roundedCorners" text="%i18n:@rounded-corners%"/>
<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%"/>
@ -138,6 +141,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/>
@ -222,6 +230,7 @@ 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';
@ -234,11 +243,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,
@ -302,6 +318,16 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'alwaysShowNsfw', value }); }
},
useShadow: {
get() { return this.$store.state.settings.useShadow; },
set(value) { this.$store.dispatch('settings/set', { key: 'useShadow', value }); }
},
roundedCorners: {
get() { return this.$store.state.settings.roundedCorners; },
set(value) { this.$store.dispatch('settings/set', { key: 'roundedCorners', value }); }
},
fetchOnScroll: {
get() { return this.$store.state.settings.fetchOnScroll; },
set(value) { this.$store.dispatch('settings/set', { key: 'fetchOnScroll', value }); }

View File

@ -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,55 +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;
case 'mentions': return (this as any).os.stream;
}
},
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';
case 'mentions': return 'notes/mentions';
}
},
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(this.src == 'mentions' ? 'mention' : '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(this.src == 'mentions' ? 'mention' : '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: {
@ -98,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;
@ -121,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) {
@ -142,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();
},
@ -158,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();
}
}
}
}
});

View File

@ -5,15 +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 == 'mentions'" @click="src = 'mentions'">%fa:at% %i18n:@mentions%</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%<i class="badge" v-if="$store.state.i.hasUnreadMentions">%fa:circle%</i></button>
<button :data-active="src == 'messages'" @click="src = 'messages'" title="%i18n:@messages%">%fa:envelope R%<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes">%fa:circle%</i></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>
@ -21,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: {
@ -32,6 +40,7 @@ export default Vue.extend({
return {
src: 'home',
list: null,
tagTl: null,
enableLocalTimeline: false
};
},
@ -41,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;
}
},
@ -55,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';
@ -71,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 => {
this.list = list;
this.src = 'list';
w.close();
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';
});
}
}];
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
});
}
}
@ -96,32 +179,55 @@ export default Vue.extend({
root(isDark)
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
> header
padding 0 8px
z-index 10
background isDark ? #313543 : #fff
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
font-size 0.9em
line-height 42px
color isDark ? #9baec8 : #ccc
padding-right 8px
&:hover
color isDark ? #b2c1d5 : #aaa
> button
padding 0 8px
font-size 0.9em
line-height 42px
color isDark ? #9baec8 : #ccc
&:active
color isDark ? #b2c1d5 : #999
> .badge
position absolute
top -4px
right 4px
font-size 10px
color $theme-color
&:hover
color isDark ? #b2c1d5 : #aaa
&: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

View File

@ -1,5 +1,5 @@
<template>
<div class="account">
<div class="account" v-hotkey.global="keymap">
<button class="header" :data-active="isOpen" @click="toggle">
<span class="username">{{ $store.state.i.username }}<template v-if="!isOpen">%fa:angle-down%</template><template v-if="isOpen">%fa:angle-up%</template></span>
<mk-avatar class="avatar" :user="$store.state.i"/>
@ -63,6 +63,13 @@ export default Vue.extend({
isOpen: false
};
},
computed: {
keymap(): any {
return {
'a|m': this.toggle
};
}
},
beforeDestroy() {
this.close();
},

View File

@ -1,5 +1,5 @@
<template>
<div class="notifications">
<div class="notifications" v-hotkey.global="keymap">
<button :data-active="isOpen" @click="toggle" title="%i18n:@title%">
%fa:R bell%<template v-if="hasUnreadNotification">%fa:circle%</template>
</button>
@ -19,11 +19,19 @@ export default Vue.extend({
isOpen: false
};
},
computed: {
hasUnreadNotification(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadNotification;
},
keymap(): any {
return {
'shift+n': this.toggle
};
}
},
methods: {
toggle() {
this.isOpen ? this.close() : this.open();

View File

@ -1,5 +1,5 @@
<template>
<div class="header">
<div class="header" :style="style">
<p class="warn" v-if="env != 'production'">%i18n:common.do-not-use-in-production%</p>
<mk-special-message/>
<div class="main" ref="main">
@ -54,8 +54,16 @@ export default Vue.extend({
};
},
computed: {
style(): any {
return {
'box-shadow': this.$store.state.settings.useShadow ? '0 0px 8px rgba(0, 0, 0, 0.2)' : 'none'
};
}
},
mounted() {
this.$store.commit('setUiHeaderHeight', 48);
this.$store.commit('setUiHeaderHeight', this.$el.offsetHeight);
if (this.$store.getters.isSignedIn) {
const ago = (new Date().getTime() - new Date(this.$store.state.i.lastUsedAt).getTime()) / 1000;
@ -120,12 +128,10 @@ export default Vue.extend({
<style lang="stylus" scoped>
root(isDark)
position -webkit-sticky
position sticky
position fixed
top 0
z-index 1000
width 100%
box-shadow 0 1px 1px rgba(#000, 0.075)
> .warn
display block

View File

@ -1,6 +1,7 @@
<template>
<div class="mk-ui" :style="style">
<x-header class="header" v-show="!zenMode"/>
<div class="mk-ui" v-hotkey.global="keymap">
<div class="bg" v-if="$store.getters.isSignedIn && $store.state.i.wallpaperUrl" :style="style"></div>
<x-header class="header" v-show="!zenMode" ref="header"/>
<div class="content">
<slot></slot>
</div>
@ -16,11 +17,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,27 +31,37 @@ 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
};
}
},
watch: {
'$store.state.uiHeaderHeight'() {
this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px';
}
},
mounted() {
document.addEventListener('keydown', this.onKeydown);
},
beforeDestroy() {
document.removeEventListener('keydown', this.onKeydown);
this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px';
},
methods: {
onKeydown(e) {
if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA') return;
post() {
(this as any).apis.post();
},
if (e.which == 80 || e.which == 78) { // p or n
e.preventDefault();
(this as any).apis.post();
}
if (e.which == 90) { // z
e.preventDefault();
this.zenMode = !this.zenMode;
}
toggleZenMode() {
this.zenMode = !this.zenMode;
this.$nextTick(() => {
this.$store.commit('setUiHeaderHeight', this.$refs.header.$el.offsetHeight);
});
}
}
});
@ -56,20 +69,22 @@ export default Vue.extend({
<style lang="stylus" scoped>
.mk-ui
display flex
flex-direction column
flex 1
background-size cover
background-position center
background-attachment fixed
min-height 100vh
padding-top 48px
> .bg
position fixed
top 0
left 0
width 100%
height 100vh
background-size cover
background-position center
background-attachment fixed
opacity 0.3
> .header
@media (max-width 1000px)
display none
> .content
display flex
flex-direction column
flex 1
overflow hidden
</style>

View File

@ -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">

View File

@ -36,13 +36,13 @@ export default Vue.extend({
<style lang="stylus" scoped>
root(isDark)
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, isDark ? 0.2 : 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
&.naked
background transparent !important
border none !important
box-shadow none !important
> header
background isDark ? #313543 : #fff

View File

@ -76,6 +76,11 @@ export default Vue.extend({
name: {
type: String,
default: null
},
animation: {
type: Boolean,
required: false,
default: true
}
},
@ -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.destroyDom();
this.$emit('closed');
}, 300);
this.destroyDom();
}, this.animation ? 300 : 0);
},
popout() {

View File

@ -14,6 +14,14 @@
</div>
<div class="form">
<div>
<label>
<p>%i18n:@banner-url%</p>
<input v-model="bannerUrl">
</label>
<button class="ui" @click="updateMeta">%i18n:@save%</button>
</div>
<div>
<label>
<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
@ -46,6 +54,7 @@ export default Vue.extend({
stats: null,
disableRegistration: false,
disableLocalTimeline: false,
bannerUrl: null,
inviteCode: null,
connection: null,
connectionId: null
@ -58,6 +67,7 @@ export default Vue.extend({
(this as any).os.getMeta().then(meta => {
this.disableRegistration = meta.disableRegistration;
this.disableLocalTimeline = meta.disableLocalTimeline;
this.bannerUrl = meta.bannerUrl;
});
(this as any).api('stats').then(stats => {
@ -76,7 +86,8 @@ export default Vue.extend({
updateMeta() {
(this as any).api('admin/update-meta', {
disableRegistration: this.disableRegistration,
disableLocalTimeline: this.disableLocalTimeline
disableLocalTimeline: this.disableLocalTimeline,
bannerUrl: this.bannerUrl
});
}
}
@ -114,6 +125,7 @@ export default Vue.extend({
> .form
> div
padding 16px
border-bottom solid 1px #eee
</style>

View File

@ -6,7 +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">
@ -15,13 +17,15 @@ 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,
XMentionsColumn
XMentionsColumn,
XDirectColumn
},
props: {

View File

@ -279,7 +279,7 @@ root(isDark)
height 100%
background isDark ? #282C37 : #fff
border-radius 6px
box-shadow 0 2px 16px rgba(#000, 0.1)
//box-shadow 0 2px 16px rgba(#000, 0.1)
overflow hidden
&.draghover

View File

@ -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>

View 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>

View 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>

View File

@ -147,10 +147,14 @@ export default Vue.extend({
methods: {
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send({
const data = {
type: 'capture',
id: this.p.id
});
} as any;
if ((this.p.visibleUserIds || []).includes(this.$store.state.i.id) || (this.p.mentions || []).includes(this.$store.state.i.id)) {
data.read = true;
}
this.connection.send(data);
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},

View File

@ -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;
}
}
},

View File

@ -1,6 +1,6 @@
<template>
<mk-ui :class="$style.root">
<div class="qlvquzbjribqcaozciifydkngcwtyzje" :data-darkmode="$store.state.device.darkmode">
<div class="qlvquzbjribqcaozciifydkngcwtyzje" :data-darkmode="$store.state.device.darkmode" :style="style">
<template v-for="ids in layout">
<div v-if="ids.length > 1" class="folder">
<template v-for="id, i in ids">
@ -35,6 +35,11 @@ export default Vue.extend({
if (this.$store.state.settings.deck == null) return [];
if (this.$store.state.settings.deck.layout == null) return this.$store.state.settings.deck.columns.map(c => [c.id]);
return this.$store.state.settings.deck.layout;
},
style(): any {
return {
height: `calc(100vh - ${this.$store.state.uiHeaderHeight}px)`
};
}
},
@ -147,6 +152,15 @@ export default Vue.extend({
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%',
@ -161,6 +175,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%',

View File

@ -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();
}
}
});

View File

@ -38,8 +38,8 @@ export default Vue.extend({
<style lang="stylus" scoped>
root(isDark)
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
> .title
z-index 1

View File

@ -42,8 +42,8 @@ export default Vue.extend({
<style lang="stylus" scoped>
root(isDark)
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
> .title

View File

@ -104,8 +104,8 @@ export default Vue.extend({
root(isDark)
background isDark ? #282C37 : #fff
border 1px solid rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
&[data-is-dark-background]

View File

@ -4,7 +4,7 @@
<p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@loading%<mk-ellipsis/></p>
<div class="stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" class="img"
:style="`background-image: url(${image.url})`"
:style="`background-image: url(${image.thumbnailUrl})`"
></div>
</div>
<p class="empty" v-if="!fetching && images.length == 0">%i18n:@no-photos%</p>
@ -41,8 +41,8 @@ export default Vue.extend({
<style lang="stylus" scoped>
root(isDark)
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
> .title

View File

@ -87,8 +87,8 @@ export default Vue.extend({
<style lang="stylus" scoped>
root(isDark)
background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
> *:first-child
border-top none !important

View File

@ -116,12 +116,13 @@ export default Vue.extend({
root(isDark)
background isDark ? #282C37 : #fff
border-radius var(--round)
overflow hidden
> header
padding 0 8px
z-index 10
background isDark ? #313543 : #fff
border-radius 6px 6px 0 0
box-shadow 0 1px isDark ? rgba(#000, 0.15) : rgba(#000, 0.08)
> span

View File

@ -6,10 +6,11 @@
<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">
<div class="instance" v-if="!$store.getters.isSignedIn"><mk-instance/></div>
<x-profile :user="user"/>
<x-twitter :user="user" v-if="user.host === null && user.twitter"/>
<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>
@ -28,7 +29,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';
@ -89,17 +89,16 @@ root(isDark)
margin-bottom 16px
padding 14px 16px
font-size 14px
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
&.is-suspended
color isDark ? #ffb4b4 : #570808
background isDark ? #611d1d : #ffdbdb
border solid 1px isDark ? #d64a4a : #e09696
&.is-remote
color isDark ? #ffbd3e : #573c08
background isDark ? #42321c : #fff0db
border solid 1px isDark ? #90733c : #dcbb7b
> a
font-weight bold
@ -119,8 +118,7 @@ root(isDark)
margin-right 16px
> .timeline
border 1px solid rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
> .side
width 275px
@ -134,13 +132,17 @@ root(isDark)
font-size 0.8em
color #aaa
> .instance
box-shadow var(--shadow)
border-radius var(--round)
> .nav
padding 16px
font-size 12px
color #aaa
background isDark ? #21242f : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
box-shadow var(--shadow)
border-radius var(--round)
a
color #999

View File

@ -1,5 +1,7 @@
<template>
<div class="mk-welcome">
<div class="banner" :style="{ backgroundImage: banner ? `url(${banner})` : null }"></div>
<button @click="dark">
<template v-if="$store.state.device.darkmode">%fa:moon%</template>
<template v-else>%fa:R moon%</template>
@ -154,6 +156,7 @@ export default Vue.extend({
return {
meta: null,
stats: null,
banner: null,
copyright,
host,
name: 'Misskey',
@ -169,6 +172,7 @@ export default Vue.extend({
this.name = meta.name;
this.description = meta.description;
this.announcements = meta.broadcasts;
this.banner = meta.bannerUrl;
});
(this as any).api('stats').then(stats => {
@ -308,6 +312,26 @@ root(isDark)
//background-position center
//background-size cover
> .banner
position absolute
top 0
left 0
width 100%
height 400px
background-position center
background-size cover
opacity 0.7
&:after
content ""
display block
position absolute
bottom 0
left 0
width 100%
height 100px
background linear-gradient(transparent, isDark ? #191b22 : #f7f7f7)
> .forkit
position absolute
top 0
@ -331,7 +355,7 @@ root(isDark)
.block
color isDark ? #fff : #444
background isDark ? #282C37 : #fff
box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
box-shadow var(--shadow)
//border-radius 8px
overflow auto

View File

@ -1,20 +1,25 @@
<template>
<div class="mkw-profile"
:data-compact="props.design == 1 || props.design == 2"
:data-melt="props.design == 2"
>
<div class="banner"
:style="$store.state.i.bannerUrl ? `background-image: url(${$store.state.i.bannerUrl})` : ''"
title="%i18n:@update-banner%"
@click="() => os.apis.updateBanner()"
></div>
<mk-avatar class="avatar" :user="$store.state.i"
:disable-link="true"
@click="() => os.apis.updateAvatar()"
title="%i18n:@update-avatar%"
/>
<router-link class="name" :to="$store.state.i | userPage">{{ $store.state.i | userName }}</router-link>
<p class="username">@{{ $store.state.i | acct }}</p>
<div class="egwyvoaaryotefqhqtmiyawwefemjfsd">
<mk-widget-container :show-header="false" :naked="props.design == 2">
<div class="egwyvoaaryotefqhqtmiyawwefemjfsd-body"
:data-compact="props.design == 1 || props.design == 2"
:data-melt="props.design == 2"
:data-darkmode="$store.state.device.darkmode"
>
<div class="banner"
:style="$store.state.i.bannerUrl ? `background-image: url(${$store.state.i.bannerUrl})` : ''"
title="%i18n:@update-banner%"
@click="() => os.apis.updateBanner()"
></div>
<mk-avatar class="avatar" :user="$store.state.i"
:disable-link="true"
@click="() => os.apis.updateAvatar()"
title="%i18n:@update-avatar%"
/>
<router-link class="name" :to="$store.state.i | userPage">{{ $store.state.i | userName }}</router-link>
<p class="username">@{{ $store.state.i | acct }}</p>
</div>
</mk-widget-container>
</div>
</template>
@ -42,10 +47,6 @@ export default define({
<style lang="stylus" scoped>
root(isDark)
overflow hidden
background isDark ? #282c37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
&[data-compact]
> .banner:before
@ -75,9 +76,6 @@ root(isDark)
display none
&[data-melt]
background transparent !important
border none !important
> .banner
visibility hidden
@ -120,10 +118,10 @@ root(isDark)
font-size 0.9em
color isDark ? #606984 : #999
.mkw-profile[data-darkmode]
.egwyvoaaryotefqhqtmiyawwefemjfsd-body[data-darkmode]
root(true)
.mkw-profile:not([data-darkmode])
.egwyvoaaryotefqhqtmiyawwefemjfsd-body:not([data-darkmode])
root(false)
</style>

View File

@ -8,6 +8,7 @@ import VueRouter from 'vue-router';
import * as TreeView from 'vue-json-tree-view';
import VAnimateCss from 'v-animate-css';
import VModal from 'vue-js-modal';
import VueHotkey from './common/hotkey';
import App from './app.vue';
import checkForUpdate from './common/scripts/check-for-update';
@ -19,6 +20,7 @@ Vue.use(VueRouter);
Vue.use(TreeView);
Vue.use(VAnimateCss);
Vue.use(VModal);
Vue.use(VueHotkey);
// Register global directives
require('./common/views/directives');
@ -123,6 +125,26 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
});
//#endregion
//#region shadow
const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadow', shadow);
os.store.watch(s => {
return s.settings.useShadow;
}, v => {
document.documentElement.style.setProperty('--shadow', v ? shadow : 'none');
});
//#endregion
//#region rounded corners
const round = '6px';
if (os.store.state.settings.roundedCorners) document.documentElement.style.setProperty('--round', round);
os.store.watch(s => {
return s.settings.roundedCorners;
}, v => {
document.documentElement.style.setProperty('--round', v ? round : '0');
});
//#endregion
Vue.mixin({
data() {
return {

View File

@ -160,10 +160,14 @@ export default Vue.extend({
methods: {
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send({
const data = {
type: 'capture',
id: this.p.id
});
} as any;
if ((this.p.visibleUserIds || []).includes(this.$store.state.i.id) || (this.p.mentions || []).includes(this.$store.state.i.id)) {
data.read = true;
}
this.connection.send(data);
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},

View File

@ -188,9 +188,6 @@ root(isDark)
overflow hidden
text-overflow ellipsis
[data-fa], [data-icon]
margin-right 4px
> img
display inline-block
vertical-align bottom

View File

@ -1,9 +1,9 @@
<template>
<mk-ui>
<span slot="header">
<template v-if="folder">%fa:R folder-open%{{ folder.name }}</template>
<template v-if="file"><mk-file-type-icon data-icon :type="file.type"/>{{ file.name }}</template>
<template v-if="!folder && !file">%fa:cloud%%i18n:@drive%</template>
<template v-if="folder"><span style="margin-right:4px;">%fa:R folder-open%</span>{{ folder.name }}</template>
<template v-if="file"><mk-file-type-icon data-icon :type="file.type" style="margin-right:4px;"/>{{ file.name }}</template>
<template v-if="!folder && !file"><span style="margin-right:4px;">%fa:cloud%</span>%i18n:@drive%</template>
</span>
<template slot="func"><button @click="fn">%fa:ellipsis-h%</button></template>
<mk-drive

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:star%%i18n:@title%</span>
<span slot="header"><span style="margin-right:4px;">%fa:star%</span>%i18n:@title%</span>
<main>
<template v-for="favorite in favorites">

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:gamepad%%i18n:@reversi%</span>
<span slot="header"><span style="margin-right:4px;">%fa:gamepad%</span>%i18n:@reversi%</span>
<mk-reversi :game-id="$route.params.game" @nav="nav" :self-nav="false"/>
</mk-ui>
</template>

View File

@ -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,51 +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;
case 'mentions': return (this as any).os.stream;
}
},
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';
case 'mentions': return 'notes/mentions';
}
},
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(this.src == 'mentions' ? 'mention' : '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(this.src == 'mentions' ? 'mention' : '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: {
@ -93,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;
@ -116,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) {
@ -137,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();
},

View File

@ -1,18 +1,21 @@
<template>
<mk-ui>
<span slot="header" @click="showNav = true">
<span>
<span :class="$style.title">
<span v-if="src == 'home'">%fa:home%%i18n:@home%</span>
<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>
<template v-else>%fa:angle-up%</template>
</span>
<i :class="$style.badge" v-if="$store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes">%fa:circle%</i>
</span>
<template slot="func">
@ -22,16 +25,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>
<span :data-active="src == 'mentions'" @click="src = 'mentions'">%fa:at% %i18n:@mentions%</span>
<div class="hr"></div>
<span :data-active="src == 'mentions'" @click="src = 'mentions'">%fa:at% %i18n:@mentions%<i class="badge" v-if="$store.state.i.hasUnreadMentions">%fa:circle%</i></span>
<span :data-active="src == 'messages'" @click="src = 'messages'">%fa:envelope R% %i18n:@messages%<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes">%fa:circle%</i></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>
@ -42,6 +51,8 @@
<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>
@ -63,6 +74,7 @@ export default Vue.extend({
src: 'home',
list: null,
lists: null,
tagTl: null,
showNav: false,
enableLocalTimeline: false
};
@ -74,9 +86,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) {
@ -97,6 +116,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';
@ -121,7 +142,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
});
},
@ -137,6 +158,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
@ -153,28 +194,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
@ -186,6 +221,11 @@ root(isDark)
&:not([data-active]):hover
background isDark ? #353e4a : #eee
> .badge
margin-left 6px
font-size 10px
color $theme-color
> .tl
max-width 680px
margin 0 auto
@ -204,3 +244,18 @@ main:not([data-darkmode])
root(false)
</style>
<style lang="stylus" module>
@import '~const.styl'
.title
i
margin-right 4px
.badge
margin-left 6px
font-size 10px
color $theme-color
vertical-align middle
</style>

View File

@ -1,7 +1,7 @@
<template>
<mk-ui>
<span slot="header">
<template v-if="user">%fa:R comments%{{ user | userName }}</template>
<template v-if="user"><span style="margin-right:4px;">%fa:R comments%</span>{{ user | userName }}</template>
<template v-else><mk-ellipsis/></template>
</span>
<mk-messaging-room v-if="!fetching" :user="user" :is-naked="true"/>

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:R comments%%i18n:@messaging%</span>
<span slot="header"><span style="margin-right:4px;">%fa:R comments%</span>%i18n:@messaging%</span>
<mk-messaging @navigate="navigate" :header-top="48"/>
</mk-ui>
</template>

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:R sticky-note%%i18n:@title%</span>
<span slot="header"><span style="margin-right:4px;">%fa:R sticky-note%</span>%i18n:@title%</span>
<main v-if="!fetching">
<div>
<mk-note-detail :note="note"/>

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:R bell%%i18n:@notifications%</span>
<span slot="header"><span style="margin-right:4px;">%fa:R bell%</span>%i18n:@notifications%</span>
<template slot="func"><button @click="fn">%fa:check%</button></template>
<main>

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:cog%%i18n:@settings%</span>
<span slot="header"><span style="margin-right:4px;">%fa:cog%</span>%i18n:@settings%</span>
<main :data-darkmode="$store.state.device.darkmode">
<div class="signin-as" v-html="'%i18n:@signed-in-as%'.replace('{}', `<b>${name}</b>`)"></div>

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:hashtag%{{ $route.params.tag }}</span>
<span slot="header"><span style="margin-right:4px;">%fa:hashtag%</span>{{ $route.params.tag }}</span>
<main>
<p v-if="!fetching && empty">%fa:search% {{ '%i18n:no-posts-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:no-posts-found%'.split('{}')[1] }}</p>

View File

@ -4,7 +4,7 @@
<div class="stream" v-if="!fetching && images.length > 0">
<a v-for="image in images"
class="img"
:style="`background-image: url(${image.media.url})`"
:style="`background-image: url(${image.media.thumbnailUrl})`"
:href="image.note | notePage"
></a>
</div>

View File

@ -1,6 +1,6 @@
<template>
<div class="root home">
<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"/>
<section class="recent-notes">
<h2>%fa:R comments%%i18n:@recent-notes%</h2>
<div>

View File

@ -1,5 +1,7 @@
<template>
<div class="wgwfgvvimdjvhjfwxropcwksnzftjqes">
<div class="banner" :style="{ backgroundImage: banner ? `url(${banner})` : null }"></div>
<div>
<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name">
<p class="host">{{ host }}</p>
@ -80,6 +82,7 @@ export default Vue.extend({
meta: null,
copyright,
stats: null,
banner: null,
host,
name: 'Misskey',
description: '',
@ -93,6 +96,7 @@ export default Vue.extend({
this.name = meta.name;
this.description = meta.description;
this.announcements = meta.broadcasts;
this.banner = meta.bannerUrl;
});
(this as any).api('stats').then(stats => {
@ -121,7 +125,27 @@ root(isDark)
text-align center
//background #fff
> div
> .banner
position absolute
top 0
left 0
width 100%
height 300px
background-position center
background-size cover
opacity 0.7
&:after
content ""
display block
position absolute
bottom 0
left 0
width 100%
height 100px
background linear-gradient(transparent, isDark ? #191b22 : #f7f7f7)
> div:not(.banner)
padding 32px
margin 0 auto
max-width 500px

View File

@ -1,6 +1,6 @@
<template>
<mk-ui>
<span slot="header">%fa:home%%i18n:@dashboard%</span>
<span slot="header"><span style="margin-right:4px;">%fa:home%</span>%i18n:@dashboard%</span>
<template slot="func">
<button @click="customizing = !customizing">%fa:cog%</button>
</template>

View File

@ -10,11 +10,14 @@ const defaultSettings = {
home: null,
mobileHome: [],
deck: null,
tagTimelines: [],
fetchOnScroll: true,
showMaps: true,
showPostFormOnTopOfTl: false,
suggestRecentHashtags: true,
showClockOnHeader: true,
useShadow: true,
roundedCorners: false,
circleIcons: true,
contrastedAcct: true,
showFullAcct: false,

View File

@ -101,15 +101,15 @@ props:
ja-JP: "投稿の数"
en-US: "The number of the notes of this user"
pinnedNote:
type: "entity(Note)"
pinnedNotes:
type: "entity(Note)[]"
optional: true
desc:
ja-JP: "ピン留めされた投稿"
en-US: "The pinned note of this user"
pinnedNoteId:
type: "id(Note)"
pinnedNoteIds:
type: "id(Note)[]"
optional: true
desc:
ja-JP: "ピン留めされた投稿のID"

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