Compare commits
308 Commits
Author | SHA1 | Date | |
---|---|---|---|
e55a254353 | |||
555a0f276c | |||
792632d726 | |||
9cac293efc | |||
cd8bfca29c | |||
b5b437b878 | |||
cc2947063a | |||
2864a9027f | |||
e11f547308 | |||
f164661ef2 | |||
c9d993b838 | |||
65f35dc9f4 | |||
b600d462c1 | |||
fa5a82c9ab | |||
7c596be638 | |||
07265f594b | |||
392cb1ba89 | |||
e6f33e997f | |||
a44387f250 | |||
b1b1b7592b | |||
ca668898f4 | |||
fcd437c89f | |||
7f7d7edc7f | |||
bd827f946a | |||
ad8aa1c179 | |||
3ebaf83ce0 | |||
39b1978ff3 | |||
bddff17e5e | |||
0ac9120064 | |||
d90f75425f | |||
dec7d537dc | |||
11e95ea092 | |||
c5e9b69eb3 | |||
120c11b181 | |||
a1ae832129 | |||
3a4833818f | |||
8814fc9c9c | |||
e6e02ece89 | |||
9059c149dd | |||
7d8e70b2ac | |||
89105f5641 | |||
1813d17b4c | |||
ce27b36fd0 | |||
e635a87628 | |||
80c52433cc | |||
1472f0b141 | |||
4d914f5c0a | |||
0318f7344f | |||
413fbb3d0c | |||
8bc47baf4f | |||
e3f6d42a47 | |||
8230935fd3 | |||
f968d05ea0 | |||
d6e5dc2167 | |||
460147fea2 | |||
cea44834bb | |||
1af50fd7b8 | |||
b18013025f | |||
399eb60809 | |||
ed67e3506b | |||
d8ff37fc45 | |||
2fcc3bb1ea | |||
2e680c3d1e | |||
af0a0ef41b | |||
bbfccb0bbf | |||
c89eb5d69f | |||
ebde84214e | |||
03fbae7b6d | |||
f90e9596d4 | |||
944f9524e2 | |||
c61050244e | |||
90337adbbc | |||
7b67e41c5b | |||
91db24fcfc | |||
bb53db905f | |||
0e9a1efe46 | |||
289cd3e200 | |||
e0f847e539 | |||
c2842b486e | |||
7235ade42f | |||
850be2df1d | |||
d504501440 | |||
208392f12c | |||
0fe036c640 | |||
a40c41f0b0 | |||
4affa5b710 | |||
4eb574d991 | |||
2c1577ea24 | |||
b87e7e50b6 | |||
36215d50bd | |||
5ff1245d0c | |||
ebd189fb27 | |||
6f724827bd | |||
b6a0982012 | |||
c3e375e8a5 | |||
302409fd83 | |||
a2046461c1 | |||
6660c34120 | |||
b88ccf0ddd | |||
b898bbf94c | |||
787e89eb95 | |||
1022c2c438 | |||
ba21c62ed4 | |||
bfe66c919b | |||
3dacf7f661 | |||
c0a3ae2612 | |||
da612ef789 | |||
df9cb7cf6e | |||
9c1a26110e | |||
0883d18a6c | |||
c7246c61a5 | |||
c5a1431fc0 | |||
f0118a0dff | |||
cffe96e46f | |||
a9256578f0 | |||
05ed202904 | |||
963b63389a | |||
e04706dc74 | |||
04d4ce5ce1 | |||
24cf3730fa | |||
0700be86e2 | |||
7cca509eb3 | |||
7d7193cb63 | |||
1cf10d05ff | |||
2ec25a7729 | |||
2a9065a61e | |||
7518e30dcf | |||
dc3c80e3ce | |||
a25f61f6be | |||
e70fb71a04 | |||
f499630c2b | |||
43319a8588 | |||
d62b943c5d | |||
8baddf2ea3 | |||
600482660b | |||
72ab5c143e | |||
96ab0e7b4c | |||
b60903e2b4 | |||
b4f4d3f267 | |||
6e017c86e8 | |||
afcfc2dca5 | |||
59e22a12a9 | |||
b740ac3e01 | |||
9719f0df03 | |||
d4be599538 | |||
f88195c90a | |||
3b33f7e752 | |||
67a37294f7 | |||
fd88955696 | |||
9d248dbb5a | |||
20ec4104c6 | |||
6c232d116d | |||
2ef78bcd40 | |||
94ce658ab9 | |||
d8cf4cd341 | |||
0360337df9 | |||
119d38ea08 | |||
bee77afb7f | |||
16d4b16872 | |||
951b2346ab | |||
b29ff0e94b | |||
c8dd8341ca | |||
8bcf44bc16 | |||
50b37a8420 | |||
22df795733 | |||
7e3bf06db1 | |||
6630ca595c | |||
5d01e19ce7 | |||
56df89f8dd | |||
13de984ce3 | |||
15fc0e30d7 | |||
4289c11185 | |||
a3f564e702 | |||
f6734a0c98 | |||
72fb416239 | |||
833f5b09d2 | |||
b21b21f30a | |||
2f77a3f6d2 | |||
0bda655452 | |||
4f80bb7031 | |||
fbe7b3cc9b | |||
8402f0abd7 | |||
149b2ee5a7 | |||
f9d5af0600 | |||
72c4ccaee8 | |||
92999dcaf2 | |||
5bbd318518 | |||
8807894890 | |||
63b7820717 | |||
9e7e2d6977 | |||
89e4c280ae | |||
b6c9f29be4 | |||
74cbbc84ed | |||
ead4197670 | |||
4fc69ccdc8 | |||
f556cb44b9 | |||
45b540d375 | |||
af2d36a3c9 | |||
42a4f92cfa | |||
ccb9ed3489 | |||
773b2aa3d1 | |||
30d5b8d65b | |||
763676a18c | |||
e166ad6780 | |||
034c96d070 | |||
f34f8d304c | |||
944000c05c | |||
e2503cdb47 | |||
52db63bca2 | |||
55dfd9e2a1 | |||
d193cbf2b7 | |||
bdec56a543 | |||
e0a6d9740c | |||
0ce9c057e1 | |||
12a2fdbc20 | |||
57c294bc89 | |||
9758757805 | |||
f9350fa35f | |||
e120da4ecd | |||
328a10b70c | |||
1ed97c8deb | |||
91b970e2aa | |||
99af1bb479 | |||
11ddcbdee3 | |||
6e8a1086d8 | |||
c78945436e | |||
6eff8fde74 | |||
726d5a177e | |||
33495b5cb3 | |||
fe159a13a9 | |||
22a1dc0566 | |||
02e6b732e9 | |||
cc6fa135ac | |||
5747732156 | |||
581d1617d8 | |||
6152fd20bf | |||
19300ca65c | |||
2f3d744e19 | |||
724e812972 | |||
9a6246fd4e | |||
34f44de59c | |||
16e446c121 | |||
8f232a9da9 | |||
ebeb7f8578 | |||
f790673068 | |||
335ab5ab54 | |||
00e0d6ce2c | |||
414fb6d303 | |||
9c35a12211 | |||
bb4fe5174f | |||
3ffd6ff5a2 | |||
b05feb5bf7 | |||
fa171f237d | |||
f2ccb684eb | |||
ffea6522ac | |||
3d40a7df00 | |||
638c41476b | |||
c6d3088374 | |||
0f93be9dd4 | |||
f59982c9c5 | |||
dff67a5e54 | |||
6adcc3b2ed | |||
877ed3663c | |||
6000a82917 | |||
6805f9b3e0 | |||
1366c785f9 | |||
70540b4500 | |||
0967f23b6e | |||
1f7d66169c | |||
af501f5eeb | |||
60be60c923 | |||
48746101e0 | |||
af9c5c6ab7 | |||
602284d38c | |||
26898142c2 | |||
b0a8d7abe9 | |||
dc2b266b75 | |||
07bbd9506a | |||
14bb218287 | |||
29f238c929 | |||
a39a1d4fa5 | |||
15117c63f5 | |||
507ffb6fc6 | |||
6b2e0164cf | |||
02e06eb1de | |||
1b50f78733 | |||
ead629407c | |||
0abbc9e7dd | |||
37681e859e | |||
caabdc68f3 | |||
9e97eaf24d | |||
4cd06a789b | |||
a3ffd968de | |||
0cf40563aa | |||
3e7e7f864b | |||
6ae415e36a | |||
6cefa3ae26 | |||
70de3af3ea | |||
66ed814527 | |||
e12cc3b7a8 | |||
93ea19d7ad | |||
79d592b431 | |||
c9c3a0be82 | |||
f04be199dd | |||
f36cb1cc66 | |||
a5597e3df9 | |||
7f4c28053e | |||
ea24043b22 |
@ -1,3 +1,9 @@
|
||||
# インスタンス名
|
||||
name:
|
||||
|
||||
# インスタンスの紹介
|
||||
description:
|
||||
|
||||
# サーバーのメンテナ情報
|
||||
maintainer:
|
||||
# メンテナの名前
|
||||
@ -55,3 +61,7 @@ twitter:
|
||||
|
||||
# インテグレーション用アプリのコンシューマーシークレット
|
||||
consumer_secret:
|
||||
|
||||
# true にすると、リモートのファイルをキャッシュしなくなります(直リンクします)。
|
||||
# ストレージ容量を節約することができますが、「リモートメディアを表示しない」設定をオンにしているユーザーは、リモートの画像などは見えなくなります。
|
||||
preventCache: false
|
||||
|
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,3 +1,4 @@
|
||||
*.svg -diff -text
|
||||
*.psd -diff -text
|
||||
*.ai -diff -text
|
||||
yarn.lock -diff -text
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,4 +11,4 @@ npm-debug.log
|
||||
run.bat
|
||||
api-docs.json
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
*.log
|
||||
|
11
CHANGELOG.md
Normal file
11
CHANGELOG.md
Normal file
@ -0,0 +1,11 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
3.0.0
|
||||
-----
|
||||
|
||||
### Migration
|
||||
|
||||
起動する前に、`node cli/recount-stats`してください。
|
||||
|
||||
Please run `node cli/recount-stats` before launch.
|
41
README.md
41
README.md
@ -12,15 +12,18 @@
|
||||
> Lead Maintainer: [syuilo][syuilo-link]
|
||||
|
||||
**[Misskey](https://misskey.xyz)** is a completely open source,
|
||||
ultimately sophisticated new type of mini-blog based SNS.
|
||||
ultimately sophisticated professional microblogging software.
|
||||
|
||||
<a href="https://www.patreon.com/syuilo"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="Become a Patron!" width="160" /></a>
|
||||
|
||||

|
||||
|
||||
:sparkles: Features
|
||||
----------------------------------------------------------------
|
||||
* Reactions
|
||||
* User lists
|
||||
* Cusromizable column view (known as MisskeyDeck)
|
||||
* Customizable column view (known as MisskeyDeck)
|
||||
* and widgets!
|
||||
* Private messages
|
||||
* Mute
|
||||
* Streaming
|
||||
@ -46,18 +49,9 @@ If you want to...
|
||||
[![Backers][backers-image]][support-url]
|
||||
[![Sponsors][sponsors-image]][support-url]
|
||||
|
||||
:mortar_board: Notable contributors
|
||||
----------------------------------------------------------------
|
||||
| ![syuilo][syuilo-icon] | ![Morisawa Aya][ayamorisawa-icon] | ![otofune][otofune-icon] | ![akihikodaki][akihikodaki-icon] | ![tamaina][tamaina-icon] | ![rinsuki][rinsuki-icon] |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| [syuilo][syuilo-link]<br>Owner | [Aya Morisawa][ayamorisawa-link]<br>Collaborator | [otofune][otofune-link]<br>Collaborator | [akihikodaki][akihikodaki-link] | [tamaina][tamaina-link] | [rinsuki][rinsuki-link] |
|
||||
|
||||
[List of all contributors](https://github.com/syuilo/misskey/graphs/contributors)
|
||||
|
||||
### :earth_americas: Translators
|
||||
| ![][mirro-san-icon] | ![][Conan-kun-icon] | ![][m4sk1n-icon] |
|
||||
|:-:|:-:|:-:|
|
||||
| [Mirro][mirro-san-link]<br>English, French | [Asriel][Conan-kun-link]<br>English, French | [Marcin Mikołajczak][m4sk1n-link]<br>Polish |
|
||||
| ![][ooo-icon] |
|
||||
|:-:|
|
||||
| [ooo][ooo-link] |
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
----------------------------------------------------------------
|
||||
@ -85,23 +79,8 @@ Misskey is an open-source software licensed under [GNU AGPLv3](LICENSE).
|
||||
[sponsors-image]: https://opencollective.com/misskey/sponsors.svg
|
||||
[support-url]: https://opencollective.com/misskey#support
|
||||
|
||||
<!-- Contributors Info -->
|
||||
[syuilo-link]: https://syuilo.com
|
||||
[syuilo-icon]: https://avatars2.githubusercontent.com/u/4439005?v=3&s=70
|
||||
[ayamorisawa-link]: https://github.com/ayamorisawa
|
||||
[ayamorisawa-icon]: https://avatars0.githubusercontent.com/u/10798641?v=3&s=70
|
||||
[otofune-link]: https://github.com/otofune
|
||||
[otofune-icon]: https://avatars0.githubusercontent.com/u/15062473?v=3&s=70
|
||||
[akihikodaki-link]: https://github.com/akihikodaki
|
||||
[akihikodaki-icon]: https://avatars2.githubusercontent.com/u/17036990?s=70&v=4
|
||||
[rinsuki-link]: https://github.com/rinsuki
|
||||
[rinsuki-icon]: https://avatars0.githubusercontent.com/u/6533808?s=70&v=4
|
||||
[tamaina-link]: https://github.com/tamaina
|
||||
[tamaina-icon]: https://avatars1.githubusercontent.com/u/7973572?s=70&v=4
|
||||
|
||||
[mirro-san-link]: https://github.com/mirro-san
|
||||
[mirro-san-icon]: https://avatars1.githubusercontent.com/u/17948612?s=70&v=4
|
||||
[Conan-kun-link]: https://github.com/Conan-kun
|
||||
[Conan-kun-icon]: https://avatars3.githubusercontent.com/u/30003708?s=70&v=4
|
||||
[m4sk1n-link]: https://github.com/m4sk1n
|
||||
[m4sk1n-icon]: https://avatars3.githubusercontent.com/u/21127288?s=70&v=4
|
||||
[ooo-link]: https://www.patreon.com/user/creators?u=11601413
|
||||
[ooo-icon]: https://c10.patreonusercontent.com/3/eyJ2IjoiMSIsInciOjIwMH0%3D/patreon-media/user/11601413/20cb15f209924302b399b99d3c98b850?token-time=2145916800&token-hash=IO31nK6VZCMWBWU2VAk2c824BX2QZ4DNPKyHHZXS0iw%3D
|
||||
|
42
cli/recount-stats.js
Normal file
42
cli/recount-stats.js
Normal file
@ -0,0 +1,42 @@
|
||||
const { default: Note } = require('../built/models/note');
|
||||
const { default: Meta } = require('../built/models/meta');
|
||||
const { default: User } = require('../built/models/user');
|
||||
|
||||
async function main() {
|
||||
const meta = await Meta.findOne({});
|
||||
|
||||
const notesCount = await Note.count();
|
||||
|
||||
const usersCount = await User.count();
|
||||
|
||||
const originalNotesCount = await Note.count({
|
||||
'_user.host': null
|
||||
});
|
||||
|
||||
const originalUsersCount = await User.count({
|
||||
host: null
|
||||
});
|
||||
|
||||
const stats = {
|
||||
notesCount,
|
||||
usersCount,
|
||||
originalNotesCount,
|
||||
originalUsersCount
|
||||
};
|
||||
|
||||
if (meta) {
|
||||
await Meta.update({}, {
|
||||
$set: {
|
||||
stats
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await Meta.insert({
|
||||
stats
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
console.log('done');
|
||||
}).catch(console.error);
|
12
cli/update-remote-user.js
Normal file
12
cli/update-remote-user.js
Normal file
@ -0,0 +1,12 @@
|
||||
const updatePerson = require('../built/remote/activitypub/models/person').updatePerson;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const user = args[0];
|
||||
|
||||
console.log(`Updating ${user}...`);
|
||||
|
||||
updatePerson(user).then(() => {
|
||||
console.log(`Updated ${user}`);
|
||||
}, e => {
|
||||
console.error(e);
|
||||
});
|
229
locales/de.yml
229
locales/de.yml
@ -57,30 +57,36 @@ common:
|
||||
memo: "Notizen"
|
||||
trends: "Trends"
|
||||
photo-stream: "Bilder"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "Diashow"
|
||||
version: "Version"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "Benachrichtigungen"
|
||||
users: "Empfohlene Benutzer"
|
||||
polls: "Umfragen"
|
||||
post-form: "投稿フォーム"
|
||||
polls: "アンケート"
|
||||
post-form: "Beitragsform"
|
||||
messaging: "Nachrichten"
|
||||
server: "Server-Info"
|
||||
donation: "Spenden"
|
||||
nav: "Navigation"
|
||||
tips: "Tipps"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
local: "ローカル"
|
||||
global: "グローバル"
|
||||
notifications: "通知"
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
widgets: "Widget hinzufügen:"
|
||||
home: "Startseite"
|
||||
local: "Lokal"
|
||||
global: "Global"
|
||||
notifications: "Mitteilungen"
|
||||
list: "Listen"
|
||||
swap-left: "Nach links"
|
||||
swap-right: "Nach rechts"
|
||||
swap-up: "Nach oben"
|
||||
swap-down: "Nach unten"
|
||||
remove: "Spalte löschen"
|
||||
add-column: "Eine Spalte hinzufügen"
|
||||
rename: "Umbenennen"
|
||||
stack-left: "Nach links schichten"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "Verbindung zum Server ist fehlgeschlagen"
|
||||
description: "Es gibt entweder ein Problem mit deiner Internetverbindung, der Server ist nicht erreichbar oder wird gerade gewartet. Bitte versuche es später noch einmal."
|
||||
@ -199,7 +205,7 @@ common/views/components/uploader.vue:
|
||||
common/views/components/visibility-chooser.vue:
|
||||
public: "Öffentlich"
|
||||
home: "Home"
|
||||
home-desc: "ホームタイムラインにのみ公開"
|
||||
home-desc: "Nur auf die Startseite posten"
|
||||
followers: "Folgende"
|
||||
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
||||
specified: "Direkt"
|
||||
@ -216,6 +222,12 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "Fotostream"
|
||||
no-photos: "Keine Fotos"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "Serverinformationen"
|
||||
toggle: "Sicht umschalten"
|
||||
@ -260,7 +272,7 @@ desktop/views/components/drive.file.vue:
|
||||
rename: "Umbenennen"
|
||||
copy-url: "URL kopieren"
|
||||
download: "Download"
|
||||
else-files: "その他..."
|
||||
else-files: "Anderes…"
|
||||
set-as-avatar: "Als Avatar festlegen"
|
||||
set-as-banner: "Setze als Banner"
|
||||
open-in-app: "In der App öffnen"
|
||||
@ -302,10 +314,10 @@ desktop/views/components/drive.vue:
|
||||
upload: "Eine Datei hochladen"
|
||||
url-upload: "Von einer URL hochladen"
|
||||
desktop/views/components/follow-button.vue:
|
||||
following: "フォロー中"
|
||||
follow: "フォロー"
|
||||
request-pending: "フォロー許可待ち"
|
||||
follow-request: "フォロー申請"
|
||||
following: "Folge ich"
|
||||
follow: "Folgen"
|
||||
request-pending: "Ausstehend"
|
||||
follow-request: "Follower-Anfragen"
|
||||
desktop/views/components/followers-window.vue:
|
||||
followers: "{} のフォロワー"
|
||||
desktop/views/components/followers.vue:
|
||||
@ -313,18 +325,18 @@ desktop/views/components/followers.vue:
|
||||
desktop/views/components/following-window.vue:
|
||||
following: "{} のフォロー"
|
||||
desktop/views/components/following.vue:
|
||||
empty: "フォロー中のユーザーはいないようです。"
|
||||
empty: "Du folgst niemanden"
|
||||
desktop/views/components/friends-maker.vue:
|
||||
title: "気になるユーザーをフォロー:"
|
||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||
fetching: "読み込んでいます"
|
||||
refresh: "もっと見る"
|
||||
close: "閉じる"
|
||||
title: "Wem folgen?"
|
||||
empty: "Der ausgewählte Benutzer konnte nicht gefunden werden."
|
||||
fetching: "Lade…"
|
||||
refresh: "Mehr"
|
||||
close: "Schließen"
|
||||
desktop/views/components/game-window.vue:
|
||||
game: "オセロ"
|
||||
game: "Othello"
|
||||
desktop/views/components/home.vue:
|
||||
done: "完了"
|
||||
add-widget: "ウィジェットを追加:"
|
||||
done: "Verbunden"
|
||||
add-widget: "Widget hinzufügen:"
|
||||
add: "Hinzufügen"
|
||||
desktop/views/input-dialog.vue:
|
||||
cancel: "Abbrechen"
|
||||
@ -335,9 +347,9 @@ desktop/views/components/messaging-window.vue:
|
||||
title: "Nachrichten"
|
||||
desktop/views/components/note-detail.vue:
|
||||
more: "Lade weitere Konversationen"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
reposted-by: "{}がRenote"
|
||||
private: "Dieser Post ist privat"
|
||||
deleted: "Dieser Beitrag wurde entfernt"
|
||||
reposted-by: "Repostet von {}"
|
||||
location: "Ort"
|
||||
renote: "Anmerkung"
|
||||
add-reaction: "Reaktion hinzufügen"
|
||||
@ -347,8 +359,8 @@ desktop/views/components/notes.note.vue:
|
||||
renote: "Anmerken"
|
||||
add-reaction: "Eine Reaktion hinzufügen"
|
||||
detail: "Zeige Details"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
private: "Dieser Beitrag ist eine privat"
|
||||
deleted: "Dieser Beitrag wurde entfernt"
|
||||
desktop/views/components/notes.vue:
|
||||
error: "Laden fehlgeschlagen."
|
||||
retry: "Erneut versuchen"
|
||||
@ -358,7 +370,7 @@ desktop/views/components/notifications.vue:
|
||||
desktop/views/components/post-form.vue:
|
||||
reply-placeholder: "Antworte auf diese Anmerkung..."
|
||||
quote-placeholder: "Zitiere diese Anmerkung..."
|
||||
submit: "投稿"
|
||||
submit: "Beitragsform"
|
||||
reply: "Antworten"
|
||||
renote: "Anmerkung"
|
||||
posted: "Gepostet!"
|
||||
@ -389,56 +401,56 @@ desktop/views/components/renote-form.vue:
|
||||
success: "Weitergesagt!"
|
||||
failure: "Weitersagen fehlgeschlagen"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
title: "Bist du dir sicher, dass du das reposten willst?"
|
||||
desktop/views/components/settings-window.vue:
|
||||
settings: "設定"
|
||||
settings: "Experimentelles"
|
||||
desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
drive: "ドライブ"
|
||||
security: "セキュリティ"
|
||||
profile: "Profil"
|
||||
notification: "Mitteilungen"
|
||||
apps: "In App öffnen"
|
||||
mute: "Stummschalten"
|
||||
drive: "Dateien vom Drive anfügen"
|
||||
security: "Sicherheit"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
2fa: "二段階認証"
|
||||
other: "その他"
|
||||
license: "ライセンス"
|
||||
behaviour: "動作"
|
||||
fetch-on-scroll: "スクロールで自動読み込み"
|
||||
fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "詳細設定"
|
||||
api-via-stream: "ストリームを経由したAPIリクエスト"
|
||||
api-via-stream-desc: "この設定をオンにすると、websocket接続を経由してAPIリクエストが行われます(パフォーマンス向上が期待できます)。オフにすると、ネイティブの fetch APIが利用されます。この設定はこのデバイスのみ有効です。"
|
||||
display: "デザインと表示"
|
||||
customize: "ホームをカスタマイズ"
|
||||
dark-mode: "ダークモード"
|
||||
circle-icons: "円形のアイコンを使用"
|
||||
gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用"
|
||||
password: "Passwort"
|
||||
2fa: "Zwei-Faktor-Authentifizierung"
|
||||
other: "Anderes"
|
||||
license: "Lizenz"
|
||||
behaviour: "Verhalten"
|
||||
fetch-on-scroll: "Aktualisieren beim scrollen"
|
||||
fetch-on-scroll-desc: "Wenn du runterscrollst empfängt die Seite automatisch zusätzliche Inhalte."
|
||||
auto-popout: "Automatische Pop-out Fenster"
|
||||
auto-popout-desc: "Pop-out ein offenes Fenster wenn möglich. Diese Einstellung wird im Browser gespeichert."
|
||||
advanced: "Erweiterte Einstellungen"
|
||||
api-via-stream: "API-Anfrage via stream"
|
||||
api-via-stream-desc: "API-Anfrage über WebSocket statt native Aktualisierungs-API (für bessere Leistung). Diese Einstellung wird im Browser gespeichert."
|
||||
display: "Erscheinungsbild und Anzeige"
|
||||
customize: "Startseite anpassen"
|
||||
dark-mode: "Nacht Modus"
|
||||
circle-icons: "Kreisförmige Icons"
|
||||
gradient-window-header: "Übergang in Fensterköpfen"
|
||||
post-form-on-timeline: "タイムライン上部に投稿フォームを表示する"
|
||||
show-reply-target: "リプライ先を表示する"
|
||||
show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
|
||||
show-renoted-my-notes: "Renoteされた自分の投稿をタイムラインに表示する"
|
||||
show-maps: "マップの自動展開"
|
||||
show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。"
|
||||
sound: "サウンド"
|
||||
enable-sounds: "サウンドを有効にする"
|
||||
enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。"
|
||||
volume: "ボリューム"
|
||||
test: "テスト"
|
||||
mobile: "モバイル"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
language: "言語"
|
||||
pick-language: "言語を選択"
|
||||
recommended: "推奨"
|
||||
auto: "自動"
|
||||
specify-language: "言語を指定"
|
||||
show-reply-target: "Zeige Antworten"
|
||||
show-my-renotes: "Zeige meine Reposts auf der Zeitleiste"
|
||||
show-renoted-my-notes: "Zeige meine Reposts, die geteilt wurden, auf der Zeitleiste"
|
||||
show-maps: "Karte anzeigen"
|
||||
show-maps-desc: "Zeige den Standort zu diesem Beitrag automatisch an."
|
||||
sound: "Ton"
|
||||
enable-sounds: "Ton aktivieren"
|
||||
enable-sounds-desc: "Spiel einen Ton ab beim Erhalten eines Beitrags bzw. einer Nachricht. Diese Einstellung wird im Browser gespeichert."
|
||||
volume: "Lautstärke"
|
||||
test: "Test"
|
||||
mobile: "Mobil"
|
||||
disable-via-mobile: "Diesen Beitrag nicht mit 'vom Handy' absenden"
|
||||
language: "Sprache"
|
||||
pick-language: "Sprache auswählen"
|
||||
recommended: "Empfohlen"
|
||||
auto: "Automatisch"
|
||||
specify-language: "Sprache auswählen"
|
||||
language-desc: "変更はページの再度読み込み後に反映されます。"
|
||||
cache: "キャッシュ"
|
||||
clean-cache: "クリーンアップ"
|
||||
cache-warn: "クリーンアップを行うと、ブラウザに記憶されたアカウント情報のキャッシュ、書きかけの投稿・返信・メッセージ、およびその他のデータ(設定情報含む)が削除されます。クリーンアップを行った後はページを再度読み込みする必要があります。"
|
||||
cache-warn: "Der Cache deines Benutzerkontos (Info, Beiträge, Antworten, Direktnachrichten, Einstellungen), die lokal im Browser gespeichert sind werden gelöscht.\nDu musst die Seite aktualisieren nachdem du aufgeräumt hast."
|
||||
cache-cleared: "キャッシュを削除しました"
|
||||
cache-cleared-desc: "ページを再度読み込みしてください。"
|
||||
auto-watch: "投稿の自動ウォッチ"
|
||||
@ -447,9 +459,9 @@ desktop/views/components/settings.vue:
|
||||
operator: "このサーバーの運営者"
|
||||
update: "Misskey Update"
|
||||
version: "バージョン:"
|
||||
latest-version: "最新のバージョン:"
|
||||
update-checking: "アップデートを確認中"
|
||||
do-update: "アップデートを確認"
|
||||
latest-version: "Neuste Version:"
|
||||
update-checking: "Suche nach Updates"
|
||||
do-update: "Suche nach Updates"
|
||||
update-settings: "詳細設定"
|
||||
prevent-update: "アップデートを延期する(非推奨)"
|
||||
prevent-update-desc: "この設定をオンにしてもアップデートが反映される場合があります。この設定はこのデバイスのみ有効です。"
|
||||
@ -461,20 +473,20 @@ desktop/views/components/settings.vue:
|
||||
debug-mode: "デバッグモードを有効にする"
|
||||
debug-mode-desc: "この設定はブラウザに記憶されます。"
|
||||
experimental: "実験的機能を有効にする"
|
||||
experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。"
|
||||
tools: "ツール"
|
||||
task-manager: "タスクマネージャ"
|
||||
experimental-desc: "Experimentelle Funktionen können die Stabilität von Misskey beeinträchtigen. Diese Einstellung wird im Browser gespeichert."
|
||||
tools: "Werkzeuge"
|
||||
task-manager: "Taskmanager"
|
||||
third-parties: "サードパーティ"
|
||||
desktop/views/components/settings.2fa.vue:
|
||||
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
|
||||
detail: "詳細..."
|
||||
url: "https://www.google.co.jp/intl/ja/landing/2step/"
|
||||
url: "https://www.google.de/intl/de/landing/2step/"
|
||||
caution: "登録したデバイスを紛失するなどした場合、Misskeyにサインインできなくなりますのでご注意ください。"
|
||||
register: "デバイスを登録する"
|
||||
already-registered: "既に設定は完了しています。"
|
||||
unregister: "設定を解除"
|
||||
unregistered: "二段階認証が無効になりました。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
register: "Ein Gerät registrieren"
|
||||
already-registered: "Das Gerät wurde bereits registriert"
|
||||
unregister: "Abschalten"
|
||||
unregistered: "Zwei-Faktor-Authentifizierung wurde deaktiviert."
|
||||
enter-password: "Bitte Passwort eingeben"
|
||||
authenticator: "まず、Google Authenticatorをお使いのデバイスにインストールします:"
|
||||
howtoinstall: "インストール方法はこちら"
|
||||
scan: "次に、表示されているQRコードをスキャンします:"
|
||||
@ -489,16 +501,16 @@ desktop/views/components/settings.api.vue:
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
enter-password: "Bitte Passwort eingeben"
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
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: "もう一度新しいパスワードを入力してください"
|
||||
enter-current-password: "Derzeitiges Passwort eingeben"
|
||||
enter-new-password: "Neues Passwort eingeben"
|
||||
enter-new-password-again: "Neues Passwort erneut eingeben"
|
||||
not-match: "新しいパスワードが一致しません"
|
||||
changed: "パスワードを変更しました"
|
||||
desktop/views/components/settings.profile.vue:
|
||||
@ -515,9 +527,9 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
title: "Taskmanager"
|
||||
desktop/views/components/timeline.vue:
|
||||
home: "Home"
|
||||
local: "Lokal"
|
||||
@ -559,12 +571,19 @@ desktop/views/components/users-list.vue:
|
||||
all: "すべて"
|
||||
iknow: "知り合い"
|
||||
load-more: "もっと"
|
||||
fetching: "読み込んでいます"
|
||||
fetching: "Lade…"
|
||||
desktop/views/components/users-list-item.vue:
|
||||
followed: "フォローされています"
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -681,7 +700,7 @@ mobile/views/components/follow-button.vue:
|
||||
mobile/views/components/friends-maker.vue:
|
||||
title: "気になるユーザーをフォロー"
|
||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||
fetching: "読み込んでいます"
|
||||
fetching: "Lade…"
|
||||
refresh: "もっと見る"
|
||||
close: "閉じる"
|
||||
mobile/views/components/note.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
@ -785,7 +804,7 @@ mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
read-all: "すべての通知を既読にしますか?"
|
||||
mobile/views/pages/settings/settings.profile.vue:
|
||||
title: "プロフィール"
|
||||
title: "Profil"
|
||||
name: "名前"
|
||||
account: "アカウント"
|
||||
location: "場所"
|
||||
@ -795,7 +814,7 @@ mobile/views/pages/settings/settings.profile.vue:
|
||||
banner: "バナー"
|
||||
is-cat: "このアカウントはCatです"
|
||||
save: "保存"
|
||||
saved: "プロフィールを保存しました"
|
||||
saved: "Profil wurde aktualisiert"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
mobile/views/pages/search.vue:
|
||||
@ -809,7 +828,7 @@ mobile/views/pages/settings.vue:
|
||||
lang-tip: "変更はページの再読み込み後に反映されます。"
|
||||
recommended: "推奨"
|
||||
auto: "自動"
|
||||
specify-language: "言語を指定"
|
||||
specify-language: "Sprache auswählen"
|
||||
design: "デザインと表示"
|
||||
dark-mode: "ダークモード"
|
||||
i-am-under-limited-internet: "私は通信を制限されている"
|
||||
@ -832,9 +851,9 @@ mobile/views/pages/settings.vue:
|
||||
twitter-disconnect: "切断する"
|
||||
update: "Misskey Update"
|
||||
version: "バージョン:"
|
||||
latest-version: "最新のバージョン:"
|
||||
update-checking: "アップデートを確認中"
|
||||
check-for-updates: "アップデートを確認"
|
||||
latest-version: "Neuste Version:"
|
||||
update-checking: "Suche nach Updates"
|
||||
check-for-updates: "Suche nach Updates"
|
||||
no-updates: "利用可能な更新はありません"
|
||||
no-updates-desc: "お使いのMisskeyは最新です。"
|
||||
update-available: "新しいバージョンが利用可能です"
|
||||
|
@ -54,9 +54,10 @@ common:
|
||||
timemachine: "Calendar (Time Machine)"
|
||||
activity: "Activity"
|
||||
rss: "RSS reader"
|
||||
memo: "Memo"
|
||||
memo: "Sticky note"
|
||||
trends: "Trends"
|
||||
photo-stream: "Photo stream"
|
||||
posts-monitor: "Chart of posts"
|
||||
slideshow: "Slideshow"
|
||||
version: "Version"
|
||||
broadcast: "Broadcast"
|
||||
@ -69,6 +70,7 @@ common:
|
||||
donation: "Donation"
|
||||
nav: "Navigation"
|
||||
tips: "Tips"
|
||||
hashtags: "Hashtags"
|
||||
deck:
|
||||
widgets: "Widgets"
|
||||
home: "Home"
|
||||
@ -76,11 +78,15 @@ common:
|
||||
global: "Global"
|
||||
notifications: "Notifications"
|
||||
list: "List"
|
||||
swap-left: "Move Left"
|
||||
swap-right: "Move Right"
|
||||
swap-left: "Move left"
|
||||
swap-right: "Move right"
|
||||
swap-up: "Move upward"
|
||||
swap-down: "Move downward"
|
||||
remove: "Remove"
|
||||
add-column: "Add a column"
|
||||
rename: "名前を変更"
|
||||
rename: "Rename"
|
||||
stack-left: "Stack to left"
|
||||
pop-right: "Pop to right"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "Unable to connect to the server"
|
||||
description: "There is a problem either with your Internet connection, or the server may be down or under maintenance. Please {try again} later."
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "Photostream"
|
||||
no-photos: "No photos"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "Chart of posts"
|
||||
toggle: "Toggle views"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "Hashtags"
|
||||
count: "{} users mentioned"
|
||||
common/views/widgets/server.vue:
|
||||
title: "Server info"
|
||||
toggle: "Toggle views"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "Memo"
|
||||
title: "Sticky note"
|
||||
memo: "Write here!"
|
||||
save: "Save"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -422,7 +434,7 @@ desktop/views/components/settings.vue:
|
||||
show-my-renotes: "Show my reposts in the timeline"
|
||||
show-renoted-my-notes: "Show my posts that have been shared in the timeline"
|
||||
show-maps: "Show the map"
|
||||
show-maps-desc: "Automatically show the map of the location attached to the post."
|
||||
show-maps-desc: "Automatically show the location on the map attached to this post."
|
||||
sound: "Sound"
|
||||
enable-sounds: "Enable sound"
|
||||
enable-sounds-desc: "Play a sound when you receive a post/message. This setting is stored in the browser."
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "this post is private"
|
||||
deleted: "this post has been deleted"
|
||||
media-count: "{} media attached"
|
||||
poll: "Polls"
|
||||
poll: "Poll"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "Task Manager"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "Popout"
|
||||
close: "Close"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "Only media posts"
|
||||
is-media-view: "Media view"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "Reposted by {}"
|
||||
private: "this post is private"
|
||||
deleted: "this post has been deleted"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "about"
|
||||
gotit: "Got it!"
|
||||
|
@ -54,21 +54,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "結果を見る"
|
||||
voted: "投票済み"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -372,7 +384,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
|
149
locales/fr.yml
149
locales/fr.yml
@ -5,7 +5,7 @@ meta:
|
||||
common:
|
||||
misskey: "Une planète du fédiverse"
|
||||
about-title: "Une ⭐ du fédiverse."
|
||||
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
||||
about: "Merci d'avoir découvert Misskey. Misskey est une <b>plateforme de micro-blogging distribuée</b> née sur Terre. Parce qu'il fait partie du Fédiverse (un univers composé de diverses plateformes de réseaux sociaux organisées), il est mutuellement connecté avec d'autres plateformes de réseaux sociaux. Désirez-vous prendre une pause, pendant un instant, loin de l'agitation de la ville et plonger dans un nouvel Internet ?"
|
||||
time:
|
||||
unknown: "inconnu"
|
||||
future: "future"
|
||||
@ -24,7 +24,7 @@ common:
|
||||
wednesday: "M"
|
||||
thursday: "J"
|
||||
friday: "V"
|
||||
saturday: "土"
|
||||
saturday: "S"
|
||||
reactions:
|
||||
like: "Aime"
|
||||
love: "Adore"
|
||||
@ -36,10 +36,10 @@ common:
|
||||
confused: "Confus"
|
||||
pudding: "Pudding"
|
||||
note-placeholders:
|
||||
a: "今どうしてる?"
|
||||
b: "何かありましたか?"
|
||||
a: "Que faîtes vous à cet instant ?"
|
||||
b: "Quoi de neuf ?"
|
||||
c: "Qu'avez-vous en tête ?"
|
||||
d: "言いたいことは?"
|
||||
d: "Voulez-vous exprimer quelque chose ?"
|
||||
e: "Écrivez ici"
|
||||
f: "En attente de vos écrits"
|
||||
delete: "Supprimer"
|
||||
@ -54,9 +54,10 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "Activité"
|
||||
rss: "Lecteur de flux RSS"
|
||||
memo: "Note"
|
||||
memo: "Pense-bête"
|
||||
trends: "Tendances"
|
||||
photo-stream: "Flux de photos"
|
||||
posts-monitor: "Graph des publications"
|
||||
slideshow: "Diaporama"
|
||||
version: "Version"
|
||||
broadcast: "Diffusion"
|
||||
@ -69,6 +70,7 @@ common:
|
||||
donation: "Dons"
|
||||
nav: "Navigation"
|
||||
tips: "Conseils"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "Widgets"
|
||||
home: "Accueil"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "Liste"
|
||||
swap-left: "Déplacer à gauche"
|
||||
swap-right: "Déplacer à droite"
|
||||
swap-up: "Vers le haut"
|
||||
swap-down: "Vers le bas"
|
||||
remove: "Supprimer"
|
||||
add-column: "Ajouter une colonne"
|
||||
rename: "名前を変更"
|
||||
rename: "Renommer"
|
||||
stack-left: "Vers la gauche"
|
||||
pop-right: "Vers la droite"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "Impossible de se connecter au server."
|
||||
description: "Il y a soit un problème avec votre connexion internet, soit le serveur est hors-ligne ou en maintenance. Veuillez {ressayer} plus tard."
|
||||
@ -104,7 +110,7 @@ common/views/components/connect-failed.troubleshooter.vue:
|
||||
success: "Connexion au serveur de Misskey reussie!"
|
||||
success-desc: "La connexion au serveur a été reussie. Veuillez recharger la page."
|
||||
flush: "Vider le cache"
|
||||
set-version: "バージョン指定"
|
||||
set-version: "Choisissez une version"
|
||||
common/views/components/messaging.vue:
|
||||
search-user: "Trouver un utilisateur"
|
||||
you: "Vous"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "Montrer les résultats"
|
||||
voted: "Voté"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "Vous devez entrer au moins deux choix"
|
||||
no-only-one-choice: "Vous devez saisir au moins deux choix."
|
||||
choice-n: "Choix {}"
|
||||
remove: "Supprimer ce choix"
|
||||
add: "+ Ajouter un choix"
|
||||
destroy: "Supprimer ce sondage"
|
||||
destroy: "Annuler ce sondage"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Choisissez votre réaction"
|
||||
common/views/components/signin.vue:
|
||||
@ -199,7 +205,7 @@ common/views/components/uploader.vue:
|
||||
common/views/components/visibility-chooser.vue:
|
||||
public: "Public"
|
||||
home: "Accueil"
|
||||
home-desc: "ホームタイムラインにのみ公開"
|
||||
home-desc: "Publier sur le fil d'Accueil uniquement"
|
||||
followers: "Abonnés"
|
||||
followers-desc: "Publier à vos abonnés uniquement"
|
||||
specified: "Direct"
|
||||
@ -216,18 +222,24 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "Flux de photo"
|
||||
no-photos: "Pas de photos"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "Graph des publications"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "Info sur le serveur"
|
||||
toggle: "Afficher les vues"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "Note"
|
||||
title: "Pense-bête"
|
||||
memo: "Écrivez ici !"
|
||||
save: "Enregistrer"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Black ... Total"
|
||||
notes: "Blue ... Notes"
|
||||
replies: "Red ... Replies"
|
||||
renotes: "Green ... Renotes"
|
||||
notes: "Bleu ... Notes"
|
||||
replies: "Rouge ... Réponses"
|
||||
renotes: "Vert ... Partages"
|
||||
desktop/views/components/activity.vue:
|
||||
title: "Activitié"
|
||||
toggle: "Afficher les vues"
|
||||
@ -304,7 +316,7 @@ desktop/views/components/drive.vue:
|
||||
desktop/views/components/follow-button.vue:
|
||||
following: "Abonnements"
|
||||
follow: "Suivre"
|
||||
request-pending: "フォロー許可待ち"
|
||||
request-pending: "En attente d'approbation"
|
||||
follow-request: "Demande d'abonnement"
|
||||
desktop/views/components/followers-window.vue:
|
||||
followers: "{} abonnés"
|
||||
@ -316,14 +328,14 @@ desktop/views/components/following.vue:
|
||||
empty: "Vous ne suivez aucun compte."
|
||||
desktop/views/components/friends-maker.vue:
|
||||
title: "Utilisateurs recommandés :"
|
||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||
empty: "Impossible de trouver des utilisateurs à recommander."
|
||||
fetching: "Chargement"
|
||||
refresh: "Plus"
|
||||
close: "Fermer"
|
||||
desktop/views/components/game-window.vue:
|
||||
game: "Othello"
|
||||
desktop/views/components/home.vue:
|
||||
done: "完了"
|
||||
done: "Envoyer"
|
||||
add-widget: "Ajouter un widget"
|
||||
add: "Ajouter"
|
||||
desktop/views/input-dialog.vue:
|
||||
@ -337,14 +349,14 @@ desktop/views/components/note-detail.vue:
|
||||
more: "Charger davantage de conversations"
|
||||
private: "cette publication est privée"
|
||||
deleted: "cette publication a été supprimée"
|
||||
reposted-by: "{}がRenote"
|
||||
reposted-by: "Republié par {}"
|
||||
location: "Géolocalisation"
|
||||
renote: "Renote"
|
||||
renote: "Republier"
|
||||
add-reaction: "Ajouter votre reaction"
|
||||
desktop/views/components/notes.note.vue:
|
||||
reposted-by: "Reposté par {}"
|
||||
reply: "Répondre"
|
||||
renote: "Renote"
|
||||
renote: "Republier"
|
||||
add-reaction: "Ajouter votre reaction"
|
||||
detail: "Afficher les détails"
|
||||
private: "cette publication est privée"
|
||||
@ -360,7 +372,7 @@ desktop/views/components/post-form.vue:
|
||||
quote-placeholder: "Citer cette note"
|
||||
submit: "Poster"
|
||||
reply: "Répondre"
|
||||
renote: "Renote"
|
||||
renote: "Republier"
|
||||
posted: "Posté!"
|
||||
replied: "Répondu!"
|
||||
reposted: "Reposté!"
|
||||
@ -384,7 +396,7 @@ desktop/views/components/progress-dialog.vue:
|
||||
desktop/views/components/renote-form.vue:
|
||||
quote: "Citer..."
|
||||
cancel: "Annuler"
|
||||
renote: "Renote"
|
||||
renote: "Republier"
|
||||
reposting: "Repost en cours..."
|
||||
success: "Reposté!"
|
||||
failure: "La renote a échoué"
|
||||
@ -405,25 +417,25 @@ desktop/views/components/settings.vue:
|
||||
other: "Autres"
|
||||
license: "License"
|
||||
behaviour: "Comportement"
|
||||
fetch-on-scroll: "スクロールで自動読み込み"
|
||||
fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
fetch-on-scroll: "Chargement lors du défilement"
|
||||
fetch-on-scroll-desc: "Chargement automatique du contenu lors du défilement de la page."
|
||||
auto-popout: "Fenêtre contextuelle automatique"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "Paramètres avancés"
|
||||
api-via-stream: "ストリームを経由したAPIリクエスト"
|
||||
api-via-stream: "Requête API via le flux"
|
||||
api-via-stream-desc: "この設定をオンにすると、websocket接続を経由してAPIリクエストが行われます(パフォーマンス向上が期待できます)。オフにすると、ネイティブの fetch APIが利用されます。この設定はこのデバイスのみ有効です。"
|
||||
display: "デザインと表示"
|
||||
display: "Affichage et design"
|
||||
customize: "Personnaliser l'Accueil"
|
||||
dark-mode: "Mode nuit"
|
||||
circle-icons: "Utiliser des icônes circulaires"
|
||||
gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用"
|
||||
post-form-on-timeline: "タイムライン上部に投稿フォームを表示する"
|
||||
show-reply-target: "リプライ先を表示する"
|
||||
show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
|
||||
show-my-renotes: "Afficher mes republications dans le fil"
|
||||
show-renoted-my-notes: "Renoteされた自分の投稿をタイムラインに表示する"
|
||||
show-maps: "Afficher la carte"
|
||||
show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。"
|
||||
sound: "サウンド"
|
||||
sound: "Son"
|
||||
enable-sounds: "Activer le son"
|
||||
enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。"
|
||||
volume: "Volume"
|
||||
@ -439,17 +451,17 @@ desktop/views/components/settings.vue:
|
||||
cache: "Cache"
|
||||
clean-cache: "Nettoyage"
|
||||
cache-warn: "クリーンアップを行うと、ブラウザに記憶されたアカウント情報のキャッシュ、書きかけの投稿・返信・メッセージ、およびその他のデータ(設定情報含む)が削除されます。クリーンアップを行った後はページを再度読み込みする必要があります。"
|
||||
cache-cleared: "キャッシュを削除しました"
|
||||
cache-cleared-desc: "ページを再度読み込みしてください。"
|
||||
cache-cleared: "Cache nettoyé"
|
||||
cache-cleared-desc: "Veuillez recharger la page."
|
||||
auto-watch: "投稿の自動ウォッチ"
|
||||
auto-watch-desc: "リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。"
|
||||
about: "À propose de Misskey"
|
||||
operator: "このサーバーの運営者"
|
||||
operator: "L'admin de cette instance"
|
||||
update: "Mise à jour de Misskey"
|
||||
version: "Version :"
|
||||
latest-version: "Dernière version :"
|
||||
update-checking: "Recherche de mises à jour"
|
||||
do-update: "アップデートを確認"
|
||||
do-update: "Rechercher des mises à jour"
|
||||
update-settings: "Paramètres avancés"
|
||||
prevent-update: "アップデートを延期する(非推奨)"
|
||||
prevent-update-desc: "この設定をオンにしてもアップデートが反映される場合があります。この設定はこのデバイスのみ有効です。"
|
||||
@ -458,13 +470,13 @@ desktop/views/components/settings.vue:
|
||||
update-available: "Nouvelle version disponible !"
|
||||
update-available-desc: "ページを再度読み込みすると更新が適用されます。"
|
||||
advanced-settings: "Réglages avancés"
|
||||
debug-mode: "デバッグモードを有効にする"
|
||||
debug-mode-desc: "この設定はブラウザに記憶されます。"
|
||||
experimental: "実験的機能を有効にする"
|
||||
debug-mode: "Activer le mode debug"
|
||||
debug-mode-desc: "Ce paramètre est stocké dans le navigateur."
|
||||
experimental: "Activer les fonctionnalités expérimentales"
|
||||
experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。"
|
||||
tools: "Outils"
|
||||
task-manager: "Gestionnaire de tâches"
|
||||
third-parties: "サードパーティ"
|
||||
third-parties: "Services tiers"
|
||||
desktop/views/components/settings.2fa.vue:
|
||||
intro: "Si vous configurez la vérication en deux étapes vous aurez non seulement besoin de votre mot de passe mais aussi un appareil déjà pré-enregistré(tel que votre smartphone) ce qui ameliora grandement la sécurité de votre compte."
|
||||
detail: "Voir les détails..."
|
||||
@ -491,7 +503,7 @@ desktop/views/components/settings.api.vue:
|
||||
token: "Jeton :"
|
||||
enter-password: "Veuillez entrer le mot de passe"
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
no-apps: "Aucune application autorisée"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "Aucun utilisateurs mis en sourdine"
|
||||
desktop/views/components/settings.password.vue:
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "cette publication est privée"
|
||||
deleted: "cette publication a été supprimée"
|
||||
media-count: "{} médias attachés"
|
||||
poll: "Sondages"
|
||||
poll: "Sondage"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "Gestionnaire de tâches"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -553,7 +565,7 @@ desktop/views/components/user-lists-window.vue:
|
||||
create-list: "Créer une liste"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Publications"
|
||||
following: "フォロー"
|
||||
following: "Abonné à"
|
||||
followers: "Abonnés"
|
||||
desktop/views/components/users-list.vue:
|
||||
all: "Tout"
|
||||
@ -565,13 +577,20 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "Fermer"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "Reposté par {}"
|
||||
private: "cette publication est privée"
|
||||
deleted: "cette publication a été supprimée"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "à propos"
|
||||
gotit: "J'ai compris !"
|
||||
signin: "Connexion"
|
||||
signup: "S'enregistrer"
|
||||
signin-button: "Se connecter"
|
||||
signup-button: "やる"
|
||||
signup-button: "S'inscrire"
|
||||
timeline: "Fil d'actualité"
|
||||
desktop/views/pages/drive.vue:
|
||||
title: "Lecteur de Misskey"
|
||||
@ -655,7 +674,7 @@ mobile/views/components/drive.vue:
|
||||
nothing-in-drive: "Rien"
|
||||
folder-is-empty: "Ce dossier est vide"
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
deletion-alert: "Désolé ! La suppression d’un dossier n’est pas encore implémentée."
|
||||
folder-name: "Nom du dossier"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
@ -679,7 +698,7 @@ mobile/views/components/follow-button.vue:
|
||||
request-pending: "フォロー許可待ち"
|
||||
follow-request: "Demande d'abonnement"
|
||||
mobile/views/components/friends-maker.vue:
|
||||
title: "気になるユーザーをフォロー"
|
||||
title: "Abonnez-vous aux utilisateurs"
|
||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||
fetching: "Chargement"
|
||||
refresh: "Voir plus"
|
||||
@ -715,34 +734,34 @@ mobile/views/components/notifications.vue:
|
||||
mobile/views/components/post-form.vue:
|
||||
add-visible-user: "Ajouter un utilisateur"
|
||||
submit: "Poster"
|
||||
reply: "返信"
|
||||
renote: "Renote"
|
||||
quote-placeholder: "この投稿を引用... (オプション)"
|
||||
reply: "Répondre"
|
||||
renote: "Republier"
|
||||
quote-placeholder: "Citer ce billet ... (Facultatif)"
|
||||
reply-placeholder: "Répondre à cette note"
|
||||
cw-placeholder: "内容への注釈 (オプション)"
|
||||
location-alert: "お使いの端末は位置情報に対応していません"
|
||||
error: "エラー"
|
||||
username-prompt: "ユーザー名を入力してください"
|
||||
location-alert: "Votre appareil ne prend pas en charge les services de localisation"
|
||||
error: "Erreur"
|
||||
username-prompt: "Saisir un nom d'utilisateur"
|
||||
mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "Sondage"
|
||||
private: "cette publication est privée"
|
||||
deleted: "cette publication a été supprimée"
|
||||
media-count: "{} médias attachés"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "Pas de notes"
|
||||
load-more: "Afficher plus"
|
||||
mobile/views/components/ui.nav.vue:
|
||||
timeline: "タイムライン"
|
||||
timeline: "Fil d'actualité"
|
||||
notifications: "Notifications"
|
||||
messaging: "Messages"
|
||||
follow-requests: "フォロー申請"
|
||||
follow-requests: "Demandes d'abonnement"
|
||||
search: "Rechercher"
|
||||
drive: "Drive"
|
||||
favorites: "お気に入り"
|
||||
user-lists: "リスト"
|
||||
widgets: "ウィジェット"
|
||||
game: "ゲーム"
|
||||
darkmode: "ダークモード"
|
||||
favorites: "Favoris"
|
||||
user-lists: "Listes"
|
||||
widgets: "Modules"
|
||||
game: "Jeux"
|
||||
darkmode: "Mode nuit"
|
||||
settings: "Réglages"
|
||||
about: "À propose de Misskey"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
@ -757,10 +776,10 @@ mobile/views/pages/favorites.vue:
|
||||
title: "Favoris"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Listes"
|
||||
enter-list-name: "リスト名を入力してください"
|
||||
enter-list-name: "Nom de la liste"
|
||||
mobile/views/pages/drive.vue:
|
||||
drive: "Drive"
|
||||
more: "もっと見る"
|
||||
more: "Afficher plus ..."
|
||||
mobile/views/pages/followers.vue:
|
||||
followers-of: "Abonnés de {}"
|
||||
mobile/views/pages/following.vue:
|
||||
@ -806,7 +825,7 @@ mobile/views/pages/selectdrive.vue:
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Connecté en tant que {}"
|
||||
lang: "Langue"
|
||||
lang-tip: "変更はページの再読み込み後に反映されます。"
|
||||
lang-tip: "Le rechargement de la page est requis afin d'appliquer les modifications."
|
||||
recommended: "Recommandé"
|
||||
auto: "Automatique"
|
||||
specify-language: "Spécifier la langue"
|
||||
@ -822,7 +841,7 @@ mobile/views/pages/settings.vue:
|
||||
post-style-standard: "Standard"
|
||||
post-style-smart: "Intelligent"
|
||||
behavior: "Comportement"
|
||||
fetch-on-scroll: "スクロールで自動読み込み"
|
||||
fetch-on-scroll: "Chargement lors du défilement"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "Afficher les photos jointes en haute qualité"
|
||||
load-remote-media: "Afficher les médias sur le serveur distant"
|
||||
|
@ -54,21 +54,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "結果を見る"
|
||||
voted: "投票済み"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -372,7 +384,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
|
@ -3,7 +3,7 @@ meta:
|
||||
divider: ""
|
||||
|
||||
common:
|
||||
misskey: "A planet of fediverse"
|
||||
misskey: "A ⭐ of fediverse"
|
||||
about-title: "A ⭐ of fediverse."
|
||||
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
||||
|
||||
@ -60,21 +60,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
@ -85,9 +87,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
@ -163,11 +169,11 @@ common/views/components/poll.vue:
|
||||
voted: "投票済み"
|
||||
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
@ -245,12 +251,21 @@ common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
empty: "トレンドなし"
|
||||
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
|
||||
@ -429,7 +444,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
|
||||
desktop/views/components/post-form-window.vue:
|
||||
@ -595,7 +610,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
@ -659,6 +674,15 @@ desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -738,7 +762,7 @@ desktop/views/widgets/notifications.vue:
|
||||
settings: "通知の設定"
|
||||
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
|
||||
@ -856,7 +880,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
|
@ -54,21 +54,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "結果を見る"
|
||||
voted: "投票済み"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -372,7 +384,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
|
@ -5,7 +5,7 @@ meta:
|
||||
common:
|
||||
misskey: "Planeta Fediwersum"
|
||||
about-title: "⭐ Fediwersum"
|
||||
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
||||
about: "Dziękujemy za znalezienie Misskey. Misskey jest <b>zdecentralizowaną platformą mikroblogową</b> powstałą na Ziemi. Ponieważ działa ona w Fediwersum (uniwersum, w którego skład wchodzi wiele sieci społecznościowych), jest ona połączona z innymi platformami społecznościowymi. Spróbujesz odpocząć od zatłoczoneo miasta i zanurzyć się w nowym Internecie?"
|
||||
time:
|
||||
unknown: "nieznany"
|
||||
future: "w przyszłości"
|
||||
@ -54,9 +54,10 @@ common:
|
||||
timemachine: "Kalendarz (wehikuł czasu)"
|
||||
activity: "Aktywność"
|
||||
rss: "Czytnik RSS"
|
||||
memo: "Notatki"
|
||||
memo: "Notatka"
|
||||
trends: "Na czasie"
|
||||
photo-stream: "Photostream"
|
||||
posts-monitor: "Wykres wpisów"
|
||||
slideshow: "Pokaz slajdów"
|
||||
version: "Wersja"
|
||||
broadcast: "Transmisja"
|
||||
@ -69,6 +70,7 @@ common:
|
||||
donation: "Dotacje"
|
||||
nav: "Nawigacja"
|
||||
tips: "Wskazówki"
|
||||
hashtags: "Hashtagi"
|
||||
deck:
|
||||
widgets: "Widżety"
|
||||
home: "Strona główna"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "Listy"
|
||||
swap-left: "Przesuń w lewo"
|
||||
swap-right: "Przesuń w prawo"
|
||||
swap-up: "Przenieś w górę"
|
||||
swap-down: "Przenieś w dół"
|
||||
remove: "Usuń"
|
||||
add-column: "Dodaj kolumnę"
|
||||
rename: "名前を変更"
|
||||
rename: "Zmień nazwę"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "Nie udało się połączyć z serwerem"
|
||||
description: "Wystąpił problem z Twoim połączeniem z Internetem, lub z serwerem. {Spróbuj ponownie} wkrótce."
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "Pokaż wyniki"
|
||||
voted: "Zagłosowano"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "Musisz wprowadzić dwie lub więcej opcji."
|
||||
no-only-one-choice: "Musisz wprowadzić przynajmniej dwie opcje."
|
||||
choice-n: "Opcja {}"
|
||||
remove: "Usuń tą opcję"
|
||||
add: "+ Dodaj opcję"
|
||||
destroy: "Usuń ankietę"
|
||||
destroy: "Usuń tę ankietę"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Wybierz reakcję"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "Photostream"
|
||||
no-photos: "Brak zdjęć"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "Wykres wpisów"
|
||||
toggle: "Przełącz widok"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "Hashtagi"
|
||||
count: "Wspomniany przez {} użytkowników"
|
||||
common/views/widgets/server.vue:
|
||||
title: "Informacje o serwerze"
|
||||
toggle: "Przełącz widok"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "Notatki"
|
||||
title: "Notatka"
|
||||
memo: "Napisz tutaj!"
|
||||
save: "Zapisz"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "ten wpis jest prywatny"
|
||||
deleted: "ten wpis został usunięty"
|
||||
media-count: "{}zawartości multimedialnej"
|
||||
poll: "Ankiety"
|
||||
poll: "Ankieta"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "Menedżer zadań"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -535,7 +547,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
dark: "Sprowadź ciemność"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
home: "Strona główna"
|
||||
deck: "デッキ"
|
||||
deck: "Talia"
|
||||
messaging: "Wiadomości"
|
||||
game: "Gra"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "Pop-out"
|
||||
close: "Zamknij"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "Tylko wpisy z zawartością multimedialną"
|
||||
is-media-view: "Widok multimediów"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "Udostępniono przez {}"
|
||||
private: "ten wpis jest prywatny"
|
||||
deleted: "ten wpis został usunięty"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "O Misskey"
|
||||
gotit: "Rozumiem!"
|
||||
|
@ -54,21 +54,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "結果を見る"
|
||||
voted: "投票済み"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -372,7 +384,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
|
@ -54,21 +54,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "結果を見る"
|
||||
voted: "投票済み"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -372,7 +384,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
|
@ -54,21 +54,23 @@ common:
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "メモ"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "投票"
|
||||
polls: "アンケート"
|
||||
post-form: "投稿フォーム"
|
||||
messaging: "メッセージ"
|
||||
server: "サーバー情報"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
deck:
|
||||
widgets: "ウィジェット"
|
||||
home: "ホーム"
|
||||
@ -78,9 +80,13 @@ common:
|
||||
list: "リスト"
|
||||
swap-left: "左に移動"
|
||||
swap-right: "右に移動"
|
||||
swap-up: "上に移動"
|
||||
swap-down: "下に移動"
|
||||
remove: "カラムを削除"
|
||||
add-column: "カラムを追加"
|
||||
rename: "名前を変更"
|
||||
stack-left: "左に重ねる"
|
||||
pop-right: "右に出す"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
@ -146,11 +152,11 @@ common/views/components/poll.vue:
|
||||
show-result: "結果を見る"
|
||||
voted: "投票済み"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "投票には、選択肢が最低2つ必要です"
|
||||
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
|
||||
choice-n: "選択肢{}"
|
||||
remove: "この選択肢を削除"
|
||||
add: "+選択肢を追加"
|
||||
destroy: "投票を破棄"
|
||||
destroy: "アンケートを破棄"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
common/views/components/signin.vue:
|
||||
@ -216,11 +222,17 @@ common/views/widgets/donation.vue:
|
||||
common/views/widgets/photo-stream.vue:
|
||||
title: "フォトストリーム"
|
||||
no-photos: "写真はありません"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿チャート"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "ハッシュタグ"
|
||||
count: "{}人が投稿"
|
||||
common/views/widgets/server.vue:
|
||||
title: "サーバー情報"
|
||||
toggle: "表示を切り替え"
|
||||
common/views/widgets/memo.vue:
|
||||
title: "メモ"
|
||||
title: "付箋"
|
||||
memo: "ここに書いて!"
|
||||
save: "保存"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -372,7 +384,7 @@ desktop/views/components/post-form.vue:
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
attach-cancel: "添付取り消し"
|
||||
insert-a-kao: "v(‘ω’)v"
|
||||
create-poll: "投票を作成"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -515,7 +527,7 @@ desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
desktop/views/components/taskmanager.vue:
|
||||
title: "タスクマネージャ"
|
||||
desktop/views/components/timeline.vue:
|
||||
@ -565,6 +577,13 @@ desktop/views/components/users-list-item.vue:
|
||||
desktop/views/components/window.vue:
|
||||
popout: "ポップアウト"
|
||||
close: "閉じる"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
desktop/views/pages/deck/deck.note.vue:
|
||||
reposted-by: "{}がRenote"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -628,7 +647,7 @@ desktop/views/widgets/notifications.vue:
|
||||
title: "通知"
|
||||
settings: "通知の設定"
|
||||
desktop/views/widgets/polls.vue:
|
||||
title: "投票"
|
||||
title: "アンケート"
|
||||
refresh: "他を見る"
|
||||
nothing: "ありません!"
|
||||
desktop/views/widgets/post-form.vue:
|
||||
@ -727,7 +746,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "投票"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
load-more: "もっと"
|
||||
|
125
package.json
125
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "2.29.1",
|
||||
"clientVersion": "1.0.6218",
|
||||
"version": "3.1.0",
|
||||
"clientVersion": "1.0.6526",
|
||||
"codename": "nighthike",
|
||||
"main": "./built/index.js",
|
||||
"private": true,
|
||||
@ -29,12 +29,69 @@
|
||||
"@fortawesome/fontawesome-free-solid": "5.0.2",
|
||||
"@koa/cors": "2.2.1",
|
||||
"@prezzemolo/rap": "0.1.2",
|
||||
"autwh": "0.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"cafy": "8.0.0",
|
||||
"chalk": "2.4.1",
|
||||
"crc-32": "1.2.0",
|
||||
"debug": "3.1.0",
|
||||
"deepcopy": "0.6.3",
|
||||
"diskusage": "0.2.4",
|
||||
"elasticsearch": "15.0.0",
|
||||
"emojilib": "2.2.12",
|
||||
"escape-regexp": "0.0.1",
|
||||
"file-type": "8.0.0",
|
||||
"gm": "1.23.1",
|
||||
"http-signature": "1.2.0",
|
||||
"is-root": "2.0.0",
|
||||
"is-url": "1.2.4",
|
||||
"js-yaml": "3.11.0",
|
||||
"jsdom": "11.11.0",
|
||||
"koa": "2.5.1",
|
||||
"koa-bodyparser": "4.2.1",
|
||||
"koa-compress": "3.0.0",
|
||||
"koa-favicon": "2.0.1",
|
||||
"koa-json-body": "5.3.0",
|
||||
"koa-logger": "3.2.0",
|
||||
"koa-mount": "3.0.0",
|
||||
"koa-multer": "1.0.2",
|
||||
"koa-router": "7.4.0",
|
||||
"koa-send": "4.1.3",
|
||||
"koa-slow": "2.1.0",
|
||||
"koa-views": "6.1.4",
|
||||
"kue": "0.11.6",
|
||||
"mongodb": "3.0.10",
|
||||
"monk": "6.0.6",
|
||||
"ms": "2.1.1",
|
||||
"nopt": "4.0.1",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "5.0.0",
|
||||
"prominence": "0.2.0",
|
||||
"promise-sequential": "1.1.1",
|
||||
"punycode": "2.1.1",
|
||||
"qrcode": "1.2.0",
|
||||
"ratelimiter": "3.0.3",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "3.2.2",
|
||||
"redis": "2.8.0",
|
||||
"request": "2.87.0",
|
||||
"request-promise-native": "1.0.5",
|
||||
"rndstr": "1.0.0",
|
||||
"speakeasy": "2.0.0",
|
||||
"summaly": "2.0.6",
|
||||
"tcp-port-used": "0.1.2",
|
||||
"tmp": "0.0.33",
|
||||
"uuid": "3.2.1",
|
||||
"web-push": "3.3.1",
|
||||
"webfinger.js": "2.6.6",
|
||||
"websocket": "1.0.26",
|
||||
"ws": "5.2.0",
|
||||
"xev": "2.0.1",
|
||||
"@prezzemolo/zip": "0.0.3",
|
||||
"@types/bcryptjs": "2.4.1",
|
||||
"@types/debug": "0.0.30",
|
||||
"@types/deep-equal": "1.0.1",
|
||||
"@types/elasticsearch": "5.0.23",
|
||||
"@types/eventemitter3": "2.0.2",
|
||||
"@types/gm": "1.18.0",
|
||||
"@types/gulp": "3.8.36",
|
||||
"@types/gulp-htmlmin": "1.3.32",
|
||||
@ -63,7 +120,6 @@
|
||||
"@types/mkdirp": "0.5.2",
|
||||
"@types/mocha": "5.2.0",
|
||||
"@types/mongodb": "3.0.18",
|
||||
"@types/monk": "6.0.0",
|
||||
"@types/ms": "0.7.30",
|
||||
"@types/node": "10.1.2",
|
||||
"@types/nopt": "3.0.29",
|
||||
@ -86,30 +142,17 @@
|
||||
"@types/ws": "5.1.1",
|
||||
"animejs": "2.2.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bootstrap-vue": "2.0.0-rc.6",
|
||||
"cafy": "8.0.0",
|
||||
"chalk": "2.4.1",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "0.28.11",
|
||||
"debug": "3.1.0",
|
||||
"deep-equal": "1.0.1",
|
||||
"deepcopy": "0.6.3",
|
||||
"diskusage": "0.2.4",
|
||||
"dompurify": "1.0.4",
|
||||
"elasticsearch": "15.0.0",
|
||||
"element-ui": "2.3.9",
|
||||
"emojilib": "2.2.12",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint": "4.19.1",
|
||||
"eslint-plugin-vue": "4.5.0",
|
||||
"eventemitter3": "3.1.0",
|
||||
"exif-js": "2.3.0",
|
||||
"file-loader": "1.1.11",
|
||||
"file-type": "8.0.0",
|
||||
"fuckadblock": "3.2.1",
|
||||
"gm": "1.23.1",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
"gulp-htmlmin": "4.0.0",
|
||||
@ -127,71 +170,32 @@
|
||||
"hard-source-webpack-plugin": "0.6.10",
|
||||
"highlight.js": "9.12.0",
|
||||
"html-minifier": "3.5.16",
|
||||
"http-signature": "1.2.0",
|
||||
"inquirer": "5.2.0",
|
||||
"is-root": "2.0.0",
|
||||
"is-url": "1.2.4",
|
||||
"js-yaml": "3.11.0",
|
||||
"jsdom": "11.11.0",
|
||||
"koa": "2.5.1",
|
||||
"koa-bodyparser": "4.2.1",
|
||||
"koa-compress": "3.0.0",
|
||||
"koa-favicon": "2.0.1",
|
||||
"koa-json-body": "5.3.0",
|
||||
"koa-logger": "3.2.0",
|
||||
"koa-mount": "3.0.0",
|
||||
"koa-multer": "1.0.2",
|
||||
"koa-router": "7.4.0",
|
||||
"koa-send": "4.1.3",
|
||||
"koa-slow": "2.1.0",
|
||||
"koa-views": "6.1.4",
|
||||
"kue": "0.11.6",
|
||||
"license-checker": "20.0.0",
|
||||
"loader-utils": "1.1.0",
|
||||
"mecab-async": "0.1.2",
|
||||
"mkdirp": "0.5.1",
|
||||
"mocha": "5.2.0",
|
||||
"moji": "0.5.1",
|
||||
"mongodb": "3.0.8",
|
||||
"monk": "6.0.6",
|
||||
"ms": "2.1.1",
|
||||
"nan": "2.10.0",
|
||||
"node-sass": "4.9.0",
|
||||
"node-sass-json-importer": "3.2.0",
|
||||
"nopt": "4.0.1",
|
||||
"nprogress": "0.2.0",
|
||||
"object-assign-deep": "0.4.0",
|
||||
"on-build-webpack": "0.1.0",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "5.0.0",
|
||||
"progress-bar-webpack-plugin": "1.11.0",
|
||||
"prominence": "0.2.0",
|
||||
"promise-sequential": "1.1.1",
|
||||
"pug": "2.0.3",
|
||||
"punycode": "2.1.1",
|
||||
"qrcode": "1.2.0",
|
||||
"ratelimiter": "3.0.3",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "3.2.2",
|
||||
"redis": "2.8.0",
|
||||
"request": "2.87.0",
|
||||
"request-promise-native": "1.0.5",
|
||||
"rimraf": "2.6.2",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"sass-loader": "7.0.1",
|
||||
"seedrandom": "2.4.3",
|
||||
"single-line-log": "1.1.2",
|
||||
"speakeasy": "2.0.0",
|
||||
"style-loader": "0.21.0",
|
||||
"stylus": "0.54.5",
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.0.6",
|
||||
"swagger-jsdoc": "1.9.7",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"tcp-port-used": "0.1.2",
|
||||
"textarea-caret": "3.1.0",
|
||||
"tmp": "0.0.33",
|
||||
"ts-loader": "4.3.0",
|
||||
"ts-node": "6.0.4",
|
||||
"tslint": "5.10.0",
|
||||
@ -199,25 +203,18 @@
|
||||
"typescript-eslint-parser": "15.0.0",
|
||||
"uglify-es": "3.3.9",
|
||||
"url-loader": "1.0.1",
|
||||
"uuid": "3.2.1",
|
||||
"v-animate-css": "0.0.2",
|
||||
"vue": "2.5.16",
|
||||
"vue-cropperjs": "2.2.0",
|
||||
"vue-js-modal": "1.3.13",
|
||||
"vue-json-tree-view": "2.1.4",
|
||||
"vue-loader": "15.2.1",
|
||||
"vue-material": "^1.0.0-beta-10.2",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-template-compiler": "2.5.16",
|
||||
"vuedraggable": "2.16.0",
|
||||
"vuex": "3.0.1",
|
||||
"vuex-persistedstate": "^2.5.4",
|
||||
"web-push": "3.3.1",
|
||||
"webfinger.js": "2.6.6",
|
||||
"webpack": "4.9.1",
|
||||
"webpack-cli": "2.1.4",
|
||||
"websocket": "1.0.26",
|
||||
"ws": "5.2.0",
|
||||
"xev": "2.0.0"
|
||||
"webpack-cli": "2.1.4"
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,6 @@ html
|
||||
cursor progress !important
|
||||
|
||||
body
|
||||
// for md
|
||||
font-size 16px !important
|
||||
line-height initial !important
|
||||
letter-spacing initial !important
|
||||
|
||||
overflow-wrap break-word
|
||||
|
||||
#error
|
||||
|
@ -42,7 +42,7 @@ html
|
||||
| JavaScriptを有効にしてください
|
||||
br
|
||||
| Please turn on your JavaScript
|
||||
div#ini: p
|
||||
span .
|
||||
span .
|
||||
span .
|
||||
div#ini.
|
||||
<svg viewBox="0 0 50 50">
|
||||
<path fill=#{themeColor} d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" />
|
||||
</svg>
|
||||
|
@ -3,15 +3,15 @@ import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Server stream connection
|
||||
* Notes stats stream connection
|
||||
*/
|
||||
export class ServerStream extends Stream {
|
||||
export class NotesStatsStream extends Stream {
|
||||
constructor(os: MiOS) {
|
||||
super(os, 'server');
|
||||
super(os, 'notes-stats');
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerStreamManager extends StreamManager<ServerStream> {
|
||||
export class NotesStatsStreamManager extends StreamManager<NotesStatsStream> {
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS) {
|
||||
@ -22,7 +22,7 @@ export class ServerStreamManager extends StreamManager<ServerStream> {
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new ServerStream(this.os);
|
||||
this.connection = new NotesStatsStream(this.os);
|
||||
}
|
||||
|
||||
return this.connection;
|
30
src/client/app/common/scripts/streaming/server-stats.ts
Normal file
30
src/client/app/common/scripts/streaming/server-stats.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Server stats stream connection
|
||||
*/
|
||||
export class ServerStatsStream extends Stream {
|
||||
constructor(os: MiOS) {
|
||||
super(os, 'server-stats');
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerStatsStreamManager extends StreamManager<ServerStatsStream> {
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS) {
|
||||
super();
|
||||
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new ServerStatsStream(this.os);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -13,9 +13,6 @@
|
||||
|
||||
.a
|
||||
display block
|
||||
position fixed
|
||||
top 0
|
||||
right 0
|
||||
|
||||
> svg
|
||||
display block
|
||||
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
||||
|
||||
import analogClock from './analog-clock.vue';
|
||||
import menu from './menu.vue';
|
||||
import noteHeader from './note-header.vue';
|
||||
import signin from './signin.vue';
|
||||
import signup from './signup.vue';
|
||||
import forkit from './forkit.vue';
|
||||
@ -28,9 +29,18 @@ import fileTypeIcon from './file-type-icon.vue';
|
||||
import Switch from './switch.vue';
|
||||
import Othello from './othello.vue';
|
||||
import welcomeTimeline from './welcome-timeline.vue';
|
||||
import uiInput from './ui/input.vue';
|
||||
import uiButton from './ui/button.vue';
|
||||
import uiCard from './ui/card.vue';
|
||||
import uiForm from './ui/form.vue';
|
||||
import uiTextarea from './ui/textarea.vue';
|
||||
import uiSwitch from './ui/switch.vue';
|
||||
import uiRadio from './ui/radio.vue';
|
||||
import uiSelect from './ui/select.vue';
|
||||
|
||||
Vue.component('mk-analog-clock', analogClock);
|
||||
Vue.component('mk-menu', menu);
|
||||
Vue.component('mk-note-header', noteHeader);
|
||||
Vue.component('mk-signin', signin);
|
||||
Vue.component('mk-signup', signup);
|
||||
Vue.component('mk-forkit', forkit);
|
||||
@ -57,3 +67,11 @@ Vue.component('mk-file-type-icon', fileTypeIcon);
|
||||
Vue.component('mk-switch', Switch);
|
||||
Vue.component('mk-othello', Othello);
|
||||
Vue.component('mk-welcome-timeline', welcomeTimeline);
|
||||
Vue.component('ui-input', uiInput);
|
||||
Vue.component('ui-button', uiButton);
|
||||
Vue.component('ui-card', uiCard);
|
||||
Vue.component('ui-form', uiForm);
|
||||
Vue.component('ui-textarea', uiTextarea);
|
||||
Vue.component('ui-switch', uiSwitch);
|
||||
Vue.component('ui-radio', uiRadio);
|
||||
Vue.component('ui-select', uiSelect);
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="mk-menu">
|
||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||
<div class="popover" :class="{ compact }" ref="popover">
|
||||
<div class="popover" :class="{ hukidasi }" ref="popover">
|
||||
<template v-for="item in items">
|
||||
<div v-if="item == null"></div>
|
||||
<button v-else @click="clicked(item.onClick)" v-html="item.content"></button>
|
||||
<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>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@ -15,7 +15,25 @@ import Vue from 'vue';
|
||||
import * as anime from 'animejs';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['source', 'compact', 'items'],
|
||||
props: {
|
||||
source: {
|
||||
required: true
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hukidasi: !this.compact
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const popover = this.$refs.popover as any;
|
||||
@ -24,18 +42,34 @@ export default Vue.extend({
|
||||
const width = popover.offsetWidth;
|
||||
const height = popover.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
if (this.compact) {
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
|
||||
popover.style.left = (x - (width / 2)) + 'px';
|
||||
popover.style.top = (y - (height / 2)) + 'px';
|
||||
left = (x - (width / 2));
|
||||
top = (y - (height / 2));
|
||||
} else {
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
||||
popover.style.left = (x - (width / 2)) + 'px';
|
||||
popover.style.top = y + 'px';
|
||||
left = (x - (width / 2));
|
||||
top = y;
|
||||
}
|
||||
|
||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - width + window.pageXOffset;
|
||||
this.hukidasi = false;
|
||||
}
|
||||
|
||||
if (top + height - window.pageYOffset > window.innerHeight) {
|
||||
top = window.innerHeight - height + window.pageYOffset;
|
||||
this.hukidasi = false;
|
||||
}
|
||||
|
||||
popover.style.left = left + 'px';
|
||||
popover.style.top = top + 'px';
|
||||
|
||||
anime({
|
||||
targets: this.$refs.backdrop,
|
||||
opacity: 1,
|
||||
@ -113,14 +147,18 @@ $border-color = rgba(27, 31, 35, 0.15)
|
||||
|
||||
$balloon-size = 16px
|
||||
|
||||
&:not(.compact)
|
||||
&.hukidasi
|
||||
margin-top $balloon-size
|
||||
transform-origin center -($balloon-size)
|
||||
|
||||
&:before
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
position absolute
|
||||
pointer-events none
|
||||
|
||||
&:before
|
||||
top -($balloon-size * 2)
|
||||
left s('calc(50% - %s)', $balloon-size)
|
||||
border-top solid $balloon-size transparent
|
||||
@ -129,9 +167,6 @@ $border-color = rgba(27, 31, 35, 0.15)
|
||||
border-bottom solid $balloon-size $border-color
|
||||
|
||||
&: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
|
||||
|
117
src/client/app/common/views/components/note-header.vue
Normal file
117
src/client/app/common/views/components/note-header.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<header class="bvonvjxbwzaiskogyhbwgyxvcgserpmu">
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
|
||||
<router-link class="name" :to="note.user | userPage" v-user-preview="note.user.id">{{ note.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="note.user.isAdmin">admin</span>
|
||||
<span class="is-bot" v-if="note.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<div class="info">
|
||||
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
mini: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark)
|
||||
display flex
|
||||
align-items baseline
|
||||
white-space nowrap
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
margin-right 8px
|
||||
width 20px
|
||||
height 20px
|
||||
border-radius 100%
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 .5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #627079
|
||||
font-size 1em
|
||||
font-weight bold
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 .5em 0 0
|
||||
padding 1px 6px
|
||||
font-size 80%
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
margin 0 .5em 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color isDark ? #606984 : #ccc
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #c0c0c0
|
||||
|
||||
> .mobile
|
||||
margin-right 8px
|
||||
|
||||
> .app
|
||||
margin-right 8px
|
||||
padding-right 8px
|
||||
border-right solid 1px isDark ? #1c2023 : #eaeaea
|
||||
|
||||
> .visibility
|
||||
margin-left 8px
|
||||
|
||||
.bvonvjxbwzaiskogyhbwgyxvcgserpmu[data-darkmode]
|
||||
root(true)
|
||||
|
||||
.bvonvjxbwzaiskogyhbwgyxvcgserpmu:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
</style>
|
@ -40,6 +40,17 @@ export default Vue.component('mk-note-html', {
|
||||
ast = this.ast;
|
||||
}
|
||||
|
||||
if (ast.filter(x => x.type != 'hashtag').length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (ast[ast.length - 1] && (
|
||||
ast[ast.length - 1].type == 'hashtag' ||
|
||||
(ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == ' ') ||
|
||||
(ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == '\n'))) {
|
||||
ast.pop();
|
||||
}
|
||||
|
||||
// Parse ast to DOM
|
||||
const els = flatten(ast.map(token => {
|
||||
switch (token.type) {
|
||||
@ -92,7 +103,7 @@ export default Vue.component('mk-note-html', {
|
||||
case 'hashtag':
|
||||
return createElement('a', {
|
||||
attrs: {
|
||||
href: `${url}/search?q=${token.content}`,
|
||||
href: `${url}/tags/${token.hashtag}`,
|
||||
target: '_blank'
|
||||
}
|
||||
}, token.content);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mk-note-menu" style="position:initial">
|
||||
<mk-menu ref="menu" :source="source" :compact="compact" :items="items" @closed="$destroy"/>
|
||||
<div style="position:initial">
|
||||
<mk-menu :source="source" :compact="compact" :items="items" @closed="closed"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -13,23 +13,27 @@ export default Vue.extend({
|
||||
items() {
|
||||
const items = [];
|
||||
items.push({
|
||||
content: '%i18n:@favorite%',
|
||||
onClick: this.favorite
|
||||
icon: '%fa:star%',
|
||||
text: '%i18n:@favorite%',
|
||||
action: this.favorite
|
||||
});
|
||||
if (this.note.userId == this.$store.state.i.id) {
|
||||
items.push({
|
||||
content: '%i18n:@pin%',
|
||||
onClick: this.pin
|
||||
icon: '%fa:thumbtack%',
|
||||
text: '%i18n:@pin%',
|
||||
action: this.pin
|
||||
});
|
||||
items.push({
|
||||
content: '%i18n:@delete%',
|
||||
onClick: this.del
|
||||
icon: '%fa:trash-alt R%',
|
||||
text: '%i18n:@delete%',
|
||||
action: this.del
|
||||
});
|
||||
}
|
||||
if (this.note.uri) {
|
||||
items.push({
|
||||
content: '%i18n:@remote%',
|
||||
onClick: () => {
|
||||
icon: '%fa:external-link-square-alt%',
|
||||
text: '%i18n:@remote%',
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
});
|
||||
@ -63,8 +67,10 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$refs.menu.close();
|
||||
closed() {
|
||||
this.$nextTick(() => {
|
||||
this.$destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,24 +1,33 @@
|
||||
<template>
|
||||
<form class="mk-signin" :class="{ signing }" @submit.prevent="onSubmit">
|
||||
<label class="user-name">
|
||||
<input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="%i18n:@username%" autofocus required @change="onUsernameChange"/>%fa:at%
|
||||
</label>
|
||||
<label class="password">
|
||||
<input v-model="password" type="password" placeholder="%i18n:@password%" required/>%fa:lock%
|
||||
</label>
|
||||
<label class="token" v-if="user && user.twoFactorEnabled">
|
||||
<input v-model="token" type="number" placeholder="%i18n:@token%" required/>%fa:lock%
|
||||
</label>
|
||||
<button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</button>
|
||||
もしくは <a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a>
|
||||
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
|
||||
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange">
|
||||
<span>%i18n:@username%</span>
|
||||
<span slot="prefix">@</span>
|
||||
<span slot="suffix">@{{ host }}</span>
|
||||
</ui-input>
|
||||
<ui-input v-model="password" type="password" required>
|
||||
<span>%i18n:@password%</span>
|
||||
<span slot="prefix">%fa:lock%</span>
|
||||
</ui-input>
|
||||
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
|
||||
<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
|
||||
<p style="margin: 8px 0;">または<a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a></p>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { apiUrl } from '../../../config';
|
||||
import { apiUrl, host } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
withAvatar: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
signing: false,
|
||||
@ -27,6 +36,7 @@ export default Vue.extend({
|
||||
password: '',
|
||||
token: '',
|
||||
apiUrl,
|
||||
host
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -35,6 +45,8 @@ export default Vue.extend({
|
||||
username: this.username
|
||||
}).then(user => {
|
||||
this.user = user;
|
||||
}, () => {
|
||||
this.user = null;
|
||||
});
|
||||
},
|
||||
onSubmit() {
|
||||
@ -59,84 +71,19 @@ export default Vue.extend({
|
||||
@import '~const.styl'
|
||||
|
||||
.mk-signin
|
||||
color #555
|
||||
|
||||
&.signing
|
||||
&, *
|
||||
cursor wait !important
|
||||
|
||||
label
|
||||
display block
|
||||
margin 12px 0
|
||||
|
||||
[data-fa]
|
||||
display block
|
||||
pointer-events none
|
||||
position absolute
|
||||
bottom 0
|
||||
top 0
|
||||
left 0
|
||||
z-index 1
|
||||
margin auto
|
||||
padding 0 16px
|
||||
height 1em
|
||||
color #898786
|
||||
|
||||
input[type=text]
|
||||
input[type=password]
|
||||
input[type=number]
|
||||
user-select text
|
||||
display inline-block
|
||||
cursor auto
|
||||
padding 0 0 0 38px
|
||||
margin 0
|
||||
width 100%
|
||||
line-height 44px
|
||||
font-size 1em
|
||||
color rgba(#000, 0.7)
|
||||
background #fff
|
||||
outline none
|
||||
border solid 1px #eee
|
||||
border-radius 4px
|
||||
|
||||
&:hover
|
||||
background rgba(255, 255, 255, 0.7)
|
||||
border-color #ddd
|
||||
|
||||
& + i
|
||||
color #797776
|
||||
|
||||
&:focus
|
||||
background #fff
|
||||
border-color #ccc
|
||||
|
||||
& + i
|
||||
color #797776
|
||||
|
||||
[type=submit]
|
||||
cursor pointer
|
||||
padding 16px
|
||||
margin -6px 0 0 0
|
||||
width 100%
|
||||
font-size 1.2em
|
||||
color rgba(#000, 0.5)
|
||||
outline none
|
||||
border none
|
||||
border-radius 0
|
||||
background transparent
|
||||
transition all .5s ease
|
||||
|
||||
&:hover
|
||||
color $theme-color
|
||||
transition all .2s ease
|
||||
|
||||
&:focus
|
||||
color $theme-color
|
||||
transition all .2s ease
|
||||
|
||||
&:active
|
||||
color darken($theme-color, 30%)
|
||||
transition all .2s ease
|
||||
|
||||
&:disabled
|
||||
opacity 0.7
|
||||
> .avatar
|
||||
margin 16px auto 0 auto
|
||||
width 64px
|
||||
height 64px
|
||||
background #ddd
|
||||
background-position center
|
||||
background-size cover
|
||||
border-radius 100%
|
||||
|
||||
</style>
|
||||
|
@ -1,60 +1,58 @@
|
||||
<template>
|
||||
<form class="mk-signup" @submit.prevent="onSubmit" autocomplete="off">
|
||||
<label class="username">
|
||||
<p class="caption">%fa:at%%i18n:@username%</p>
|
||||
<input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required @input="onChangeUsername"/>
|
||||
<p class="profile-page-url-preview" v-if="shouldShowProfileUrl">{{ `${url}/@${username}` }}</p>
|
||||
<p class="info" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%%i18n:@checking%</p>
|
||||
<p class="info" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw%%i18n:@available%</p>
|
||||
<p class="info" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@unavailable%</p>
|
||||
<p class="info" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@error%</p>
|
||||
<p class="info" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@invalid-format%</p>
|
||||
<p class="info" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-short%</p>
|
||||
<p class="info" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@too-long%</p>
|
||||
</label>
|
||||
<label class="password">
|
||||
<p class="caption">%fa:lock%%i18n:@password%</p>
|
||||
<input v-model="password" type="password" placeholder="%i18n:@password-placeholder%" autocomplete="off" required @input="onChangePassword"/>
|
||||
<div class="meter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
|
||||
<div class="value" ref="passwordMetar"></div>
|
||||
<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
|
||||
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername">
|
||||
<span>%i18n:@username%</span>
|
||||
<span slot="prefix">@</span>
|
||||
<span slot="suffix">@{{ host }}</span>
|
||||
<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
|
||||
<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
|
||||
<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
|
||||
<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
|
||||
<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
|
||||
<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
|
||||
<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
|
||||
</ui-input>
|
||||
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true">
|
||||
<span>%i18n:@password%</span>
|
||||
<span slot="prefix">%fa:lock%</span>
|
||||
<div slot="text">
|
||||
<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
|
||||
</div>
|
||||
<p class="info" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@weak-password%</p>
|
||||
<p class="info" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw%%i18n:@normal-password%</p>
|
||||
<p class="info" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw%%i18n:@strong-password%</p>
|
||||
</label>
|
||||
<label class="retype-password">
|
||||
<p class="caption">%fa:lock%%i18n:@password%(%i18n:@retype%)</p>
|
||||
<input v-model="retypedPassword" type="password" placeholder="%i18n:@retype-placeholder%" autocomplete="off" required @input="onChangePasswordRetype"/>
|
||||
<p class="info" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw%%i18n:@password-matched%</p>
|
||||
<p class="info" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:@password-not-matched%</p>
|
||||
</label>
|
||||
<label class="recaptcha">
|
||||
<p class="caption"><template v-if="recaptchaed">%fa:toggle-on%</template><template v-if="!recaptchaed">%fa:toggle-off%</template>%i18n:@recaptcha%</p>
|
||||
<div class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" :data-sitekey="recaptchaSitekey"></div>
|
||||
</label>
|
||||
<label class="agree-tou">
|
||||
<input name="agree-tou" type="checkbox" autocomplete="off" required/>
|
||||
</ui-input>
|
||||
<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
|
||||
<span>%i18n:@password% (%i18n:@retype%)</span>
|
||||
<span slot="prefix">%fa:lock%</span>
|
||||
<div slot="text">
|
||||
<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
|
||||
<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
|
||||
</div>
|
||||
</ui-input>
|
||||
<div class="g-recaptcha" :data-sitekey="recaptchaSitekey" style="margin: 16px 0;"></div>
|
||||
<label class="agree-tou" style="display: block; margin: 16px 0;">
|
||||
<input name="agree-tou" type="checkbox" required/>
|
||||
<p><a :href="touUrl" target="_blank">利用規約</a>に同意する</p>
|
||||
</label>
|
||||
<button type="submit">%i18n:@create%</button>
|
||||
<ui-button type="submit">%i18n:@create%</ui-button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
const getPasswordStrength = require('syuilo-password-strength');
|
||||
import { url, docsUrl, lang, recaptchaSitekey } from '../../../config';
|
||||
import { host, url, docsUrl, lang, recaptchaSitekey } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
host,
|
||||
username: '',
|
||||
password: '',
|
||||
retypedPassword: '',
|
||||
url,
|
||||
touUrl: `${docsUrl}/${lang}/tou`,
|
||||
recaptchaSitekey,
|
||||
recaptchaed: false,
|
||||
usernameState: null,
|
||||
passwordStrength: '',
|
||||
passwordRetypeState: null
|
||||
@ -104,7 +102,6 @@ export default Vue.extend({
|
||||
|
||||
const strength = getPasswordStrength(this.password);
|
||||
this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
|
||||
(this.$refs.passwordMetar as any).style.width = `${strength * 100}%`;
|
||||
},
|
||||
onChangePasswordRetype() {
|
||||
if (this.retypedPassword == '') {
|
||||
@ -130,19 +127,9 @@ export default Vue.extend({
|
||||
alert('%i18n:@some-error%');
|
||||
|
||||
(window as any).grecaptcha.reset();
|
||||
this.recaptchaed = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
(window as any).onRecaptchaed = () => {
|
||||
this.recaptchaed = true;
|
||||
};
|
||||
|
||||
(window as any).onRecaptchaExpired = () => {
|
||||
this.recaptchaed = false;
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
const script = document.createElement('script');
|
||||
@ -158,100 +145,6 @@ export default Vue.extend({
|
||||
.mk-signup
|
||||
min-width 302px
|
||||
|
||||
label
|
||||
display block
|
||||
margin 0 0 16px 0
|
||||
|
||||
> .caption
|
||||
margin 0 0 4px 0
|
||||
color #828888
|
||||
font-size 0.95em
|
||||
|
||||
> [data-fa]
|
||||
margin-right 0.25em
|
||||
color #96adac
|
||||
|
||||
> .info
|
||||
display block
|
||||
margin 4px 0
|
||||
font-size 0.8em
|
||||
|
||||
> [data-fa]
|
||||
margin-right 0.3em
|
||||
|
||||
&.username
|
||||
.profile-page-url-preview
|
||||
display block
|
||||
margin 4px 8px 0 4px
|
||||
font-size 0.8em
|
||||
color #888
|
||||
|
||||
&:empty
|
||||
display none
|
||||
|
||||
&:not(:empty) + .info
|
||||
margin-top 0
|
||||
|
||||
&.password
|
||||
.meter
|
||||
display block
|
||||
margin-top 8px
|
||||
width 100%
|
||||
height 8px
|
||||
|
||||
&[data-strength='']
|
||||
display none
|
||||
|
||||
&[data-strength='low']
|
||||
> .value
|
||||
background #d73612
|
||||
|
||||
&[data-strength='medium']
|
||||
> .value
|
||||
background #d7ca12
|
||||
|
||||
&[data-strength='high']
|
||||
> .value
|
||||
background #61bb22
|
||||
|
||||
> .value
|
||||
display block
|
||||
width 0%
|
||||
height 100%
|
||||
background transparent
|
||||
border-radius 4px
|
||||
transition all 0.1s ease
|
||||
|
||||
[type=text], [type=password]
|
||||
user-select text
|
||||
display inline-block
|
||||
cursor auto
|
||||
padding 0 12px
|
||||
margin 0
|
||||
width 100%
|
||||
line-height 44px
|
||||
font-size 1em
|
||||
color #333 !important
|
||||
background #fff !important
|
||||
outline none
|
||||
border solid 1px rgba(#000, 0.1)
|
||||
border-radius 4px
|
||||
box-shadow 0 0 0 114514px #fff inset
|
||||
transition all .3s ease
|
||||
|
||||
&:hover
|
||||
border-color rgba(#000, 0.2)
|
||||
transition all .1s ease
|
||||
|
||||
&:focus
|
||||
color $theme-color !important
|
||||
border-color $theme-color
|
||||
box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%)
|
||||
transition all 0s ease
|
||||
|
||||
&:disabled
|
||||
opacity 0.5
|
||||
|
||||
.agree-tou
|
||||
padding 4px
|
||||
border-radius 4px
|
||||
@ -269,19 +162,4 @@ export default Vue.extend({
|
||||
display inline
|
||||
color #555
|
||||
|
||||
button
|
||||
margin 0
|
||||
padding 16px
|
||||
width 100%
|
||||
font-size 1em
|
||||
color #fff
|
||||
background $theme-color
|
||||
border-radius 3px
|
||||
|
||||
&:hover
|
||||
background lighten($theme-color, 5%)
|
||||
|
||||
&:active
|
||||
background darken($theme-color, 5%)
|
||||
|
||||
</style>
|
||||
|
@ -58,18 +58,21 @@ export default Vue.extend({
|
||||
},
|
||||
created() {
|
||||
if (this.mode == 'relative' || this.mode == 'detail') {
|
||||
this.tick();
|
||||
this.tickId = setInterval(this.tick, 5000);
|
||||
this.tickId = window.requestAnimationFrame(this.tick);
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.mode === 'relative' || this.mode === 'detail') {
|
||||
clearInterval(this.tickId);
|
||||
window.clearTimeout(this.tickId);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tick() {
|
||||
this.now = new Date();
|
||||
|
||||
this.tickId = setTimeout(() => {
|
||||
window.requestAnimationFrame(this.tick);
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
82
src/client/app/common/views/components/ui/button.vue
Normal file
82
src/client/app/common/views/components/ui/button.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="ui-button" :class="[styl]">
|
||||
<button :type="type" @click="$emit('click')">
|
||||
<slot></slot>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
styl: 'fill'
|
||||
};
|
||||
},
|
||||
inject: {
|
||||
isCardChild: { default: false }
|
||||
},
|
||||
created() {
|
||||
if (this.isCardChild) {
|
||||
this.styl = 'line';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark, fill)
|
||||
> button
|
||||
display block
|
||||
width 100%
|
||||
margin 0
|
||||
padding 0
|
||||
font-weight bold
|
||||
font-size 16px
|
||||
line-height 44px
|
||||
border none
|
||||
border-radius 6px
|
||||
outline none
|
||||
box-shadow none
|
||||
|
||||
if fill
|
||||
color $theme-color-foreground
|
||||
background $theme-color
|
||||
|
||||
&:hover
|
||||
background lighten($theme-color, 5%)
|
||||
|
||||
&:active
|
||||
background darken($theme-color, 5%)
|
||||
else
|
||||
color $theme-color
|
||||
background none
|
||||
|
||||
&:hover
|
||||
color darken($theme-color, 5%)
|
||||
|
||||
&:active
|
||||
background rgba($theme-color, 0.3)
|
||||
|
||||
.ui-button[data-darkmode]
|
||||
&.fill
|
||||
root(true, true)
|
||||
&:not(.fill)
|
||||
root(true, false)
|
||||
|
||||
.ui-button:not([data-darkmode])
|
||||
&.fill
|
||||
root(false, true)
|
||||
&:not(.fill)
|
||||
root(false, false)
|
||||
|
||||
</style>
|
46
src/client/app/common/views/components/ui/card.vue
Normal file
46
src/client/app/common/views/components/ui/card.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="ui-card">
|
||||
<header>
|
||||
<slot name="title"></slot>
|
||||
</header>
|
||||
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
provide() {
|
||||
return {
|
||||
isCardChild: true
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark)
|
||||
margin 16px
|
||||
padding 16px
|
||||
color isDark ? #fff : #000
|
||||
background isDark ? #282C37 : #fff
|
||||
box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
|
||||
|
||||
@media (min-width 500px)
|
||||
padding 32px
|
||||
|
||||
> header
|
||||
font-weight normal
|
||||
font-size 24px
|
||||
color isDark ? #fff : #444
|
||||
|
||||
.ui-card[data-darkmode]
|
||||
root(true)
|
||||
|
||||
.ui-card:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
</style>
|
30
src/client/app/common/views/components/ui/form.vue
Normal file
30
src/client/app/common/views/components/ui/form.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="ui-form">
|
||||
<fieldset :disabled="disabled">
|
||||
<slot></slot>
|
||||
</fieldset>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
.ui-form
|
||||
> fieldset
|
||||
margin 0
|
||||
padding 0
|
||||
border none
|
||||
|
||||
</style>
|
346
src/client/app/common/views/components/ui/input.vue
Normal file
346
src/client/app/common/views/components/ui/input.vue
Normal file
@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<div class="ui-input" :class="[{ focused, filled }, styl]">
|
||||
<div class="icon" ref="icon"><slot name="icon"></slot></div>
|
||||
<div class="input">
|
||||
<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
|
||||
<div class="value" ref="passwordMetar"></div>
|
||||
</div>
|
||||
<span class="label" ref="label"><slot></slot></span>
|
||||
<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
|
||||
<template v-if="type != 'file'">
|
||||
<input ref="input"
|
||||
:type="type"
|
||||
v-model="v"
|
||||
:required="required"
|
||||
:readonly="readonly"
|
||||
:pattern="pattern"
|
||||
:autocomplete="autocomplete"
|
||||
:spellcheck="spellcheck"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false">
|
||||
</template>
|
||||
<template v-else>
|
||||
<input ref="input"
|
||||
type="text"
|
||||
:value="placeholder"
|
||||
readonly
|
||||
@click="chooseFile">
|
||||
<input ref="file"
|
||||
type="file"
|
||||
:value="value"
|
||||
@change="onChangeFile">
|
||||
</template>
|
||||
<div class="suffix" ref="suffix"><slot name="suffix"></slot></div>
|
||||
</div>
|
||||
<div class="text"><slot name="text"></slot></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
const getPasswordStrength = require('syuilo-password-strength');
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: false
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
pattern: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
autocomplete: {
|
||||
required: false
|
||||
},
|
||||
spellcheck: {
|
||||
required: false
|
||||
},
|
||||
withPasswordMeter: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
v: this.value,
|
||||
focused: false,
|
||||
passwordStrength: '',
|
||||
styl: 'fill'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filled(): boolean {
|
||||
return this.v != '' && this.v != null;
|
||||
},
|
||||
placeholder(): string {
|
||||
if (this.type != 'file') return null;
|
||||
if (this.v == null) return null;
|
||||
|
||||
if (typeof this.v == 'string') return this.v;
|
||||
|
||||
if (Array.isArray(this.v)) {
|
||||
return this.v.map(file => file.name).join(', ');
|
||||
} else {
|
||||
return this.v.name;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v) {
|
||||
this.v = v;
|
||||
},
|
||||
v(v) {
|
||||
this.$emit('input', v);
|
||||
|
||||
if (this.withPasswordMeter) {
|
||||
if (v == '') {
|
||||
this.passwordStrength = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const strength = getPasswordStrength(v);
|
||||
this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
|
||||
(this.$refs.passwordMetar as any).style.width = `${strength * 100}%`;
|
||||
}
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
isCardChild: { default: false }
|
||||
},
|
||||
created() {
|
||||
if (this.isCardChild) {
|
||||
this.styl = 'line';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$refs.prefix) {
|
||||
this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
|
||||
if (this.$refs.prefix.offsetWidth) {
|
||||
this.$refs.input.style.paddingLeft = this.$refs.prefix.offsetWidth + 'px';
|
||||
}
|
||||
}
|
||||
if (this.$refs.suffix) {
|
||||
if (this.$refs.suffix.offsetWidth) {
|
||||
this.$refs.input.style.paddingRight = this.$refs.suffix.offsetWidth + 'px';
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
chooseFile() {
|
||||
this.$refs.file.click();
|
||||
},
|
||||
onChangeFile() {
|
||||
this.v = Array.from((this.$refs.file as any).files);
|
||||
this.$emit('input', this.v);
|
||||
this.$emit('change', this.v);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark, fill)
|
||||
margin 32px 0
|
||||
|
||||
> .icon
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 24px
|
||||
text-align center
|
||||
line-height 32px
|
||||
color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
|
||||
|
||||
&:not(:empty) + .input
|
||||
margin-left 28px
|
||||
|
||||
> .input
|
||||
|
||||
if !fill
|
||||
&:before
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
height 1px
|
||||
background isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42)
|
||||
|
||||
&:after
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
height 2px
|
||||
background $theme-color
|
||||
opacity 0
|
||||
transform scaleX(0.12)
|
||||
transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
||||
will-change border opacity transform
|
||||
|
||||
> .password-meter
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
height 100%
|
||||
border-radius 6px
|
||||
overflow hidden
|
||||
opacity 0.3
|
||||
|
||||
&[data-strength='']
|
||||
display none
|
||||
|
||||
&[data-strength='low']
|
||||
> .value
|
||||
background #d73612
|
||||
|
||||
&[data-strength='medium']
|
||||
> .value
|
||||
background #d7ca12
|
||||
|
||||
&[data-strength='high']
|
||||
> .value
|
||||
background #61bb22
|
||||
|
||||
> .value
|
||||
display block
|
||||
width 0%
|
||||
height 100%
|
||||
background transparent
|
||||
border-radius 6px
|
||||
transition all 0.1s ease
|
||||
|
||||
> .label
|
||||
position absolute
|
||||
z-index 1
|
||||
top fill ? 6px : 0
|
||||
left 0
|
||||
pointer-events none
|
||||
transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
|
||||
transition-duration 0.3s
|
||||
font-size 16px
|
||||
line-height 32px
|
||||
color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
|
||||
pointer-events none
|
||||
//will-change transform
|
||||
transform-origin top left
|
||||
transform scale(1)
|
||||
|
||||
> input
|
||||
display block
|
||||
width 100%
|
||||
margin 0
|
||||
padding 0
|
||||
font inherit
|
||||
font-weight fill ? bold : normal
|
||||
font-size 16px
|
||||
line-height 32px
|
||||
color isDark ? #fff : #000
|
||||
background transparent
|
||||
border none
|
||||
border-radius 0
|
||||
outline none
|
||||
box-shadow none
|
||||
|
||||
if fill
|
||||
padding 6px 12px
|
||||
background rgba(#000, 0.035)
|
||||
border-radius 6px
|
||||
|
||||
&[type='file']
|
||||
display none
|
||||
|
||||
> .prefix
|
||||
> .suffix
|
||||
display block
|
||||
position absolute
|
||||
z-index 1
|
||||
top 0
|
||||
font-size 16px
|
||||
line-height fill ? 44px : 32px
|
||||
color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
|
||||
pointer-events none
|
||||
|
||||
&:empty
|
||||
display none
|
||||
|
||||
> *
|
||||
display block
|
||||
min-width 16px
|
||||
|
||||
> .prefix
|
||||
left 0
|
||||
padding-right 4px
|
||||
|
||||
if fill
|
||||
padding-left 12px
|
||||
|
||||
> .suffix
|
||||
right 0
|
||||
padding-left 4px
|
||||
|
||||
if fill
|
||||
padding-right 12px
|
||||
|
||||
> .text
|
||||
margin 6px 0
|
||||
font-size 13px
|
||||
|
||||
*
|
||||
margin 0
|
||||
|
||||
&.focused
|
||||
> .input
|
||||
if fill
|
||||
background rgba(#000, 0.05)
|
||||
else
|
||||
&:after
|
||||
opacity 1
|
||||
transform scaleX(1)
|
||||
|
||||
> .label
|
||||
color $theme-color
|
||||
|
||||
&.focused
|
||||
&.filled
|
||||
> .input
|
||||
> .label
|
||||
top fill ? -24px : -17px
|
||||
left 0 !important
|
||||
transform scale(0.75)
|
||||
|
||||
.ui-input[data-darkmode]
|
||||
&.fill
|
||||
root(true, true)
|
||||
&:not(.fill)
|
||||
root(true, false)
|
||||
|
||||
.ui-input:not([data-darkmode])
|
||||
&.fill
|
||||
root(false, true)
|
||||
&:not(.fill)
|
||||
root(false, false)
|
||||
|
||||
</style>
|
120
src/client/app/common/views/components/ui/radio.vue
Normal file
120
src/client/app/common/views/components/ui/radio.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div
|
||||
class="ui-radio"
|
||||
:class="{ disabled, checked }"
|
||||
:aria-checked="checked"
|
||||
:aria-disabled="disabled"
|
||||
@click="toggle"
|
||||
>
|
||||
<input type="radio"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<span class="button">
|
||||
<span></span>
|
||||
</span>
|
||||
<span class="label"><slot></slot></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
model: {
|
||||
prop: 'model',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
checked(): boolean {
|
||||
return this.model === this.value;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$emit('change', this.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark)
|
||||
display inline-block
|
||||
margin 32px 32px 32px 0
|
||||
cursor pointer
|
||||
transition all 0.3s
|
||||
|
||||
> *
|
||||
user-select none
|
||||
|
||||
&.disabled
|
||||
opacity 0.6
|
||||
cursor not-allowed
|
||||
|
||||
&.checked
|
||||
> .button
|
||||
border-color $theme-color
|
||||
|
||||
&:after
|
||||
background-color $theme-color
|
||||
transform scale(1)
|
||||
opacity 1
|
||||
|
||||
> input
|
||||
position absolute
|
||||
width 0
|
||||
height 0
|
||||
opacity 0
|
||||
margin 0
|
||||
|
||||
> .button
|
||||
position absolute
|
||||
width 20px
|
||||
height 20px
|
||||
background none
|
||||
border solid 2px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
|
||||
border-radius 100%
|
||||
transition inherit
|
||||
|
||||
&:after
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
top 3px
|
||||
right 3px
|
||||
bottom 3px
|
||||
left 3px
|
||||
border-radius 100%
|
||||
opacity 0
|
||||
transform scale(0)
|
||||
transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
|
||||
|
||||
> .label
|
||||
margin-left 28px
|
||||
display block
|
||||
font-size 16px
|
||||
line-height 20px
|
||||
cursor pointer
|
||||
|
||||
.ui-radio[data-darkmode]
|
||||
root(true)
|
||||
|
||||
.ui-radio:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
</style>
|
215
src/client/app/common/views/components/ui/select.vue
Normal file
215
src/client/app/common/views/components/ui/select.vue
Normal file
@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<div class="ui-select" :class="[{ focused, filled }, styl]">
|
||||
<div class="icon" ref="icon"><slot name="icon"></slot></div>
|
||||
<div class="input" @click="focus">
|
||||
<span class="label" ref="label"><slot name="label"></slot></span>
|
||||
<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
|
||||
<select ref="input"
|
||||
:value="v"
|
||||
:required="required"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false">
|
||||
<slot></slot>
|
||||
</select>
|
||||
<div class="suffix"><slot name="suffix"></slot></div>
|
||||
</div>
|
||||
<div class="text"><slot name="text"></slot></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
v: this.value,
|
||||
focused: false,
|
||||
styl: 'fill'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filled(): boolean {
|
||||
return this.v != '' && this.v != null;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v) {
|
||||
this.v = v;
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
isCardChild: { default: false }
|
||||
},
|
||||
created() {
|
||||
if (this.isCardChild) {
|
||||
this.styl = 'line';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$refs.prefix) {
|
||||
this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark, fill)
|
||||
margin 32px 0
|
||||
|
||||
> .icon
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
width 24px
|
||||
text-align center
|
||||
line-height 32px
|
||||
color rgba(#000, 0.54)
|
||||
|
||||
&:not(:empty) + .input
|
||||
margin-left 28px
|
||||
|
||||
> .input
|
||||
display flex
|
||||
|
||||
if fill
|
||||
padding 6px 12px
|
||||
background rgba(#000, 0.035)
|
||||
border-radius 6px
|
||||
else
|
||||
&:before
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
height 1px
|
||||
background isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42)
|
||||
|
||||
&:after
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
height 2px
|
||||
background $theme-color
|
||||
opacity 0
|
||||
transform scaleX(0.12)
|
||||
transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
||||
will-change border opacity transform
|
||||
|
||||
> .label
|
||||
position absolute
|
||||
top fill ? 6px : 0
|
||||
left 0
|
||||
pointer-events none
|
||||
transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
|
||||
transition-duration 0.3s
|
||||
font-size 16px
|
||||
line-height 32px
|
||||
color rgba(#000, 0.54)
|
||||
pointer-events none
|
||||
//will-change transform
|
||||
transform-origin top left
|
||||
transform scale(1)
|
||||
|
||||
> select
|
||||
display block
|
||||
flex 1
|
||||
width 100%
|
||||
padding 0
|
||||
font inherit
|
||||
font-weight fill ? bold : normal
|
||||
font-size 16px
|
||||
height 32px
|
||||
color isDark ? #fff : #000
|
||||
background transparent
|
||||
border none
|
||||
border-radius 0
|
||||
outline none
|
||||
box-shadow none
|
||||
|
||||
*
|
||||
color #000
|
||||
|
||||
> .prefix
|
||||
> .suffix
|
||||
display block
|
||||
align-self center
|
||||
justify-self center
|
||||
font-size 16px
|
||||
line-height 32px
|
||||
color rgba(#000, 0.54)
|
||||
pointer-events none
|
||||
|
||||
> *
|
||||
display block
|
||||
min-width 16px
|
||||
|
||||
> .prefix
|
||||
padding-right 4px
|
||||
|
||||
> .suffix
|
||||
padding-left 4px
|
||||
|
||||
> .text
|
||||
margin 6px 0
|
||||
font-size 13px
|
||||
|
||||
*
|
||||
margin 0
|
||||
|
||||
&.focused
|
||||
> .input
|
||||
if fill
|
||||
background rgba(#000, 0.05)
|
||||
else
|
||||
&:after
|
||||
opacity 1
|
||||
transform scaleX(1)
|
||||
|
||||
> .label
|
||||
color $theme-color
|
||||
|
||||
&.focused
|
||||
&.filled
|
||||
> .input
|
||||
> .label
|
||||
top fill ? -24px : -17px
|
||||
left 0 !important
|
||||
transform scale(0.75)
|
||||
|
||||
.ui-select[data-darkmode]
|
||||
&.fill
|
||||
root(true, true)
|
||||
&:not(.fill)
|
||||
root(true, false)
|
||||
|
||||
.ui-select:not([data-darkmode])
|
||||
&.fill
|
||||
root(false, true)
|
||||
&:not(.fill)
|
||||
root(false, false)
|
||||
|
||||
</style>
|
135
src/client/app/common/views/components/ui/switch.vue
Normal file
135
src/client/app/common/views/components/ui/switch.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div
|
||||
class="ui-switch"
|
||||
:class="{ disabled, checked }"
|
||||
role="switch"
|
||||
:aria-checked="checked"
|
||||
:aria-disabled="disabled"
|
||||
@click="toggle"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
ref="input"
|
||||
:disabled="disabled"
|
||||
@keydown.enter="toggle"
|
||||
>
|
||||
<span class="button">
|
||||
<span></span>
|
||||
</span>
|
||||
<span class="label">
|
||||
<span :aria-hidden="!checked"><slot></slot></span>
|
||||
<p :aria-hidden="!checked">
|
||||
<slot name="text"></slot>
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
checked(): boolean {
|
||||
return this.value;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$emit('change', !this.checked);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark)
|
||||
display flex
|
||||
margin 32px 0
|
||||
cursor pointer
|
||||
transition all 0.3s
|
||||
|
||||
> *
|
||||
user-select none
|
||||
|
||||
&.disabled
|
||||
opacity 0.6
|
||||
cursor not-allowed
|
||||
|
||||
&.checked
|
||||
> .button
|
||||
background-color rgba($theme-color, 0.4)
|
||||
border-color rgba($theme-color, 0.4)
|
||||
|
||||
> *
|
||||
background-color $theme-color
|
||||
transform translateX(14px)
|
||||
|
||||
> input
|
||||
position absolute
|
||||
width 0
|
||||
height 0
|
||||
opacity 0
|
||||
margin 0
|
||||
|
||||
> .button
|
||||
display inline-block
|
||||
margin 3px 0 0 0
|
||||
width 34px
|
||||
height 14px
|
||||
background isDark ? rgba(#fff, 0.15) : rgba(#000, 0.25)
|
||||
outline none
|
||||
border-radius 14px
|
||||
transition inherit
|
||||
|
||||
> *
|
||||
position absolute
|
||||
top -3px
|
||||
left 0
|
||||
border-radius 100%
|
||||
transition background-color 0.3s, transform 0.3s
|
||||
width 20px
|
||||
height 20px
|
||||
background-color #fff
|
||||
box-shadow 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12)
|
||||
|
||||
> .label
|
||||
margin-left 8px
|
||||
display block
|
||||
font-size 16px
|
||||
cursor pointer
|
||||
transition inherit
|
||||
|
||||
> span
|
||||
display block
|
||||
line-height 20px
|
||||
color isDark ? #c4ccd2 : rgba(#000, 0.75)
|
||||
transition inherit
|
||||
|
||||
> p
|
||||
margin 0
|
||||
//font-size 90%
|
||||
color isDark ? #78858e : #9daab3
|
||||
|
||||
.ui-switch[data-darkmode]
|
||||
root(true)
|
||||
|
||||
.ui-switch:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
</style>
|
174
src/client/app/common/views/components/ui/textarea.vue
Normal file
174
src/client/app/common/views/components/ui/textarea.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="ui-textarea" :class="{ focused, filled }">
|
||||
<div class="input">
|
||||
<span class="label" ref="label"><slot></slot></span>
|
||||
<textarea ref="input"
|
||||
:value="value"
|
||||
:required="required"
|
||||
:readonly="readonly"
|
||||
:pattern="pattern"
|
||||
:autocomplete="autocomplete"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false">
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="text"><slot name="text"></slot></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
const getPasswordStrength = require('syuilo-password-strength');
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
pattern: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
autocomplete: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false,
|
||||
passwordStrength: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filled(): boolean {
|
||||
return this.value != '' && this.value != null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
root(isDark, fill)
|
||||
margin 42px 0 32px 0
|
||||
|
||||
> .input
|
||||
padding 12px
|
||||
|
||||
if fill
|
||||
background rgba(#000, 0.035)
|
||||
border-radius 6px
|
||||
else
|
||||
&:before
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
top 0
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
background none
|
||||
border solid 1px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42)
|
||||
border-radius 3px
|
||||
pointer-events none
|
||||
|
||||
&:after
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
top 0
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
background none
|
||||
border solid 2px $theme-color
|
||||
border-radius 3px
|
||||
opacity 0
|
||||
transition opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
||||
pointer-events none
|
||||
|
||||
> .label
|
||||
position absolute
|
||||
top 6px
|
||||
left 12px
|
||||
pointer-events none
|
||||
transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
|
||||
transition-duration 0.3s
|
||||
font-size 16px
|
||||
line-height 32px
|
||||
color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
|
||||
pointer-events none
|
||||
//will-change transform
|
||||
transform-origin top left
|
||||
transform scale(1)
|
||||
|
||||
> textarea
|
||||
display block
|
||||
width 100%
|
||||
min-height 100px
|
||||
padding 0
|
||||
font inherit
|
||||
font-weight fill ? bold : normal
|
||||
font-size 16px
|
||||
color isDark ? #fff : #000
|
||||
background transparent
|
||||
border none
|
||||
border-radius 0
|
||||
outline none
|
||||
box-shadow none
|
||||
|
||||
> .text
|
||||
margin 6px 0
|
||||
font-size 13px
|
||||
|
||||
*
|
||||
margin 0
|
||||
|
||||
&.focused
|
||||
> .input
|
||||
if fill
|
||||
background rgba(#000, 0.05)
|
||||
else
|
||||
&:after
|
||||
opacity 1
|
||||
|
||||
> .label
|
||||
color $theme-color
|
||||
|
||||
&.focused
|
||||
&.filled
|
||||
> .input
|
||||
> .label
|
||||
top -24px
|
||||
left 0 !important
|
||||
transform scale(0.75)
|
||||
|
||||
.ui-textarea[data-darkmode]
|
||||
&.fill
|
||||
root(true, true)
|
||||
&:not(.fill)
|
||||
root(true, false)
|
||||
|
||||
.ui-textarea:not([data-darkmode])
|
||||
&.fill
|
||||
root(false, true)
|
||||
&:not(.fill)
|
||||
root(false, false)
|
||||
|
||||
</style>
|
@ -203,6 +203,7 @@ root(isDark)
|
||||
justify-content center
|
||||
align-items center
|
||||
margin-right 10px
|
||||
width 16px
|
||||
|
||||
> *:last-child
|
||||
flex 1 1 auto
|
||||
|
@ -109,6 +109,9 @@ root(isDark)
|
||||
> .created-at
|
||||
color isDark ? #606984 : #c0c0c0
|
||||
|
||||
> .text
|
||||
text-align left
|
||||
|
||||
.mk-welcome-timeline[data-darkmode]
|
||||
root(true)
|
||||
|
||||
|
89
src/client/app/common/views/widgets/hashtags.chart.vue
Normal file
89
src/client/app/common/views/widgets/hashtags.chart.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" style="overflow:visible">
|
||||
<defs>
|
||||
<linearGradient :id="gradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
|
||||
<stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
|
||||
</linearGradient>
|
||||
<mask :id="maskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
|
||||
<polygon
|
||||
:points="polygonPoints"
|
||||
fill="#fff"
|
||||
fill-opacity="0.5"/>
|
||||
<polyline
|
||||
:points="polylinePoints"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="2"/>
|
||||
<circle
|
||||
:cx="headX"
|
||||
:cy="headY"
|
||||
r="3"
|
||||
fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-10" y="-10"
|
||||
:width="viewBoxX + 20" :height="viewBoxY + 20"
|
||||
:style="`stroke: none; fill: url(#${ gradientId }); mask: url(#${ maskId })`"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
src: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewBoxX: 50,
|
||||
viewBoxY: 30,
|
||||
gradientId: uuid(),
|
||||
maskId: uuid(),
|
||||
polylinePoints: '',
|
||||
polygonPoints: '',
|
||||
headX: null,
|
||||
headY: null,
|
||||
clock: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
src() {
|
||||
this.draw();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.draw();
|
||||
|
||||
// Vueが何故かWatchを発動させない場合があるので
|
||||
this.clock = setInterval(this.draw, 1000);
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.clock);
|
||||
},
|
||||
methods: {
|
||||
draw() {
|
||||
const stats = this.src.slice().reverse();
|
||||
const peak = Math.max.apply(null, stats) || 1;
|
||||
|
||||
const polylinePoints = stats.map((n, i) => [
|
||||
i * (this.viewBoxX / (stats.length - 1)),
|
||||
(1 - (n / peak)) * this.viewBoxY
|
||||
]);
|
||||
|
||||
this.polylinePoints = polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
|
||||
this.polygonPoints = `0,${ this.viewBoxY } ${ this.polylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
|
||||
|
||||
this.headX = polylinePoints[polylinePoints.length - 1][0];
|
||||
this.headY = polylinePoints[polylinePoints.length - 1][1];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
118
src/client/app/common/views/widgets/hashtags.vue
Normal file
118
src/client/app/common/views/widgets/hashtags.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="mkw-hashtags">
|
||||
<mk-widget-container :show-header="!props.compact">
|
||||
<template slot="header">%fa:hashtag%%i18n:@title%</template>
|
||||
|
||||
<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
|
||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
|
||||
<transition-group v-else tag="div" name="chart">
|
||||
<div v-for="stat in stats" :key="stat.tag">
|
||||
<div class="tag">
|
||||
<router-link :to="`/tags/${ stat.tag }`" :title="stat.tag">#{{ stat.tag }}</router-link>
|
||||
<p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p>
|
||||
</div>
|
||||
<x-chart class="chart" :src="stat.chart"/>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
</mk-widget-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import define from '../../../common/define-widget';
|
||||
import XChart from './hashtags.chart.vue';
|
||||
|
||||
export default define({
|
||||
name: 'hashtags',
|
||||
props: () => ({
|
||||
compact: false
|
||||
})
|
||||
}).extend({
|
||||
components: {
|
||||
XChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stats: [],
|
||||
fetching: true,
|
||||
clock: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.fetch();
|
||||
this.clock = setInterval(this.fetch, 1000 * 60);
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.clock);
|
||||
},
|
||||
methods: {
|
||||
func() {
|
||||
this.props.compact = !this.props.compact;
|
||||
this.save();
|
||||
},
|
||||
fetch() {
|
||||
(this as any).api('hashtags/trend').then(stats => {
|
||||
this.stats = stats;
|
||||
this.fetching = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
root(isDark)
|
||||
.mkw-hashtags--body
|
||||
> .fetching
|
||||
> .empty
|
||||
margin 0
|
||||
padding 16px
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
margin-right 4px
|
||||
|
||||
> div
|
||||
.chart-move
|
||||
transition transform 1s ease
|
||||
|
||||
> div
|
||||
display flex
|
||||
align-items center
|
||||
padding 14px 16px
|
||||
|
||||
&:not(:last-child)
|
||||
border-bottom solid 1px isDark ? #393f4f : #eee
|
||||
|
||||
> .tag
|
||||
flex 1
|
||||
overflow hidden
|
||||
font-size 14px
|
||||
color isDark ? #9baec8 : #65727b
|
||||
|
||||
> a
|
||||
display block
|
||||
width 100%
|
||||
white-space nowrap
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color inherit
|
||||
|
||||
> p
|
||||
margin 0
|
||||
font-size 75%
|
||||
opacity 0.7
|
||||
|
||||
> .chart
|
||||
height 30px
|
||||
|
||||
.mkw-hashtags[data-darkmode]
|
||||
root(true)
|
||||
|
||||
.mkw-hashtags:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
</style>
|
@ -4,6 +4,7 @@ import wAnalogClock from './analog-clock.vue';
|
||||
import wVersion from './version.vue';
|
||||
import wRss from './rss.vue';
|
||||
import wServer from './server.vue';
|
||||
import wPostsMonitor from './posts-monitor.vue';
|
||||
import wMemo from './memo.vue';
|
||||
import wBroadcast from './broadcast.vue';
|
||||
import wCalendar from './calendar.vue';
|
||||
@ -12,6 +13,7 @@ import wSlideshow from './slideshow.vue';
|
||||
import wTips from './tips.vue';
|
||||
import wDonation from './donation.vue';
|
||||
import wNav from './nav.vue';
|
||||
import wHashtags from './hashtags.vue';
|
||||
|
||||
Vue.component('mkw-analog-clock', wAnalogClock);
|
||||
Vue.component('mkw-nav', wNav);
|
||||
@ -22,6 +24,8 @@ Vue.component('mkw-tips', wTips);
|
||||
Vue.component('mkw-donation', wDonation);
|
||||
Vue.component('mkw-broadcast', wBroadcast);
|
||||
Vue.component('mkw-server', wServer);
|
||||
Vue.component('mkw-posts-monitor', wPostsMonitor);
|
||||
Vue.component('mkw-memo', wMemo);
|
||||
Vue.component('mkw-rss', wRss);
|
||||
Vue.component('mkw-version', wVersion);
|
||||
Vue.component('mkw-hashtags', wHashtags);
|
||||
|
211
src/client/app/common/views/widgets/posts-monitor.vue
Normal file
211
src/client/app/common/views/widgets/posts-monitor.vue
Normal file
@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<div class="mkw-posts-monitor">
|
||||
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
|
||||
<template slot="header">%fa:chart-line%%i18n:@title%</template>
|
||||
<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
|
||||
|
||||
<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }" :data-darkmode="$store.state.device.darkmode">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">
|
||||
<defs>
|
||||
<linearGradient :id="localGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
|
||||
<stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
|
||||
</linearGradient>
|
||||
<mask :id="localMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
|
||||
<polygon
|
||||
:points="localPolygonPoints"
|
||||
fill="#fff"
|
||||
fill-opacity="0.5"/>
|
||||
<polyline
|
||||
:points="localPolylinePoints"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1"/>
|
||||
<circle
|
||||
:cx="localHeadX"
|
||||
:cy="localHeadY"
|
||||
r="1.5"
|
||||
fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-2" y="-2"
|
||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||
:style="`stroke: none; fill: url(#${ localGradientId }); mask: url(#${ localMaskId })`"/>
|
||||
<text x="1" y="5">Local</text>
|
||||
</svg>
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 1">
|
||||
<defs>
|
||||
<linearGradient :id="fediGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
|
||||
<stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
|
||||
</linearGradient>
|
||||
<mask :id="fediMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
|
||||
<polygon
|
||||
:points="fediPolygonPoints"
|
||||
fill="#fff"
|
||||
fill-opacity="0.5"/>
|
||||
<polyline
|
||||
:points="fediPolylinePoints"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1"/>
|
||||
<circle
|
||||
:cx="fediHeadX"
|
||||
:cy="fediHeadY"
|
||||
r="1.5"
|
||||
fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-2" y="-2"
|
||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||
:style="`stroke: none; fill: url(#${ fediGradientId }); mask: url(#${ fediMaskId })`"/>
|
||||
<text x="1" y="5">Fedi</text>
|
||||
</svg>
|
||||
</div>
|
||||
</mk-widget-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import define from '../../../common/define-widget';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
export default define({
|
||||
name: 'server',
|
||||
props: () => ({
|
||||
design: 0,
|
||||
view: 0
|
||||
})
|
||||
}).extend({
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
viewBoxY: 30,
|
||||
stats: [],
|
||||
fediGradientId: uuid(),
|
||||
fediMaskId: uuid(),
|
||||
localGradientId: uuid(),
|
||||
localMaskId: uuid(),
|
||||
fediPolylinePoints: '',
|
||||
localPolylinePoints: '',
|
||||
fediPolygonPoints: '',
|
||||
localPolygonPoints: '',
|
||||
fediHeadX: null,
|
||||
fediHeadY: null,
|
||||
localHeadX: null,
|
||||
localHeadY: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
viewBoxX(): number {
|
||||
return this.props.view == 0 ? 50 : 100;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
viewBoxX() {
|
||||
this.draw();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.streams.notesStatsStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.notesStatsStream.use();
|
||||
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
this.connection.send({
|
||||
type: 'requestLog',
|
||||
id: Math.random().toString()
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
this.connection.off('statsLog', this.onStatsLog);
|
||||
(this as any).os.streams.notesStatsStream.dispose(this.connectionId);
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.props.view == 2) {
|
||||
this.props.view = 0;
|
||||
} else {
|
||||
this.props.view++;
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
func() {
|
||||
if (this.props.design == 2) {
|
||||
this.props.design = 0;
|
||||
} else {
|
||||
this.props.design++;
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
draw() {
|
||||
const stats = this.props.view == 0 ? this.stats.slice(-50) : this.stats;
|
||||
const fediPeak = Math.max.apply(null, stats.map(x => x.all)) || 1;
|
||||
const localPeak = Math.max.apply(null, stats.map(x => x.local)) || 1;
|
||||
|
||||
const fediPolylinePoints = stats.map((s, i) => [this.viewBoxX - ((stats.length - 1) - i), (1 - (s.all / fediPeak)) * this.viewBoxY]);
|
||||
const localPolylinePoints = stats.map((s, i) => [this.viewBoxX - ((stats.length - 1) - i), (1 - (s.local / localPeak)) * this.viewBoxY]);
|
||||
this.fediPolylinePoints = fediPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
this.localPolylinePoints = localPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
|
||||
this.fediPolygonPoints = `${this.viewBoxX - (stats.length - 1)},${ this.viewBoxY } ${ this.fediPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
|
||||
this.localPolygonPoints = `${this.viewBoxX - (stats.length - 1)},${ this.viewBoxY } ${ this.localPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
|
||||
|
||||
this.fediHeadX = fediPolylinePoints[fediPolylinePoints.length - 1][0];
|
||||
this.fediHeadY = fediPolylinePoints[fediPolylinePoints.length - 1][1];
|
||||
this.localHeadX = localPolylinePoints[localPolylinePoints.length - 1][0];
|
||||
this.localHeadY = localPolylinePoints[localPolylinePoints.length - 1][1];
|
||||
},
|
||||
onStats(stats) {
|
||||
this.stats.push(stats);
|
||||
if (this.stats.length > 100) this.stats.shift();
|
||||
this.draw();
|
||||
},
|
||||
onStatsLog(statsLog) {
|
||||
statsLog.forEach(stats => this.onStats(stats));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
root(isDark)
|
||||
&.dual
|
||||
> svg
|
||||
width 50%
|
||||
float left
|
||||
|
||||
&:first-child
|
||||
padding-right 5px
|
||||
|
||||
&:last-child
|
||||
padding-left 5px
|
||||
|
||||
> svg
|
||||
display block
|
||||
padding 10px
|
||||
width 100%
|
||||
|
||||
> text
|
||||
font-size 5px
|
||||
fill isDark ? rgba(#fff, 0.55) : rgba(#000, 0.55)
|
||||
|
||||
> tspan
|
||||
opacity 0.5
|
||||
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
clear both
|
||||
|
||||
.qpdmibaztplkylerhdbllwcokyrfxeyj[data-darkmode]
|
||||
root(true)
|
||||
|
||||
.qpdmibaztplkylerhdbllwcokyrfxeyj:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="cpu-memory">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
|
||||
<defs>
|
||||
<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
|
||||
@ -16,15 +16,20 @@
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1"/>
|
||||
<circle
|
||||
:cx="cpuHeadX"
|
||||
:cy="cpuHeadY"
|
||||
r="1.5"
|
||||
fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-1" y="-1"
|
||||
:width="viewBoxX + 2" :height="viewBoxY + 2"
|
||||
x="-2" y="-2"
|
||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||
:style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"/>
|
||||
<text x="1" y="5">CPU <tspan>{{ cpuP }}%</tspan></text>
|
||||
</svg>
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
|
||||
<defs>
|
||||
<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
|
||||
@ -40,11 +45,16 @@
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1"/>
|
||||
<circle
|
||||
:cx="memHeadX"
|
||||
:cy="memHeadY"
|
||||
r="1.5"
|
||||
fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-1" y="-1"
|
||||
:width="viewBoxX + 2" :height="viewBoxY + 2"
|
||||
x="-2" y="-2"
|
||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||
:style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"/>
|
||||
<text x="1" y="5">MEM <tspan>{{ memP }}%</tspan></text>
|
||||
</svg>
|
||||
@ -70,15 +80,25 @@ export default Vue.extend({
|
||||
memPolylinePoints: '',
|
||||
cpuPolygonPoints: '',
|
||||
memPolygonPoints: '',
|
||||
cpuHeadX: null,
|
||||
cpuHeadY: null,
|
||||
memHeadX: null,
|
||||
memHeadY: null,
|
||||
cpuP: '',
|
||||
memP: ''
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
this.connection.send({
|
||||
type: 'requestLog',
|
||||
id: Math.random().toString()
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
this.connection.off('statsLog', this.onStatsLog);
|
||||
},
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
@ -86,14 +106,24 @@ export default Vue.extend({
|
||||
this.stats.push(stats);
|
||||
if (this.stats.length > 50) this.stats.shift();
|
||||
|
||||
this.cpuPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - s.cpu_usage) * this.viewBoxY}`).join(' ');
|
||||
this.memPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - (s.mem.used / s.mem.total)) * this.viewBoxY}`).join(' ');
|
||||
const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu_usage) * this.viewBoxY]);
|
||||
const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.used / s.mem.total)) * this.viewBoxY]);
|
||||
this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
|
||||
this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ this.cpuPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
|
||||
this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ this.memPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
|
||||
|
||||
this.cpuHeadX = cpuPolylinePoints[cpuPolylinePoints.length - 1][0];
|
||||
this.cpuHeadY = cpuPolylinePoints[cpuPolylinePoints.length - 1][1];
|
||||
this.memHeadX = memPolylinePoints[memPolylinePoints.length - 1][0];
|
||||
this.memHeadY = memPolylinePoints[memPolylinePoints.length - 1][1];
|
||||
|
||||
this.cpuP = (stats.cpu_usage * 100).toFixed(0);
|
||||
this.memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
|
||||
},
|
||||
onStatsLog(statsLog) {
|
||||
statsLog.forEach(stats => this.onStats(stats));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -55,11 +55,11 @@ export default define({
|
||||
this.fetching = false;
|
||||
});
|
||||
|
||||
this.connection = (this as any).os.streams.serverStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.serverStream.use();
|
||||
this.connection = (this as any).os.streams.serverStatsStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.serverStatsStream.use();
|
||||
},
|
||||
beforeDestroy() {
|
||||
(this as any).os.streams.serverStream.dispose(this.connectionId);
|
||||
(this as any).os.streams.serverStatsStream.dispose(this.connectionId);
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
|
@ -1,6 +1,8 @@
|
||||
declare const _HOST_: string;
|
||||
declare const _HOSTNAME_: string;
|
||||
declare const _URL_: string;
|
||||
declare const _NAME_: string;
|
||||
declare const _DESCRIPTION_: string;
|
||||
declare const _API_URL_: string;
|
||||
declare const _WS_URL_: string;
|
||||
declare const _DOCS_URL_: string;
|
||||
@ -17,10 +19,13 @@ declare const _VERSION_: string;
|
||||
declare const _CODENAME_: string;
|
||||
declare const _LICENSE_: string;
|
||||
declare const _GOOGLE_MAPS_API_KEY_: string;
|
||||
declare const _WELCOME_BG_URL_: string;
|
||||
|
||||
export const host = _HOST_;
|
||||
export const hostname = _HOSTNAME_;
|
||||
export const url = _URL_;
|
||||
export const name = _NAME_;
|
||||
export const description = _DESCRIPTION_;
|
||||
export const apiUrl = _API_URL_;
|
||||
export const wsUrl = _WS_URL_;
|
||||
export const docsUrl = _DOCS_URL_;
|
||||
@ -37,3 +42,4 @@ export const version = _VERSION_;
|
||||
export const codename = _CODENAME_;
|
||||
export const license = _LICENSE_;
|
||||
export const googleMapsApiKey = _GOOGLE_MAPS_API_KEY_;
|
||||
export const welcomeBgUrl = _WELCOME_BG_URL_;
|
||||
|
@ -21,7 +21,7 @@ export default (os: OS) => opts => {
|
||||
res(file);
|
||||
};
|
||||
|
||||
window.open(url + '/selectdrive',
|
||||
window.open(url + `/selectdrive?multiple=${o.multiple}`,
|
||||
'choose_drive_window',
|
||||
'height=500, width=800');
|
||||
}
|
||||
|
@ -33,7 +33,9 @@ import MkHomeCustomize from './views/pages/home-customize.vue';
|
||||
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
||||
import MkNote from './views/pages/note.vue';
|
||||
import MkSearch from './views/pages/search.vue';
|
||||
import MkTag from './views/pages/tag.vue';
|
||||
import MkOthello from './views/pages/othello.vue';
|
||||
import MkShare from './views/pages/share.vue';
|
||||
|
||||
/**
|
||||
* init
|
||||
@ -60,6 +62,8 @@ init(async (launch) => {
|
||||
{ path: '/i/lists/:list', component: MkUserList },
|
||||
{ path: '/selectdrive', component: MkSelectDrive },
|
||||
{ path: '/search', component: MkSearch },
|
||||
{ path: '/tags/:tag', component: MkTag },
|
||||
{ path: '/share', component: MkShare },
|
||||
{ path: '/othello', component: MkOthello },
|
||||
{ path: '/othello/:game', component: MkOthello },
|
||||
{ path: '/@:user', component: MkUser },
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 21 7" preserveAspectRatio="none">
|
||||
<svg viewBox="0 0 21 7">
|
||||
<rect v-for="record in data" class="day"
|
||||
width="1" height="1"
|
||||
:x="record.x" :y="record.date.weekday"
|
||||
@ -15,7 +15,7 @@
|
||||
style="pointer-events: none;"/>
|
||||
<rect class="today"
|
||||
width="1" height="1"
|
||||
:x="data[data.length - 1].x" :y="data[data.length - 1].date.weekday"
|
||||
:x="data[0].x" :y="data[0].date.weekday"
|
||||
rx="1" ry="1"
|
||||
fill="none"
|
||||
stroke-width="0.1"
|
||||
@ -33,7 +33,7 @@ export default Vue.extend({
|
||||
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
||||
|
||||
let x = 0;
|
||||
this.data.reverse().forEach(d => {
|
||||
this.data.slice().reverse().forEach(d => {
|
||||
d.x = x;
|
||||
d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" @mousedown.prevent="onMousedown">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" @mousedown.prevent="onMousedown">
|
||||
<title>%i18n:@total%<br/>%i18n:@notes%<br/>%i18n:@replies%<br/>%i18n:@renotes%</title>
|
||||
<polyline
|
||||
:points="pointsNote"
|
||||
@ -55,7 +55,6 @@ export default Vue.extend({
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.data.reverse();
|
||||
this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
|
||||
this.render();
|
||||
},
|
||||
@ -63,10 +62,11 @@ export default Vue.extend({
|
||||
render() {
|
||||
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
||||
if (peak != 0) {
|
||||
this.pointsNote = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
|
||||
this.pointsReply = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
|
||||
this.pointsRenote = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
|
||||
this.pointsTotal = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
|
||||
const data = this.data.slice().reverse();
|
||||
this.pointsNote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
|
||||
this.pointsReply = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
|
||||
this.pointsRenote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
|
||||
this.pointsTotal = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
|
||||
}
|
||||
},
|
||||
onMousedown(e) {
|
||||
|
@ -1,15 +1,17 @@
|
||||
<template>
|
||||
<ul class="menu">
|
||||
<li v-for="(item, i) in menu" :class="item.type">
|
||||
<template v-if="item.type == 'item'">
|
||||
<p @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
|
||||
</template>
|
||||
<template v-if="item.type == 'link'">
|
||||
<a :href="item.href" :target="item.target" @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</a>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'nest'">
|
||||
<p><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
|
||||
<me-nu :menu="item.menu" @x="click"/>
|
||||
<li v-for="(item, i) in menu" :class="item ? item.type : item === null ? 'divider' : null">
|
||||
<template v-if="item">
|
||||
<template v-if="item.type == null || item.type == 'item'">
|
||||
<p @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'link'">
|
||||
<a :href="item.href" :target="item.target" @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</a>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'nest'">
|
||||
<p><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
|
||||
<me-nu :menu="item.menu" @x="click"/>
|
||||
</template>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="context-menu" :style="{ left: `${x}px`, top: `${y}px` }" @contextmenu.prevent="() => {}">
|
||||
<div class="context-menu" @contextmenu.prevent="() => {}">
|
||||
<x-menu :menu="menu" @x="click"/>
|
||||
</div>
|
||||
</template>
|
||||
@ -17,6 +17,23 @@ export default Vue.extend({
|
||||
props: ['x', 'y', 'menu'],
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const width = this.$el.offsetWidth;
|
||||
const height = this.$el.offsetHeight;
|
||||
|
||||
let x = this.x;
|
||||
let y = this.y;
|
||||
|
||||
if (x + width - window.pageXOffset > window.innerWidth) {
|
||||
x = window.innerWidth - width + window.pageXOffset;
|
||||
}
|
||||
|
||||
if (y + height - window.pageYOffset > window.innerHeight) {
|
||||
y = window.innerHeight - height + window.pageYOffset;
|
||||
}
|
||||
|
||||
this.$el.style.left = x + 'px';
|
||||
this.$el.style.top = y + 'px';
|
||||
|
||||
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||
el.addEventListener('mousedown', this.onMousedown);
|
||||
});
|
||||
@ -38,7 +55,7 @@ export default Vue.extend({
|
||||
return false;
|
||||
},
|
||||
click(item) {
|
||||
if (item.onClick) item.onClick();
|
||||
if (item.action) item.action();
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
@ -59,7 +76,6 @@ root(isDark)
|
||||
$item-height = 38px
|
||||
$padding = 10px
|
||||
|
||||
display none
|
||||
position fixed
|
||||
top 0
|
||||
left 0
|
||||
|
@ -66,37 +66,33 @@ export default Vue.extend({
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.rename%',
|
||||
icon: '%fa:i-cursor%',
|
||||
onClick: this.rename
|
||||
action: this.rename
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.copy-url%',
|
||||
icon: '%fa:link%',
|
||||
onClick: this.copyUrl
|
||||
action: this.copyUrl
|
||||
}, {
|
||||
type: 'link',
|
||||
href: `${this.file.url}?download`,
|
||||
text: '%i18n:@contextmenu.download%',
|
||||
icon: '%fa:download%',
|
||||
}, {
|
||||
type: 'divider',
|
||||
}, {
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:common.delete%',
|
||||
icon: '%fa:R trash-alt%',
|
||||
onClick: this.deleteFile
|
||||
}, {
|
||||
type: 'divider',
|
||||
}, {
|
||||
action: this.deleteFile
|
||||
}, null, {
|
||||
type: 'nest',
|
||||
text: '%i18n:@contextmenu.else-files%',
|
||||
menu: [{
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.set-as-avatar%',
|
||||
onClick: this.setAsAvatar
|
||||
action: this.setAsAvatar
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.set-as-banner%',
|
||||
onClick: this.setAsBanner
|
||||
action: this.setAsBanner
|
||||
}]
|
||||
}, {
|
||||
type: 'nest',
|
||||
@ -104,7 +100,7 @@ export default Vue.extend({
|
||||
menu: [{
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.add-app%...',
|
||||
onClick: this.addApp
|
||||
action: this.addApp
|
||||
}]
|
||||
}], {
|
||||
closed: () => {
|
||||
@ -149,7 +145,7 @@ export default Vue.extend({
|
||||
(this as any).api('drive/files/update', {
|
||||
fileId: this.file.id,
|
||||
name: name
|
||||
})
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -177,7 +173,9 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
deleteFile() {
|
||||
alert('not implemented yet');
|
||||
(this as any).api('drive/files/delete', {
|
||||
fileId: this.file.id
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -56,26 +56,22 @@ export default Vue.extend({
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.move-to-this-folder%',
|
||||
icon: '%fa:arrow-right%',
|
||||
onClick: this.go
|
||||
action: this.go
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.show-in-new-window%',
|
||||
icon: '%fa:R window-restore%',
|
||||
onClick: this.newWindow
|
||||
}, {
|
||||
type: 'divider',
|
||||
}, {
|
||||
action: this.newWindow
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.rename%',
|
||||
icon: '%fa:i-cursor%',
|
||||
onClick: this.rename
|
||||
}, {
|
||||
type: 'divider',
|
||||
}, {
|
||||
action: this.rename
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:common.delete%',
|
||||
icon: '%fa:R trash-alt%',
|
||||
onClick: this.deleteFolder
|
||||
action: this.deleteFolder
|
||||
}], {
|
||||
closed: () => {
|
||||
this.isContextmenuShowing = false;
|
||||
|
@ -118,6 +118,7 @@ export default Vue.extend({
|
||||
|
||||
this.connection.on('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.on('file_updated', this.onStreamDriveFileUpdated);
|
||||
this.connection.on('file_deleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.on('folder_created', this.onStreamDriveFolderCreated);
|
||||
this.connection.on('folder_updated', this.onStreamDriveFolderUpdated);
|
||||
|
||||
@ -130,6 +131,7 @@ export default Vue.extend({
|
||||
beforeDestroy() {
|
||||
this.connection.off('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.off('file_updated', this.onStreamDriveFileUpdated);
|
||||
this.connection.off('file_deleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.off('folder_created', this.onStreamDriveFolderCreated);
|
||||
this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
|
||||
(this as any).os.streams.driveStream.dispose(this.connectionId);
|
||||
@ -140,17 +142,17 @@ export default Vue.extend({
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.create-folder%',
|
||||
icon: '%fa:R folder%',
|
||||
onClick: this.createFolder
|
||||
action: this.createFolder
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.upload%',
|
||||
icon: '%fa:upload%',
|
||||
onClick: this.selectLocalFile
|
||||
action: this.selectLocalFile
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.url-upload%',
|
||||
icon: '%fa:cloud-upload-alt%',
|
||||
onClick: this.urlUpload
|
||||
action: this.urlUpload
|
||||
}]);
|
||||
},
|
||||
|
||||
@ -167,6 +169,10 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
onStreamDriveFileDeleted(fileId) {
|
||||
this.removeFile(fileId);
|
||||
},
|
||||
|
||||
onStreamDriveFolderCreated(folder) {
|
||||
this.addFolder(folder, true);
|
||||
},
|
||||
|
@ -23,6 +23,8 @@
|
||||
<option value="post-form">%i18n:common.widgets.post-form%</option>
|
||||
<option value="messaging">%i18n:common.widgets.messaging%</option>
|
||||
<option value="memo">%i18n:common.widgets.memo%</option>
|
||||
<option value="hashtags">%i18n:common.widgets.hashtags%</option>
|
||||
<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
|
||||
<option value="server">%i18n:common.widgets.server%</option>
|
||||
<option value="donation">%i18n:common.widgets.donation%</option>
|
||||
<option value="nav">%i18n:common.widgets.nav%</option>
|
||||
|
@ -48,7 +48,7 @@
|
||||
<mk-poll v-if="p.poll" :note="p"/>
|
||||
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
|
||||
</div>
|
||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
|
||||
<div class="map" v-if="p.geo" ref="map"></div>
|
||||
|
@ -1,23 +1,8 @@
|
||||
<template>
|
||||
<div class="mk-note-preview" :title="title">
|
||||
<mk-avatar class="avatar" :user="note.user"/>
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="!mini"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="note" :mini="true"/>
|
||||
<div class="body">
|
||||
<mk-sub-note-content class="text" :note="note"/>
|
||||
</div>
|
||||
@ -30,7 +15,17 @@ import Vue from 'vue';
|
||||
import dateStringify from '../../../common/scripts/date-stringify';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['note'],
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
mini: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title(): string {
|
||||
return dateStringify(this.note.createdAt);
|
||||
@ -56,43 +51,6 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
white-space nowrap
|
||||
|
||||
> .name
|
||||
margin 0 .5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #607073
|
||||
font-size 1em
|
||||
font-weight bold
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .username
|
||||
margin 0 .5em 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color isDark ? #606984 : #d1d8da
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #b2b8bb
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
|
||||
> .text
|
||||
|
@ -2,25 +2,7 @@
|
||||
<div class="sub" :title="title">
|
||||
<mk-avatar class="avatar" :user="note.user"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="note.user.isAdmin">admin</span>
|
||||
<span class="is-bot" v-if="note.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="note"/>
|
||||
<div class="body">
|
||||
<mk-sub-note-content class="text" :note="note"/>
|
||||
</div>
|
||||
@ -62,57 +44,8 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
> .header
|
||||
margin-bottom 2px
|
||||
white-space nowrap
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 .5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #607073
|
||||
font-size 1em
|
||||
font-weight bold
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 0.5em 0 0
|
||||
padding 1px 5px
|
||||
font-size 10px
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
margin 0 .5em 0 0
|
||||
color isDark ? #606984 : #d1d8da
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #b2b8bb
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
|
||||
|
@ -14,26 +14,7 @@
|
||||
<article>
|
||||
<mk-avatar class="avatar" :user="p.user"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<router-link class="name" :to="p.user | userPage" v-user-preview="p.user.id">{{ p.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="p.user.isAdmin">admin</span>
|
||||
<span class="is-bot" v-if="p.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="p.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="p.user"/></span>
|
||||
<div class="info">
|
||||
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
||||
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="p | notePage">
|
||||
<mk-time :time="p.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="p.visibility != 'public'">
|
||||
<template v-if="p.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="p.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="p.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="p.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="p"/>
|
||||
<div class="body">
|
||||
<p v-if="p.cw != null" class="cw">
|
||||
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
|
||||
@ -52,7 +33,7 @@
|
||||
</div>
|
||||
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
|
||||
</div>
|
||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||
<div class="map" v-if="p.geo" ref="map"></div>
|
||||
@ -409,64 +390,8 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
> .header
|
||||
margin-bottom 4px
|
||||
white-space nowrap
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 .5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #627079
|
||||
font-size 1em
|
||||
font-weight bold
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 .5em 0 0
|
||||
padding 1px 6px
|
||||
font-size 12px
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
margin 0 .5em 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color isDark ? #606984 : #ccc
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #c0c0c0
|
||||
|
||||
> .mobile
|
||||
margin-right 8px
|
||||
|
||||
> .app
|
||||
margin-right 8px
|
||||
padding-right 8px
|
||||
border-right solid 1px isDark ? #1c2023 : #eaeaea
|
||||
|
||||
> .visibility
|
||||
margin-left 8px
|
||||
|
||||
> .body
|
||||
|
||||
|
@ -206,7 +206,7 @@ root(isDark)
|
||||
margin 0
|
||||
padding 16px
|
||||
overflow-wrap break-word
|
||||
font-size 12px
|
||||
font-size 13px
|
||||
border-bottom solid 1px isDark ? #1c2023 : rgba(#000, 0.05)
|
||||
|
||||
&:last-child
|
||||
|
@ -50,6 +50,7 @@ import * as XDraggable from 'vuedraggable';
|
||||
import getKao from '../../../common/scripts/get-kao';
|
||||
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
||||
import parse from '../../../../../text/parse';
|
||||
import { host } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
@ -57,7 +58,25 @@ export default Vue.extend({
|
||||
MkVisibilityChooser
|
||||
},
|
||||
|
||||
props: ['reply', 'renote'],
|
||||
props: {
|
||||
reply: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
renote: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
initialText: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
instant: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
@ -117,6 +136,10 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.initialText) {
|
||||
this.text = this.initialText;
|
||||
}
|
||||
|
||||
if (this.reply && this.reply.user.host != null) {
|
||||
this.text = `@${this.reply.user.username}@${this.reply.user.host} `;
|
||||
}
|
||||
@ -129,6 +152,7 @@ export default Vue.extend({
|
||||
|
||||
// 自分は除外
|
||||
if (this.$store.state.i.username == x.username && x.host == null) return;
|
||||
if (this.$store.state.i.username == x.username && x.host == host) return;
|
||||
|
||||
// 重複は除外
|
||||
if (this.text.indexOf(`${mention} `) != -1) return;
|
||||
@ -139,17 +163,19 @@ export default Vue.extend({
|
||||
|
||||
this.$nextTick(() => {
|
||||
// 書きかけの投稿を復元
|
||||
const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[this.draftId];
|
||||
if (draft) {
|
||||
this.text = draft.data.text;
|
||||
this.files = draft.data.files;
|
||||
if (draft.data.poll) {
|
||||
this.poll = true;
|
||||
this.$nextTick(() => {
|
||||
(this.$refs.poll as any).set(draft.data.poll);
|
||||
});
|
||||
if (!this.instant) {
|
||||
const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[this.draftId];
|
||||
if (draft) {
|
||||
this.text = draft.data.text;
|
||||
this.files = draft.data.files;
|
||||
if (draft.data.poll) {
|
||||
this.poll = true;
|
||||
this.$nextTick(() => {
|
||||
(this.$refs.poll as any).set(draft.data.poll);
|
||||
});
|
||||
}
|
||||
this.$emit('change-attached-media', this.files);
|
||||
}
|
||||
this.$emit('change-attached-media', this.files);
|
||||
}
|
||||
|
||||
this.$nextTick(() => this.watch());
|
||||
@ -347,6 +373,8 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
saveDraft() {
|
||||
if (this.instant) return;
|
||||
|
||||
const data = JSON.parse(localStorage.getItem('drafts') || '{}');
|
||||
|
||||
data[this.draftId] = {
|
||||
|
@ -17,7 +17,11 @@ export default Vue.extend({
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
location.href = `/search?q=${encodeURIComponent(this.q)}`;
|
||||
if (this.q.startsWith('#')) {
|
||||
this.$router.push(`/tags/${encodeURIComponent(this.q.substr(1))}`);
|
||||
} else {
|
||||
this.$router.push(`/search?q=${encodeURIComponent(this.q)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mk-ui" :style="style">
|
||||
<x-header class="header"/>
|
||||
<x-header class="header" v-show="!zenMode"/>
|
||||
<div class="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@ -16,6 +16,11 @@ 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 {};
|
||||
@ -39,6 +44,11 @@ export default Vue.extend({
|
||||
e.preventDefault();
|
||||
(this as any).apis.post();
|
||||
}
|
||||
|
||||
if (e.which == 90) { // z
|
||||
e.preventDefault();
|
||||
this.zenMode = !this.zenMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
35
src/client/app/desktop/views/pages/deck/deck.column-core.vue
Normal file
35
src/client/app/desktop/views/pages/deck/deck.column-core.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<x-widgets-column v-if="column.type == 'widgets'" :column="column" :is-stacked="isStacked"/>
|
||||
<x-notifications-column v-else-if="column.type == 'notifications'" :column="column" :is-stacked="isStacked"/>
|
||||
<x-tl-column v-else-if="column.type == 'home'" :column="column" :is-stacked="isStacked"/>
|
||||
<x-tl-column v-else-if="column.type == 'local'" :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"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XTlColumn from './deck.tl-column.vue';
|
||||
import XNotificationsColumn from './deck.notifications-column.vue';
|
||||
import XWidgetsColumn from './deck.widgets-column.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XTlColumn,
|
||||
XNotificationsColumn,
|
||||
XWidgetsColumn
|
||||
},
|
||||
|
||||
props: {
|
||||
column: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isStacked: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,10 +1,22 @@
|
||||
<template>
|
||||
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow }">
|
||||
<header :class="{ indicate }">
|
||||
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow, active, isStacked, draghover, dragging, dropready }"
|
||||
@dragover.prevent.stop="onDragover"
|
||||
@dragenter.prevent="onDragenter"
|
||||
@dragleave="onDragleave"
|
||||
@drop.prevent.stop="onDrop"
|
||||
>
|
||||
<header :class="{ indicate: count > 0 }"
|
||||
draggable="true"
|
||||
@click="toggleActive"
|
||||
@dragstart="onDragstart"
|
||||
@dragend="onDragend"
|
||||
@contextmenu.prevent.stop="onContextmenu"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
<button ref="menu" @click="showMenu">%fa:caret-down%</button>
|
||||
<span class="count" v-if="count > 0">({{ count }})</span>
|
||||
<button ref="menu" @click.stop="showMenu">%fa:caret-down%</button>
|
||||
</header>
|
||||
<div ref="body">
|
||||
<div ref="body" v-show="active">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
@ -13,12 +25,17 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import Menu from '../../../../common/views/components/menu.vue';
|
||||
import contextmenu from '../../../api/contextmenu';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: false
|
||||
column: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isStacked: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
@ -26,7 +43,8 @@ export default Vue.extend({
|
||||
},
|
||||
menu: {
|
||||
type: Array,
|
||||
required: false
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
naked: {
|
||||
type: Boolean,
|
||||
@ -40,30 +58,69 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
inject: {
|
||||
getColumnVm: { from: 'getColumnVm' }
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
indicate: false
|
||||
count: 0,
|
||||
active: true,
|
||||
dragging: false,
|
||||
draghover: false,
|
||||
dropready: false
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
active(v) {
|
||||
if (v && this.isScrollTop()) {
|
||||
this.$emit('top');
|
||||
}
|
||||
},
|
||||
dragging(v) {
|
||||
this.$root.$emit(v ? 'deck.column.dragStart' : 'deck.column.dragEnd');
|
||||
}
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
column: this,
|
||||
isScrollTop: this.isScrollTop,
|
||||
indicate: v => this.indicate = v
|
||||
count: v => this.count = v
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$refs.body.addEventListener('scroll', this.onScroll, { passive: true });
|
||||
this.$root.$on('deck.column.dragStart', this.onOtherDragStart);
|
||||
this.$root.$on('deck.column.dragEnd', this.onOtherDragEnd);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.$refs.body.removeEventListener('scroll', this.onScroll);
|
||||
this.$root.$off('deck.column.dragStart', this.onOtherDragStart);
|
||||
this.$root.$off('deck.column.dragEnd', this.onOtherDragEnd);
|
||||
},
|
||||
|
||||
methods: {
|
||||
onOtherDragStart() {
|
||||
this.dropready = true;
|
||||
},
|
||||
|
||||
onOtherDragEnd() {
|
||||
this.dropready = false;
|
||||
},
|
||||
|
||||
toggleActive() {
|
||||
if (!this.isStacked) return;
|
||||
const vms = this.$store.state.settings.deck.layout.find(ids => ids.indexOf(this.column.id) != -1).map(id => this.getColumnVm(id));
|
||||
if (this.active && vms.filter(vm => vm.$el.classList.contains('active')).length == 1) return;
|
||||
this.active = !this.active;
|
||||
},
|
||||
|
||||
isScrollTop() {
|
||||
return this.$refs.body.scrollTop == 0;
|
||||
return this.active && this.$refs.body.scrollTop == 0;
|
||||
},
|
||||
|
||||
onScroll() {
|
||||
@ -77,32 +134,60 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
showMenu() {
|
||||
getMenu() {
|
||||
const items = [{
|
||||
content: '%fa:pencil-alt% %i18n:common.deck.rename%',
|
||||
onClick: () => {
|
||||
icon: '%fa:pencil-alt%',
|
||||
text: '%i18n:common.deck.rename%',
|
||||
action: () => {
|
||||
(this as any).apis.input({
|
||||
title: '%i18n:common.deck.rename%',
|
||||
default: this.name,
|
||||
allowEmpty: false
|
||||
}).then(name => {
|
||||
this.$store.dispatch('settings/renameDeckColumn', { id: this.id, name });
|
||||
this.$store.dispatch('settings/renameDeckColumn', { id: this.column.id, name });
|
||||
});
|
||||
}
|
||||
}, null, {
|
||||
content: '%fa:arrow-left% %i18n:common.deck.swap-left%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/swapLeftDeckColumn', this.id);
|
||||
icon: '%fa:arrow-left%',
|
||||
text: '%i18n:common.deck.swap-left%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/swapLeftDeckColumn', this.column.id);
|
||||
}
|
||||
}, {
|
||||
content: '%fa:arrow-right% %i18n:common.deck.swap-right%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/swapRightDeckColumn', this.id);
|
||||
icon: '%fa:arrow-right%',
|
||||
text: '%i18n:common.deck.swap-right%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/swapRightDeckColumn', this.column.id);
|
||||
}
|
||||
}, null, {
|
||||
content: '%fa:trash-alt R% %i18n:common.deck.remove%',
|
||||
onClick: () => {
|
||||
this.$store.dispatch('settings/removeDeckColumn', this.id);
|
||||
}, this.isStacked ? {
|
||||
icon: '%fa:arrow-up%',
|
||||
text: '%i18n:common.deck.swap-up%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/swapUpDeckColumn', this.column.id);
|
||||
}
|
||||
} : undefined, this.isStacked ? {
|
||||
icon: '%fa:arrow-down%',
|
||||
text: '%i18n:common.deck.swap-down%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/swapDownDeckColumn', this.column.id);
|
||||
}
|
||||
} : undefined, null, {
|
||||
icon: '%fa:window-restore R%',
|
||||
text: '%i18n:common.deck.stack-left%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/stackLeftDeckColumn', this.column.id);
|
||||
}
|
||||
}, this.isStacked ? {
|
||||
icon: '%fa:window-maximize R%',
|
||||
text: '%i18n:common.deck.pop-right%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/popRightDeckColumn', this.column.id);
|
||||
}
|
||||
} : undefined, null, {
|
||||
icon: '%fa:trash-alt R%',
|
||||
text: '%i18n:common.deck.remove%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/removeDeckColumn', this.column.id);
|
||||
}
|
||||
}];
|
||||
|
||||
@ -111,11 +196,63 @@ export default Vue.extend({
|
||||
this.menu.reverse().forEach(i => items.unshift(i));
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
onContextmenu(e) {
|
||||
contextmenu((this as any).os)(e, this.getMenu());
|
||||
},
|
||||
|
||||
showMenu() {
|
||||
this.os.new(Menu, {
|
||||
source: this.$refs.menu,
|
||||
compact: false,
|
||||
items
|
||||
items: this.getMenu()
|
||||
});
|
||||
},
|
||||
|
||||
onDragstart(e) {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('mk-deck-column', this.column.id);
|
||||
this.dragging = true;
|
||||
},
|
||||
|
||||
onDragend(e) {
|
||||
this.dragging = false;
|
||||
},
|
||||
|
||||
onDragover(e) {
|
||||
// 自分自身がドラッグされている場合
|
||||
if (this.dragging) {
|
||||
// 自分自身にはドロップさせない
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const isDeckColumn = e.dataTransfer.types[0] == 'mk-deck-column';
|
||||
|
||||
e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
|
||||
},
|
||||
|
||||
onDragenter() {
|
||||
if (!this.dragging) this.draghover = true;
|
||||
},
|
||||
|
||||
onDragleave() {
|
||||
this.draghover = false;
|
||||
},
|
||||
|
||||
onDrop(e) {
|
||||
this.draghover = false;
|
||||
this.$root.$emit('deck.column.dragEnd');
|
||||
|
||||
const id = e.dataTransfer.getData('mk-deck-column');
|
||||
if (id != null && id != '') {
|
||||
this.$store.dispatch('settings/swapDeckColumn', {
|
||||
a: this.column.id,
|
||||
b: id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -128,14 +265,30 @@ root(isDark)
|
||||
$header-height = 42px
|
||||
|
||||
width 330px
|
||||
min-width 330px
|
||||
height 100%
|
||||
background isDark ? #282C37 : #fff
|
||||
border-radius 6px
|
||||
box-shadow 0 2px 16px rgba(#000, 0.1)
|
||||
overflow hidden
|
||||
|
||||
&.narrow
|
||||
&.draghover
|
||||
box-shadow 0 0 0 2px rgba($theme-color, 0.8)
|
||||
|
||||
&.dragging
|
||||
box-shadow 0 0 0 2px rgba($theme-color, 0.4)
|
||||
|
||||
&.dropready
|
||||
*
|
||||
pointer-events none
|
||||
|
||||
&:not(.active)
|
||||
flex-basis $header-height
|
||||
min-height $header-height
|
||||
|
||||
&:not(.isStacked).narrow
|
||||
width 285px
|
||||
min-width 285px
|
||||
|
||||
&.naked
|
||||
background rgba(#000, isDark ? 0.25 : 0.1)
|
||||
@ -156,6 +309,13 @@ root(isDark)
|
||||
color isDark ? #e3e5e8 : #888
|
||||
background isDark ? #313543 : #fff
|
||||
box-shadow 0 1px rgba(#000, 0.15)
|
||||
cursor pointer
|
||||
|
||||
&, *
|
||||
user-select none
|
||||
|
||||
*:not(button)
|
||||
pointer-events none
|
||||
|
||||
&.indicate
|
||||
box-shadow 0 3px 0 0 $theme-color
|
||||
@ -164,12 +324,17 @@ root(isDark)
|
||||
[data-fa]
|
||||
margin-right 8px
|
||||
|
||||
> .count
|
||||
margin-left 4px
|
||||
opacity 0.5
|
||||
|
||||
> button
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
width $header-height
|
||||
line-height $header-height
|
||||
font-size 16px
|
||||
color isDark ? #9baec8 : #ccc
|
||||
|
||||
&:hover
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :more="existMore ? more : null"/>
|
||||
<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -23,6 +23,11 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2,25 +2,7 @@
|
||||
<div class="fnlfosztlhtptnongximhlbykxblytcq">
|
||||
<mk-avatar class="avatar" :user="note.user"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="note.user.isAdmin">%i18n:@admin%</span>
|
||||
<span class="is-bot" v-if="note.user.isBot">%i18n:@bot%</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">%i18n:@cat%</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="note" :mini="true"/>
|
||||
<div class="body">
|
||||
<mk-sub-note-content class="text" :note="note"/>
|
||||
</div>
|
||||
@ -72,66 +54,8 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
> .header
|
||||
margin-bottom 2px
|
||||
white-space nowrap
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
margin-right 8px
|
||||
width 18px
|
||||
height 18px
|
||||
border-radius 100%
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 0.5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #607073
|
||||
font-size 1em
|
||||
font-weight 700
|
||||
text-align left
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 0.5em 0 0
|
||||
padding 1px 5px
|
||||
font-size 0.8em
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
text-align left
|
||||
margin 0
|
||||
color isDark ? #606984 : #d1d8da
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #b2b8bb
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="zyjjkidcqjnlegkqebitfviomuqmseqk" :class="{ renote: isRenote }">
|
||||
<div v-if="!mediaView" class="zyjjkidcqjnlegkqebitfviomuqmseqk" :class="{ renote: isRenote }">
|
||||
<div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
|
||||
<x-sub :note="p.reply"/>
|
||||
</div>
|
||||
@ -14,25 +14,7 @@
|
||||
<article>
|
||||
<mk-avatar class="avatar" :user="p.user"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<router-link class="name" :to="p.user | userPage">{{ p.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="p.user.isAdmin">admin</span>
|
||||
<span class="is-bot" v-if="p.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="p.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="p.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="p | notePage">
|
||||
<mk-time :time="p.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="p.visibility != 'public'">
|
||||
<template v-if="p.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="p.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="p.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="p.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="p" :mini="true"/>
|
||||
<div class="body">
|
||||
<p v-if="p.cw != null" class="cw">
|
||||
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
|
||||
@ -51,11 +33,11 @@
|
||||
</div>
|
||||
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
|
||||
</div>
|
||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
|
||||
<div class="renote" v-if="p.renote">
|
||||
<mk-note-preview :note="p.renote"/>
|
||||
<mk-note-preview :note="p.renote" :mini="true"/>
|
||||
</div>
|
||||
</div>
|
||||
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
||||
@ -73,6 +55,14 @@
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else class="srwrkujossgfuhrbnvqkybtzxpblgchi">
|
||||
<div v-if="note.media.length > 0">
|
||||
<mk-media-list :media-list="note.media"/>
|
||||
</div>
|
||||
<div v-if="note.renote && note.renote.media.length > 0">
|
||||
<mk-media-list :media-list="note.renote.media"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -89,7 +79,17 @@ export default Vue.extend({
|
||||
XSub
|
||||
},
|
||||
|
||||
props: ['note'],
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
@ -217,8 +217,18 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
mediaRoot(isDark)
|
||||
font-size 13px
|
||||
margin 4px 12px
|
||||
|
||||
&:first-child
|
||||
margin-top 12px
|
||||
|
||||
&:last-child
|
||||
margin-bottom 12px
|
||||
|
||||
root(isDark)
|
||||
font-size 12px
|
||||
font-size 13px
|
||||
border-bottom solid 1px isDark ? #1c2023 : #eaeaea
|
||||
|
||||
&:last-of-type
|
||||
@ -234,7 +244,7 @@ root(isDark)
|
||||
> .renote
|
||||
display flex
|
||||
align-items center
|
||||
padding 8px 16px
|
||||
padding 8px 16px 0 16px
|
||||
line-height 28px
|
||||
white-space pre
|
||||
color #9dbb00
|
||||
@ -275,7 +285,7 @@ root(isDark)
|
||||
|
||||
> article
|
||||
display flex
|
||||
padding 16px 16px 9px
|
||||
padding 16px 16px 4px
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
@ -292,62 +302,6 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
white-space nowrap
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
margin-right 8px
|
||||
width 20px
|
||||
height 20px
|
||||
border-radius 100%
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 0.5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #627079
|
||||
font-weight bold
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 0.5em 0 0
|
||||
padding 1px 6px
|
||||
font-size 0.8em
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
margin 0 0.5em 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color isDark ? #606984 : #ccc
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #c0c0c0
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
|
||||
> .cw
|
||||
@ -482,7 +436,7 @@ root(isDark)
|
||||
> footer
|
||||
> button
|
||||
margin 0
|
||||
padding 8px
|
||||
padding 4px 8px 8px 8px
|
||||
background transparent
|
||||
border none
|
||||
box-shadow none
|
||||
@ -510,4 +464,10 @@ root(isDark)
|
||||
.zyjjkidcqjnlegkqebitfviomuqmseqk:not([data-darkmode])
|
||||
root(false)
|
||||
|
||||
.srwrkujossgfuhrbnvqkybtzxpblgchi[data-darkmode]
|
||||
mediaRoot(true)
|
||||
|
||||
.srwrkujossgfuhrbnvqkybtzxpblgchi:not([data-darkmode])
|
||||
mediaRoot(false)
|
||||
|
||||
</style>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
<transition-group name="mk-notes" class="transition">
|
||||
<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)" :media-view="mediaView"/>
|
||||
<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>
|
||||
@ -28,24 +28,27 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { url } from '../../../config';
|
||||
import getNoteSummary from '../../../../../renderers/get-note-summary';
|
||||
|
||||
import XNote from './deck.note.vue';
|
||||
|
||||
const displayLimit = 30;
|
||||
const displayLimit = 20;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNote
|
||||
},
|
||||
|
||||
inject: ['column', 'isScrollTop', 'indicate'],
|
||||
inject: ['column', 'isScrollTop', 'count'],
|
||||
|
||||
props: {
|
||||
more: {
|
||||
type: Function,
|
||||
required: false
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
@ -55,7 +58,6 @@ export default Vue.extend({
|
||||
requestInitPromise: null as () => Promise<any[]>,
|
||||
notes: [],
|
||||
queue: [],
|
||||
unreadCount: 0,
|
||||
fetching: true,
|
||||
moreFetching: false
|
||||
};
|
||||
@ -73,6 +75,12 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
queue(q) {
|
||||
this.count(q.length);
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.column.$on('top', this.onTop);
|
||||
this.column.$on('bottom', this.onBottom);
|
||||
@ -141,7 +149,6 @@ export default Vue.extend({
|
||||
}
|
||||
} else {
|
||||
this.queue.push(note);
|
||||
this.indicate(true);
|
||||
}
|
||||
},
|
||||
|
||||
@ -156,7 +163,6 @@ export default Vue.extend({
|
||||
releaseQueue() {
|
||||
this.queue.forEach(n => this.prepend(n, true));
|
||||
this.queue = [];
|
||||
this.indicate(false);
|
||||
},
|
||||
|
||||
async loadMore() {
|
||||
|
@ -112,7 +112,7 @@ export default Vue.extend({
|
||||
root(isDark)
|
||||
> .notification
|
||||
padding 16px
|
||||
font-size 12px
|
||||
font-size 13px
|
||||
overflow-wrap break-word
|
||||
|
||||
&:after
|
||||
|
@ -1,11 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<x-column :id="column.id" :name="name">
|
||||
<span slot="header">%fa:bell R%{{ name }}</span>
|
||||
<x-column :name="name" :column="column" :is-stacked="isStacked">
|
||||
<span slot="header">%fa:bell R%{{ name }}</span>
|
||||
|
||||
<x-notifications/>
|
||||
</x-column>
|
||||
</div>
|
||||
<x-notifications/>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -23,6 +21,10 @@ export default Vue.extend({
|
||||
column: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isStacked: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -21,20 +21,27 @@
|
||||
import Vue from 'vue';
|
||||
import XNotification from './deck.notification.vue';
|
||||
|
||||
const displayLimit = 20;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNotification
|
||||
},
|
||||
|
||||
inject: ['column', 'isScrollTop', 'count'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
fetchingMoreNotifications: false,
|
||||
notifications: [],
|
||||
queue: [],
|
||||
moreNotifications: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
_notifications(): any[] {
|
||||
return (this.notifications as any).map(notification => {
|
||||
@ -46,12 +53,22 @@ export default Vue.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
queue(q) {
|
||||
this.count(q.length);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
||||
this.column.$on('top', this.onTop);
|
||||
this.column.$on('bottom', this.onBottom);
|
||||
|
||||
const max = 10;
|
||||
|
||||
(this as any).api('i/notifications', {
|
||||
@ -66,15 +83,20 @@ export default Vue.extend({
|
||||
this.fetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('notification', this.onNotification);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
|
||||
this.column.$off('top', this.onTop);
|
||||
this.column.$off('bottom', this.onBottom);
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchMoreNotifications() {
|
||||
this.fetchingMoreNotifications = true;
|
||||
|
||||
const max = 30;
|
||||
const max = 20;
|
||||
|
||||
(this as any).api('i/notifications', {
|
||||
limit: max + 1,
|
||||
@ -90,6 +112,7 @@ export default Vue.extend({
|
||||
this.fetchingMoreNotifications = false;
|
||||
});
|
||||
},
|
||||
|
||||
onNotification(notification) {
|
||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||
this.connection.send({
|
||||
@ -97,7 +120,34 @@ export default Vue.extend({
|
||||
id: notification.id
|
||||
});
|
||||
|
||||
this.notifications.unshift(notification);
|
||||
this.prepend(notification);
|
||||
},
|
||||
|
||||
prepend(notification) {
|
||||
if (this.isScrollTop()) {
|
||||
// Prepend the notification
|
||||
this.notifications.unshift(notification);
|
||||
|
||||
// オーバーフローしたら古い通知は捨てる
|
||||
if (this.notifications.length >= displayLimit) {
|
||||
this.notifications = this.notifications.slice(0, displayLimit);
|
||||
}
|
||||
} else {
|
||||
this.queue.push(notification);
|
||||
}
|
||||
},
|
||||
|
||||
releaseQueue() {
|
||||
this.queue.forEach(n => this.prepend(n));
|
||||
this.queue = [];
|
||||
},
|
||||
|
||||
onTop() {
|
||||
this.releaseQueue();
|
||||
},
|
||||
|
||||
onBottom() {
|
||||
this.fetchMoreNotifications();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,22 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<x-column :id="column.id" :menu="menu" :name="name">
|
||||
<span slot="header">
|
||||
<template v-if="column.type == 'home'">%fa:home%</template>
|
||||
<template v-if="column.type == 'local'">%fa:R comments%</template>
|
||||
<template v-if="column.type == 'global'">%fa:globe%</template>
|
||||
<template v-if="column.type == 'list'">%fa:list%</template>
|
||||
<span>{{ name }}</span>
|
||||
</span>
|
||||
<x-column :menu="menu" :name="name" :column="column" :is-stacked="isStacked">
|
||||
<span slot="header">
|
||||
<template v-if="column.type == 'home'">%fa:home%</template>
|
||||
<template v-if="column.type == 'local'">%fa:R comments%</template>
|
||||
<template v-if="column.type == 'global'">%fa:globe%</template>
|
||||
<template v-if="column.type == 'list'">%fa:list%</template>
|
||||
<span>{{ name }}</span>
|
||||
</span>
|
||||
|
||||
<div class="editor" v-if="edit">
|
||||
<mk-switch v-model="column.isMediaOnly" @change="onChangeSettings" text="%i18n:@is-media-only%"/>
|
||||
<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"/>
|
||||
<x-tl v-else :src="column.type" :media-only="column.isMediaOnly"/>
|
||||
</x-column>
|
||||
</div>
|
||||
<div class="editor" style="padding:0 12px" v-if="edit">
|
||||
<mk-switch v-model="column.isMediaOnly" @change="onChangeSettings" text="%i18n:@is-media-only%"/>
|
||||
<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-tl v-else :src="column.type" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -36,6 +34,10 @@ export default Vue.extend({
|
||||
column: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isStacked: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
@ -43,8 +45,9 @@ export default Vue.extend({
|
||||
return {
|
||||
edit: false,
|
||||
menu: [{
|
||||
content: '%fa:cog% %i18n:@edit%',
|
||||
onClick: () => {
|
||||
icon: '%fa:cog%',
|
||||
text: '%i18n:@edit%',
|
||||
action: () => {
|
||||
this.edit = !this.edit;
|
||||
}
|
||||
}]
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :more="existMore ? more : null"/>
|
||||
<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -23,6 +23,11 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<mk-ui :class="$style.root">
|
||||
<div class="qlvquzbjribqcaozciifydkngcwtyzje" :data-darkmode="$store.state.device.darkmode">
|
||||
<template v-for="column in columns">
|
||||
<x-widgets-column v-if="column.type == 'widgets'" :key="column.id" :column="column"/>
|
||||
<x-notifications-column v-if="column.type == 'notifications'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'home'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'local'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'global'" :key="column.id" :column="column"/>
|
||||
<x-tl-column v-if="column.type == 'list'" :key="column.id" :column="column"/>
|
||||
<template v-for="ids in layout">
|
||||
<div v-if="ids.length > 1" class="folder">
|
||||
<template v-for="id, i in ids">
|
||||
<x-column-core :ref="id" :key="id" :column="columns.find(c => c.id == id)" :is-stacked="true"/>
|
||||
</template>
|
||||
</div>
|
||||
<x-column-core v-else :ref="ids[0]" :key="ids[0]" :column="columns.find(c => c.id == ids[0])"/>
|
||||
</template>
|
||||
<button ref="add" @click="add" title="%i18n:common.deck.add-column%">%fa:plus%</button>
|
||||
</div>
|
||||
@ -16,27 +16,34 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XTlColumn from './deck.tl-column.vue';
|
||||
import XNotificationsColumn from './deck.notifications-column.vue';
|
||||
import XWidgetsColumn from './deck.widgets-column.vue';
|
||||
import XColumnCore from './deck.column-core.vue';
|
||||
import Menu from '../../../../common/views/components/menu.vue';
|
||||
import MkUserListsWindow from '../../components/user-lists-window.vue';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XTlColumn,
|
||||
XNotificationsColumn,
|
||||
XWidgetsColumn
|
||||
XColumnCore
|
||||
},
|
||||
|
||||
computed: {
|
||||
columns() {
|
||||
columns(): any[] {
|
||||
if (this.$store.state.settings.deck == null) return [];
|
||||
return this.$store.state.settings.deck.columns;
|
||||
},
|
||||
layout(): any[] {
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
getColumnVm: this.getColumnVm
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.state.settings.deck == null) {
|
||||
const deck = {
|
||||
@ -58,11 +65,23 @@ export default Vue.extend({
|
||||
}]
|
||||
};
|
||||
|
||||
deck.layout = deck.columns.map(c => [c.id]);
|
||||
|
||||
this.$store.dispatch('settings/set', {
|
||||
key: 'deck',
|
||||
value: deck
|
||||
});
|
||||
}
|
||||
|
||||
// 互換性のため
|
||||
if (this.$store.state.settings.deck != null && this.$store.state.settings.deck.layout == null) {
|
||||
this.$store.dispatch('settings/set', {
|
||||
key: 'deck',
|
||||
value: Object.assign({}, this.$store.state.settings.deck, {
|
||||
layout: this.$store.state.settings.deck.columns.map(c => [c.id])
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@ -74,37 +93,45 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
methods: {
|
||||
getColumnVm(id) {
|
||||
return this.$refs[id][0];
|
||||
},
|
||||
|
||||
add() {
|
||||
this.os.new(Menu, {
|
||||
source: this.$refs.add,
|
||||
compact: true,
|
||||
items: [{
|
||||
content: '%i18n:common.deck.home%',
|
||||
onClick: () => {
|
||||
icon: '%fa:home%',
|
||||
text: '%i18n:common.deck.home%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'home'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:common.deck.local%',
|
||||
onClick: () => {
|
||||
icon: '%fa:comments R%',
|
||||
text: '%i18n:common.deck.local%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'local'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:common.deck.global%',
|
||||
onClick: () => {
|
||||
icon: '%fa:globe%',
|
||||
text: '%i18n:common.deck.global%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'global'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:common.deck.list%',
|
||||
onClick: () => {
|
||||
icon: '%fa:list%',
|
||||
text: '%i18n:common.deck.list%',
|
||||
action: () => {
|
||||
const w = (this as any).os.new(MkUserListsWindow);
|
||||
w.$once('choosen', list => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
@ -116,16 +143,18 @@ export default Vue.extend({
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:common.deck.notifications%',
|
||||
onClick: () => {
|
||||
icon: '%fa:bell R%',
|
||||
text: '%i18n:common.deck.notifications%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'notifications'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
content: '%i18n:common.deck.widgets%',
|
||||
onClick: () => {
|
||||
icon: '%fa:calculator%',
|
||||
text: '%i18n:common.deck.widgets%',
|
||||
action: () => {
|
||||
this.$store.dispatch('settings/addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'widgets',
|
||||
@ -159,6 +188,13 @@ root(isDark)
|
||||
&:last-of-type
|
||||
margin-right 0
|
||||
|
||||
&.folder
|
||||
display flex
|
||||
flex-direction column
|
||||
|
||||
> *:not(:last-child)
|
||||
margin-bottom 8px
|
||||
|
||||
> *
|
||||
&:first-child
|
||||
margin-left auto
|
||||
|
@ -1,57 +1,52 @@
|
||||
<template>
|
||||
<div class="wtdtxvecapixsepjtcupubtsmometobz">
|
||||
<x-column :id="column.id" :menu="menu" :naked="true" :narrow="true" :name="name">
|
||||
<span slot="header">%fa:calculator%{{ name }}</span>
|
||||
<x-column :menu="menu" :naked="true" :narrow="true" :name="name" :column="column" :is-stacked="isStacked" class="wtdtxvecapixsepjtcupubtsmometobz">
|
||||
<span slot="header">%fa:calculator%{{ name }}</span>
|
||||
|
||||
<div class="gqpwvtwtprsbmnssnbicggtwqhmylhnq">
|
||||
<template v-if="edit">
|
||||
<header>
|
||||
<select v-model="widgetAdderSelected">
|
||||
<option value="profile">%i18n:common.widgets.profile%</option>
|
||||
<option value="analog-clock">%i18n:common.widgets.analog-clock%</option>
|
||||
<option value="calendar">%i18n:common.widgets.calendar%</option>
|
||||
<option value="timemachine">%i18n:common.widgets.timemachine%</option>
|
||||
<option value="activity">%i18n:common.widgets.activity%</option>
|
||||
<option value="rss">%i18n:common.widgets.rss%</option>
|
||||
<option value="trends">%i18n:common.widgets.trends%</option>
|
||||
<option value="photo-stream">%i18n:common.widgets.photo-stream%</option>
|
||||
<option value="slideshow">%i18n:common.widgets.slideshow%</option>
|
||||
<option value="version">%i18n:common.widgets.version%</option>
|
||||
<option value="broadcast">%i18n:common.widgets.broadcast%</option>
|
||||
<option value="notifications">%i18n:common.widgets.notifications%</option>
|
||||
<option value="users">%i18n:common.widgets.users%</option>
|
||||
<option value="polls">%i18n:common.widgets.polls%</option>
|
||||
<option value="post-form">%i18n:common.widgets.post-form%</option>
|
||||
<option value="messaging">%i18n:common.widgets.messaging%</option>
|
||||
<option value="memo">%i18n:common.widgets.memo%</option>
|
||||
<option value="server">%i18n:common.widgets.server%</option>
|
||||
<option value="donation">%i18n:common.widgets.donation%</option>
|
||||
<option value="nav">%i18n:common.widgets.nav%</option>
|
||||
<option value="tips">%i18n:common.widgets.tips%</option>
|
||||
</select>
|
||||
<button @click="addWidget">%i18n:@add%</button>
|
||||
</header>
|
||||
<x-draggable
|
||||
:list="column.widgets"
|
||||
:options="{ handle: '.handle', animation: 150 }"
|
||||
@sort="onWidgetSort"
|
||||
>
|
||||
<div v-for="widget in column.widgets" class="customize-container" :key="widget.id">
|
||||
<header>
|
||||
<span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
|
||||
</header>
|
||||
<div @click="widgetFunc(widget.id)">
|
||||
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="deck"/>
|
||||
</div>
|
||||
</div>
|
||||
</x-draggable>
|
||||
</template>
|
||||
<template v-else>
|
||||
<component class="widget" v-for="widget in column.widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" platform="deck"/>
|
||||
</template>
|
||||
</div>
|
||||
</x-column>
|
||||
</div>
|
||||
<div class="gqpwvtwtprsbmnssnbicggtwqhmylhnq">
|
||||
<template v-if="edit">
|
||||
<header>
|
||||
<select v-model="widgetAdderSelected" @change="addWidget">
|
||||
<option value="profile">%i18n:common.widgets.profile%</option>
|
||||
<option value="analog-clock">%i18n:common.widgets.analog-clock%</option>
|
||||
<option value="calendar">%i18n:common.widgets.calendar%</option>
|
||||
<option value="timemachine">%i18n:common.widgets.timemachine%</option>
|
||||
<option value="activity">%i18n:common.widgets.activity%</option>
|
||||
<option value="rss">%i18n:common.widgets.rss%</option>
|
||||
<option value="trends">%i18n:common.widgets.trends%</option>
|
||||
<option value="photo-stream">%i18n:common.widgets.photo-stream%</option>
|
||||
<option value="slideshow">%i18n:common.widgets.slideshow%</option>
|
||||
<option value="version">%i18n:common.widgets.version%</option>
|
||||
<option value="broadcast">%i18n:common.widgets.broadcast%</option>
|
||||
<option value="notifications">%i18n:common.widgets.notifications%</option>
|
||||
<option value="users">%i18n:common.widgets.users%</option>
|
||||
<option value="polls">%i18n:common.widgets.polls%</option>
|
||||
<option value="post-form">%i18n:common.widgets.post-form%</option>
|
||||
<option value="messaging">%i18n:common.widgets.messaging%</option>
|
||||
<option value="memo">%i18n:common.widgets.memo%</option>
|
||||
<option value="hashtags">%i18n:common.widgets.hashtags%</option>
|
||||
<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
|
||||
<option value="server">%i18n:common.widgets.server%</option>
|
||||
<option value="donation">%i18n:common.widgets.donation%</option>
|
||||
<option value="nav">%i18n:common.widgets.nav%</option>
|
||||
<option value="tips">%i18n:common.widgets.tips%</option>
|
||||
</select>
|
||||
</header>
|
||||
<x-draggable
|
||||
:list="column.widgets"
|
||||
:options="{ animation: 150 }"
|
||||
@sort="onWidgetSort"
|
||||
>
|
||||
<div v-for="widget in column.widgets" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="widgetFunc(widget.id)">
|
||||
<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
|
||||
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="deck"/>
|
||||
</div>
|
||||
</x-draggable>
|
||||
</template>
|
||||
<template v-else>
|
||||
<component class="widget" v-for="widget in column.widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" platform="deck"/>
|
||||
</template>
|
||||
</div>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -70,6 +65,10 @@ export default Vue.extend({
|
||||
column: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isStacked: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
@ -90,8 +89,9 @@ export default Vue.extend({
|
||||
|
||||
created() {
|
||||
this.menu = [{
|
||||
content: '%fa:cog% %i18n:@edit%',
|
||||
onClick: () => {
|
||||
icon: '%fa:cog%',
|
||||
text: '%i18n:@edit%',
|
||||
action: () => {
|
||||
this.edit = !this.edit;
|
||||
}
|
||||
}];
|
||||
@ -137,6 +137,13 @@ export default Vue.extend({
|
||||
|
||||
root(isDark)
|
||||
.gqpwvtwtprsbmnssnbicggtwqhmylhnq
|
||||
> header
|
||||
padding 16px
|
||||
|
||||
> *
|
||||
width 100%
|
||||
padding 4px
|
||||
|
||||
.widget, .customize-container
|
||||
margin 8px
|
||||
|
||||
@ -144,7 +151,21 @@ root(isDark)
|
||||
margin-top 0
|
||||
|
||||
.customize-container
|
||||
background #fff
|
||||
cursor move
|
||||
|
||||
> *:not(.remove)
|
||||
pointer-events none
|
||||
|
||||
> .remove
|
||||
position absolute
|
||||
z-index 1
|
||||
top 8px
|
||||
right 8px
|
||||
width 32px
|
||||
height 32px
|
||||
color #fff
|
||||
background rgba(#000, 0.7)
|
||||
border-radius 4px
|
||||
|
||||
> header
|
||||
color isDark ? #fff : #000
|
||||
|
58
src/client/app/desktop/views/pages/share.vue
Normal file
58
src/client/app/desktop/views/pages/share.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="pptjhabgjtt7kwskbfv4y3uml6fpuhmr">
|
||||
<h1>Misskeyで共有</h1>
|
||||
<div>
|
||||
<mk-signin v-if="!$store.getters.isSignedIn"/>
|
||||
<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
|
||||
<p v-if="posted" class="posted">%fa:check%</p>
|
||||
</div>
|
||||
<button v-if="posted" class="ui button" @click="close">閉じる</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
posted: false,
|
||||
text: new URLSearchParams(location.search).get('text')
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.pptjhabgjtt7kwskbfv4y3uml6fpuhmr
|
||||
padding 16px
|
||||
|
||||
> h1
|
||||
margin 0 0 8px 0
|
||||
color #555
|
||||
font-size 20px
|
||||
text-align center
|
||||
|
||||
> div
|
||||
max-width 500px
|
||||
margin 0 auto
|
||||
background #fff
|
||||
border solid 1px rgba(#000, 0.1)
|
||||
border-radius 6px
|
||||
overflow hidden
|
||||
|
||||
> .posted
|
||||
display block
|
||||
margin 0
|
||||
padding 64px
|
||||
text-align center
|
||||
|
||||
> button
|
||||
display block
|
||||
margin 16px auto
|
||||
</style>
|
128
src/client/app/desktop/views/pages/tag.vue
Normal file
128
src/client/app/desktop/views/pages/tag.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<mk-ui>
|
||||
<header :class="$style.header">
|
||||
<h1>#{{ $route.params.tag }}</h1>
|
||||
</header>
|
||||
<div :class="$style.loading" v-if="fetching">
|
||||
<mk-ellipsis-icon/>
|
||||
</div>
|
||||
<p :class="$style.empty" v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
|
||||
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
|
||||
</mk-ui>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import Progress from '../../../common/scripts/loading';
|
||||
|
||||
const limit = 20;
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
existMore: false,
|
||||
offset: 0,
|
||||
empty: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
$route: 'fetch'
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||
|
||||
this.fetch();
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||
window.removeEventListener('scroll', this.onScroll);
|
||||
},
|
||||
methods: {
|
||||
onDocumentKeydown(e) {
|
||||
if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
|
||||
if (e.which == 84) { // t
|
||||
(this.$refs.timeline as any).focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
this.fetching = true;
|
||||
Progress.start();
|
||||
|
||||
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
|
||||
(this as any).api('notes/search_by_tag', {
|
||||
limit: limit + 1,
|
||||
offset: this.offset,
|
||||
tag: this.$route.params.tag
|
||||
}).then(notes => {
|
||||
if (notes.length == 0) this.empty = true;
|
||||
if (notes.length == limit + 1) {
|
||||
notes.pop();
|
||||
this.existMore = true;
|
||||
}
|
||||
res(notes);
|
||||
this.fetching = false;
|
||||
Progress.done();
|
||||
}, rej);
|
||||
}));
|
||||
},
|
||||
more() {
|
||||
this.offset += limit;
|
||||
|
||||
const promise = (this as any).api('notes/search_by_tag', {
|
||||
limit: limit + 1,
|
||||
offset: this.offset,
|
||||
tag: this.$route.params.tag
|
||||
});
|
||||
|
||||
promise.then(notes => {
|
||||
if (notes.length == limit + 1) {
|
||||
notes.pop();
|
||||
} else {
|
||||
this.existMore = false;
|
||||
}
|
||||
notes.forEach(n => (this.$refs.timeline as any).append(n));
|
||||
this.moreFetching = false;
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" module>
|
||||
.header
|
||||
width 100%
|
||||
max-width 600px
|
||||
margin 0 auto
|
||||
color #555
|
||||
|
||||
.notes
|
||||
width 600px
|
||||
margin 0 auto
|
||||
border solid 1px rgba(#000, 0.075)
|
||||
border-radius 6px
|
||||
overflow hidden
|
||||
|
||||
.loading
|
||||
padding 64px 0
|
||||
|
||||
.empty
|
||||
display block
|
||||
margin 0 auto
|
||||
padding 32px
|
||||
max-width 400px
|
||||
text-align center
|
||||
color #999
|
||||
|
||||
> [data-fa]
|
||||
display block
|
||||
margin-bottom 16px
|
||||
font-size 3em
|
||||
color #ccc
|
||||
|
||||
</style>
|
@ -1,59 +1,80 @@
|
||||
<template>
|
||||
<div class="mk-welcome">
|
||||
<img ref="pointer" class="pointer" src="/assets/pointer.png" alt="">
|
||||
<button @click="dark">
|
||||
<template v-if="$store.state.device.darkmode">%fa:moon%</template>
|
||||
<template v-else>%fa:R moon%</template>
|
||||
</button>
|
||||
<main v-if="about" class="about">
|
||||
<article>
|
||||
<h1>%i18n:common.about-title%</h1>
|
||||
<p v-html="'%i18n:common.about%'"></p>
|
||||
<span class="gotit" @click="about = false">%i18n:@gotit%</span>
|
||||
</article>
|
||||
</main>
|
||||
<main v-else class="index">
|
||||
<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" alt="Misskey">
|
||||
<p class="desc"><b>%i18n:common.misskey%</b> - <span @click="about = true">%i18n:@about%</span></p>
|
||||
<p class="account">
|
||||
<button class="signup" @click="signup">%i18n:@signup-button%</button>
|
||||
<button class="signin" @click="signin">%i18n:@signin-button%</button>
|
||||
</p>
|
||||
|
||||
<div class="tl">
|
||||
<header>%fa:comments R% %i18n:@timeline%<div><span></span><span></span><span></span></div></header>
|
||||
<mk-welcome-timeline/>
|
||||
<div class="body" :style="{ backgroundImage: `url('${ welcomeBgUrl }')` }">
|
||||
<div class="container">
|
||||
<main>
|
||||
<div class="about">
|
||||
<h1 v-if="name">{{ name }}</h1>
|
||||
<h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" alt="Misskey"></h1>
|
||||
<p class="powerd-by" v-if="name">powerd by <b>Misskey</b></p>
|
||||
<p class="desc" v-html="description || '%i18n:common.about%'"></p>
|
||||
<a ref="signup" @click="signup">%i18n:@signup%</a>
|
||||
</div>
|
||||
<div class="login">
|
||||
<mk-signin/>
|
||||
</div>
|
||||
</main>
|
||||
<div class="info">
|
||||
<span>%i18n:common.misskey% <b>{{ host }}</b></span>
|
||||
<span class="stats" v-if="stats">
|
||||
<span>%fa:user% {{ stats.originalUsersCount | number }}</span>
|
||||
<span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<mk-nav class="nav"/>
|
||||
</div>
|
||||
</main>
|
||||
<mk-forkit/>
|
||||
<footer>
|
||||
<div>
|
||||
<mk-nav :class="$style.nav"/>
|
||||
<p class="c">{{ copyright }}</p>
|
||||
</div>
|
||||
</footer>
|
||||
<mk-forkit class="forkit"/>
|
||||
<img src="assets/title.dark.svg" alt="Misskey">
|
||||
</div>
|
||||
<div class="tl">
|
||||
<mk-welcome-timeline/>
|
||||
</div>
|
||||
<modal name="signup" width="500px" height="auto" scrollable>
|
||||
<header :class="$style.signupFormHeader">%i18n:@signup%</header>
|
||||
<mk-signup :class="$style.signupForm"/>
|
||||
</modal>
|
||||
<modal name="signin" width="500px" height="auto" scrollable>
|
||||
<header :class="$style.signinFormHeader">%i18n:@signin%</header>
|
||||
<mk-signin :class="$style.signinForm"/>
|
||||
</modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { copyright } from '../../../config';
|
||||
import { host, name, description, copyright, welcomeBgUrl } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
about: false,
|
||||
copyright
|
||||
stats: null,
|
||||
copyright,
|
||||
welcomeBgUrl,
|
||||
host,
|
||||
name,
|
||||
description,
|
||||
pointerInterval: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
(this as any).api('stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.point();
|
||||
this.pointerInterval = setInterval(this.point, 100);
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.pointerInterval);
|
||||
},
|
||||
methods: {
|
||||
point() {
|
||||
const x = this.$refs.signup.getBoundingClientRect();
|
||||
this.$refs.pointer.style.top = x.top + x.height + 'px';
|
||||
this.$refs.pointer.style.left = x.left + 'px';
|
||||
},
|
||||
signup() {
|
||||
this.$modal.show('signup');
|
||||
},
|
||||
@ -80,13 +101,20 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
@import '~const.styl'
|
||||
|
||||
@import url(https://fonts.googleapis.com/earlyaccess/notosansjp.css);
|
||||
|
||||
root(isDark)
|
||||
display flex
|
||||
min-height 100vh
|
||||
background-image isDark ? url('/assets/welcome-bg.dark.svg') : url('/assets/welcome-bg.light.svg')
|
||||
background-size cover
|
||||
background-position center
|
||||
|
||||
> .pointer
|
||||
display block
|
||||
position absolute
|
||||
z-index 1
|
||||
top 0
|
||||
right 0
|
||||
width 180px
|
||||
margin 0 0 0 -180px
|
||||
transform rotateY(180deg) translateX(-10px) translateY(-48px)
|
||||
pointer-events none
|
||||
|
||||
> button
|
||||
position fixed
|
||||
@ -95,140 +123,117 @@ root(isDark)
|
||||
left 0
|
||||
padding 16px
|
||||
font-size 18px
|
||||
color isDark ? #fff : #555
|
||||
color #fff
|
||||
|
||||
> main
|
||||
display none // TODO
|
||||
|
||||
> .body
|
||||
flex 1
|
||||
padding 64px 0 0 0
|
||||
text-align center
|
||||
background #578394
|
||||
background-position center
|
||||
background-size cover
|
||||
|
||||
&.about
|
||||
font-family 'Noto Sans JP'
|
||||
color isDark ? #fff : #627574
|
||||
&:before
|
||||
content ''
|
||||
display block
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
right 0
|
||||
bottom 0
|
||||
background rgba(#000, 0.5)
|
||||
|
||||
> article
|
||||
max-width 700px
|
||||
margin 42px auto 0 auto
|
||||
padding 64px 82px
|
||||
background isDark ? #282C37 : #fff
|
||||
box-shadow 0 8px 32px rgba(#000, 0.15)
|
||||
> .forkit
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
|
||||
> h1
|
||||
margin 0
|
||||
font-weight 900
|
||||
> img
|
||||
position absolute
|
||||
bottom 16px
|
||||
right 16px
|
||||
width 150px
|
||||
|
||||
> p
|
||||
margin 20px 0
|
||||
line-height 2em
|
||||
> .container
|
||||
$aboutWidth = 380px
|
||||
$loginWidth = 340px
|
||||
$width = $aboutWidth + $loginWidth
|
||||
|
||||
> .gotit
|
||||
color $theme-color
|
||||
cursor pointer
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
&.index
|
||||
color isDark ? #9aa4b3 : #555
|
||||
|
||||
> img
|
||||
width 350px
|
||||
|
||||
> .desc
|
||||
margin -12px 0 24px 0
|
||||
color isDark ? #fff : #555
|
||||
|
||||
> span
|
||||
color $theme-color
|
||||
cursor pointer
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .account
|
||||
margin 8px 0
|
||||
line-height 2em
|
||||
|
||||
button
|
||||
padding 8px 16px
|
||||
font-size inherit
|
||||
|
||||
.signup
|
||||
color $theme-color
|
||||
border solid 2px $theme-color
|
||||
border-radius 4px
|
||||
|
||||
&:focus
|
||||
box-shadow 0 0 0 3px rgba($theme-color, 0.2)
|
||||
|
||||
&:hover
|
||||
color $theme-color-foreground
|
||||
background $theme-color
|
||||
|
||||
&:active
|
||||
color $theme-color-foreground
|
||||
background darken($theme-color, 10%)
|
||||
border-color darken($theme-color, 10%)
|
||||
|
||||
.signin
|
||||
&:hover
|
||||
color isDark ? #fff : #000
|
||||
|
||||
> .tl
|
||||
margin 32px auto 0 auto
|
||||
width 410px
|
||||
text-align left
|
||||
background isDark ? #313543 : #fff
|
||||
> main
|
||||
display flex
|
||||
margin auto
|
||||
width $width
|
||||
border-radius 8px
|
||||
box-shadow 0 8px 32px rgba(#000, 0.15)
|
||||
overflow hidden
|
||||
box-shadow 0 2px 8px rgba(#000, 0.3)
|
||||
|
||||
> header
|
||||
z-index 1
|
||||
padding 12px 16px
|
||||
color isDark ? #e3e5e8 : #888d94
|
||||
box-shadow 0 1px 0px rgba(#000, 0.1)
|
||||
> .about
|
||||
width $aboutWidth
|
||||
color #444
|
||||
background #fff
|
||||
|
||||
> div
|
||||
position absolute
|
||||
top 0
|
||||
right 0
|
||||
padding inherit
|
||||
> h1
|
||||
margin 0 0 16px 0
|
||||
padding 32px 32px 0 32px
|
||||
color #444
|
||||
|
||||
> span
|
||||
display inline-block
|
||||
height 11px
|
||||
width 11px
|
||||
margin-left 6px
|
||||
border-radius 100%
|
||||
vertical-align middle
|
||||
> img
|
||||
width 170px
|
||||
vertical-align bottom
|
||||
|
||||
&:nth-child(1)
|
||||
background #5BCC8B
|
||||
> .powerd-by
|
||||
margin 16px
|
||||
opacity 0.7
|
||||
|
||||
&:nth-child(2)
|
||||
background #E6BB46
|
||||
> .desc
|
||||
margin 0
|
||||
padding 0 32px 16px 32px
|
||||
|
||||
&:nth-child(3)
|
||||
background #DF7065
|
||||
> a
|
||||
display inline-block
|
||||
margin 0 0 32px 0
|
||||
font-weight bold
|
||||
|
||||
> .mk-welcome-timeline
|
||||
max-height 350px
|
||||
overflow auto
|
||||
> .login
|
||||
width $loginWidth
|
||||
padding 16px 32px 32px 32px
|
||||
background #f5f5f5
|
||||
|
||||
> footer
|
||||
font-size 12px
|
||||
color isDark ? #949ea5 : #737c82
|
||||
> .info
|
||||
margin 16px auto
|
||||
padding 12px
|
||||
width $width
|
||||
font-size 14px
|
||||
color #fff
|
||||
background rgba(#000, 0.2)
|
||||
border-radius 8px
|
||||
|
||||
> div
|
||||
margin 0 auto
|
||||
padding 64px
|
||||
text-align center
|
||||
> .stats
|
||||
margin-left 16px
|
||||
padding-left 16px
|
||||
border-left solid 1px #fff
|
||||
|
||||
> .c
|
||||
margin 16px 0 0 0
|
||||
font-size 10px
|
||||
opacity 0.7
|
||||
> *
|
||||
margin-right 16px
|
||||
|
||||
> .nav
|
||||
display block
|
||||
margin 16px 0
|
||||
font-size 14px
|
||||
color #fff
|
||||
|
||||
> .tl
|
||||
margin 0
|
||||
width 410px
|
||||
height 100vh
|
||||
text-align left
|
||||
background isDark ? #313543 : #fff
|
||||
|
||||
> *
|
||||
max-height 100%
|
||||
overflow auto
|
||||
|
||||
.mk-welcome[data-darkmode]
|
||||
root(true)
|
||||
|
@ -32,42 +32,30 @@ body > noscript {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
cursor: wait;
|
||||
}
|
||||
#ini > p {
|
||||
display: block;
|
||||
user-select: none;
|
||||
margin: 32px;
|
||||
font-size: 4em;
|
||||
color: #555;
|
||||
#ini > svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
animation: ini 0.6s infinite linear;
|
||||
}
|
||||
#ini > p > span {
|
||||
animation: ini 1.4s infinite ease-in-out both;
|
||||
}
|
||||
#ini > p > span:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
#ini > p > span:nth-child(2) {
|
||||
animation-delay: 0.16s;
|
||||
}
|
||||
#ini > p > span:nth-child(3) {
|
||||
animation-delay: 0.32s;
|
||||
}
|
||||
|
||||
html[data-darkmode] #ini {
|
||||
background: #191b22;
|
||||
}
|
||||
html[data-darkmode] #ini > p {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@keyframes ini {
|
||||
0%, 80%, 100% {
|
||||
opacity: 1;
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
40% {
|
||||
opacity: 0;
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import Progress from './common/scripts/loading';
|
||||
import Connection from './common/scripts/streaming/stream';
|
||||
import { HomeStreamManager } from './common/scripts/streaming/home';
|
||||
import { DriveStreamManager } from './common/scripts/streaming/drive';
|
||||
import { ServerStreamManager } from './common/scripts/streaming/server';
|
||||
import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats';
|
||||
import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats';
|
||||
import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index';
|
||||
import { OthelloStreamManager } from './common/scripts/streaming/othello';
|
||||
|
||||
@ -104,14 +105,16 @@ export default class MiOS extends EventEmitter {
|
||||
localTimelineStream: LocalTimelineStreamManager;
|
||||
globalTimelineStream: GlobalTimelineStreamManager;
|
||||
driveStream: DriveStreamManager;
|
||||
serverStream: ServerStreamManager;
|
||||
serverStatsStream: ServerStatsStreamManager;
|
||||
notesStatsStream: NotesStatsStreamManager;
|
||||
messagingIndexStream: MessagingIndexStreamManager;
|
||||
othelloStream: OthelloStreamManager;
|
||||
} = {
|
||||
localTimelineStream: null,
|
||||
globalTimelineStream: null,
|
||||
driveStream: null,
|
||||
serverStream: null,
|
||||
serverStatsStream: null,
|
||||
notesStatsStream: null,
|
||||
messagingIndexStream: null,
|
||||
othelloStream: null
|
||||
};
|
||||
@ -218,7 +221,8 @@ export default class MiOS extends EventEmitter {
|
||||
this.store = initStore(this);
|
||||
|
||||
//#region Init stream managers
|
||||
this.streams.serverStream = new ServerStreamManager(this);
|
||||
this.streams.serverStatsStream = new ServerStatsStreamManager(this);
|
||||
this.streams.notesStatsStream = new NotesStatsStreamManager(this);
|
||||
|
||||
this.once('signedin', () => {
|
||||
// Init home stream manager
|
||||
|
@ -2,17 +2,11 @@
|
||||
* Mobile Client
|
||||
*/
|
||||
|
||||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
|
||||
import { MdCard, MdButton, MdField, MdMenu, MdList, MdSwitch, MdSubheader, MdDialog, MdDialogAlert, MdRadio } from 'vue-material/dist/components';
|
||||
import 'vue-material/dist/vue-material.min.css';
|
||||
import 'vue-material/dist/theme/default.css';
|
||||
|
||||
// Style
|
||||
import './style.styl';
|
||||
import '../../element.scss';
|
||||
import '../../md.scss';
|
||||
|
||||
import init from '../init';
|
||||
|
||||
@ -42,17 +36,8 @@ import MkUserLists from './views/pages/user-lists.vue';
|
||||
import MkUserList from './views/pages/user-list.vue';
|
||||
import MkSettings from './views/pages/settings.vue';
|
||||
import MkOthello from './views/pages/othello.vue';
|
||||
|
||||
Vue.use(MdCard);
|
||||
Vue.use(MdButton);
|
||||
Vue.use(MdField);
|
||||
Vue.use(MdMenu);
|
||||
Vue.use(MdList);
|
||||
Vue.use(MdSwitch);
|
||||
Vue.use(MdSubheader);
|
||||
Vue.use(MdDialog);
|
||||
Vue.use(MdDialogAlert);
|
||||
Vue.use(MdRadio);
|
||||
import MkTag from './views/pages/tag.vue';
|
||||
import MkShare from './views/pages/share.vue';
|
||||
|
||||
/**
|
||||
* init
|
||||
@ -88,6 +73,8 @@ init((launch) => {
|
||||
{ path: '/i/drive/file/:file', component: MkDrive },
|
||||
{ path: '/selectdrive', component: MkSelectDrive },
|
||||
{ path: '/search', component: MkSearch },
|
||||
{ path: '/tags/:tag', component: MkTag },
|
||||
{ path: '/share', component: MkShare },
|
||||
{ path: '/othello', name: 'othello', component: MkOthello },
|
||||
{ path: '/othello/:game', component: MkOthello },
|
||||
{ path: '/@:user', component: MkUser },
|
||||
|
@ -10,9 +10,6 @@ html
|
||||
height 100%
|
||||
background #ececed !important
|
||||
|
||||
// for md
|
||||
transition none !important
|
||||
|
||||
&[data-darkmode]
|
||||
background #191B22 !important
|
||||
|
||||
|
@ -34,15 +34,10 @@
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div>
|
||||
<a :href="`${file.url}?download`" :download="file.name">
|
||||
%fa:download%%i18n:@download%
|
||||
</a>
|
||||
<button @click="rename">
|
||||
%fa:pencil-alt%%i18n:@rename%
|
||||
</button>
|
||||
<button @click="move">
|
||||
%fa:R folder-open%%i18n:@move%
|
||||
</button>
|
||||
<a :href="`${file.url}?download`" :download="file.name">%fa:download%%i18n:@download%</a>
|
||||
<button @click="rename">%fa:pencil-alt%%i18n:@rename%</button>
|
||||
<button @click="move">%fa:R folder-open%%i18n:@move%</button>
|
||||
<button @click="del">%fa:trash-alt R%%i18n:@delete%</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="exif" v-show="exif">
|
||||
@ -112,6 +107,13 @@ export default Vue.extend({
|
||||
});
|
||||
});
|
||||
},
|
||||
del() {
|
||||
(this as any).api('drive/files/delete', {
|
||||
fileId: this.file.id
|
||||
}).then(() => {
|
||||
this.browser.cd(this.file.folderId, true);
|
||||
});
|
||||
},
|
||||
showCreatedAt() {
|
||||
alert(new Date(this.file.createdAt).toLocaleString());
|
||||
},
|
||||
|
@ -100,6 +100,7 @@ export default Vue.extend({
|
||||
|
||||
this.connection.on('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.on('file_updated', this.onStreamDriveFileUpdated);
|
||||
this.connection.on('file_deleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.on('folder_created', this.onStreamDriveFolderCreated);
|
||||
this.connection.on('folder_updated', this.onStreamDriveFolderUpdated);
|
||||
|
||||
@ -118,6 +119,7 @@ export default Vue.extend({
|
||||
beforeDestroy() {
|
||||
this.connection.off('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.off('file_updated', this.onStreamDriveFileUpdated);
|
||||
this.connection.off('file_deleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.off('folder_created', this.onStreamDriveFolderCreated);
|
||||
this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
|
||||
(this as any).os.streams.driveStream.dispose(this.connectionId);
|
||||
@ -136,6 +138,10 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
onStreamDriveFileDeleted(fileId) {
|
||||
this.removeFile(fileId);
|
||||
},
|
||||
|
||||
onStreamDriveFolderCreated(folder) {
|
||||
this.addFolder(folder, true);
|
||||
},
|
||||
|
@ -22,6 +22,7 @@ import userTimeline from './user-timeline.vue';
|
||||
import userListTimeline from './user-list-timeline.vue';
|
||||
import activity from './activity.vue';
|
||||
import widgetContainer from './widget-container.vue';
|
||||
import postForm from './post-form.vue';
|
||||
|
||||
Vue.component('mk-ui', ui);
|
||||
Vue.component('mk-note', note);
|
||||
@ -45,3 +46,4 @@ Vue.component('mk-user-timeline', userTimeline);
|
||||
Vue.component('mk-user-list-timeline', userListTimeline);
|
||||
Vue.component('mk-activity', activity);
|
||||
Vue.component('mk-widget-container', widgetContainer);
|
||||
Vue.component('mk-post-form', postForm);
|
||||
|
@ -41,7 +41,7 @@
|
||||
<mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
|
||||
</div>
|
||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
|
||||
</div>
|
||||
<div class="media" v-if="p.media.length > 0">
|
||||
<mk-media-list :media-list="p.media" :raw="true"/>
|
||||
|
@ -2,26 +2,7 @@
|
||||
<div class="mk-note-preview" :class="{ smart: $store.state.device.postStyle == 'smart' }">
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle != 'smart'"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
|
||||
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="note.user.isAdmin">%i18n:@admin%</span>
|
||||
<span class="is-bot" v-if="note.user.isBot">%i18n:@bot%</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">%i18n:@cat%</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="note" :mini="true"/>
|
||||
<div class="body">
|
||||
<mk-sub-note-content class="text" :note="note"/>
|
||||
</div>
|
||||
@ -79,64 +60,8 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
> .header
|
||||
margin-bottom 2px
|
||||
white-space nowrap
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
margin-right 8px
|
||||
width 18px
|
||||
height 18px
|
||||
border-radius 100%
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 .5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #607073
|
||||
font-size 1em
|
||||
font-weight 700
|
||||
text-align left
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 0.5em 0 0
|
||||
padding 1px 6px
|
||||
font-size 0.8em
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
margin 0 .5em 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color isDark ? #606984 : #d1d8da
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #b2b8bb
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
|
||||
|
@ -2,26 +2,7 @@
|
||||
<div class="sub" :class="{ smart: $store.state.device.postStyle == 'smart' }">
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle != 'smart'"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
|
||||
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="note.user.isAdmin">%i18n:@admin%</span>
|
||||
<span class="is-bot" v-if="note.user.isBot">%i18n:@bot%</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">%i18n:@cat%</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="note" :mini="true"/>
|
||||
<div class="body">
|
||||
<mk-sub-note-content class="text" :note="note"/>
|
||||
</div>
|
||||
@ -92,66 +73,8 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
> .header
|
||||
margin-bottom 2px
|
||||
white-space nowrap
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
margin-right 8px
|
||||
width 18px
|
||||
height 18px
|
||||
border-radius 100%
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 0.5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #607073
|
||||
font-size 1em
|
||||
font-weight 700
|
||||
text-align left
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 0.5em 0 0
|
||||
padding 1px 5px
|
||||
font-size 0.8em
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
text-align left
|
||||
margin 0
|
||||
color isDark ? #606984 : #d1d8da
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #b2b8bb
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
|
||||
|
@ -14,26 +14,7 @@
|
||||
<article>
|
||||
<mk-avatar class="avatar" :user="p.user" v-if="$store.state.device.postStyle != 'smart'"/>
|
||||
<div class="main">
|
||||
<header>
|
||||
<mk-avatar class="avatar" :user="p.user" v-if="$store.state.device.postStyle == 'smart'"/>
|
||||
<router-link class="name" :to="p.user | userPage">{{ p.user | userName }}</router-link>
|
||||
<span class="is-admin" v-if="p.user.isAdmin">admin</span>
|
||||
<span class="is-bot" v-if="p.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="p.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="p.user"/></span>
|
||||
<div class="info">
|
||||
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
|
||||
<router-link class="created-at" :to="p | notePage">
|
||||
<mk-time :time="p.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="p.visibility != 'public'">
|
||||
<template v-if="p.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="p.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="p.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="p.visibility == 'private'">%fa:lock%</template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<mk-note-header class="header" :note="p" :mini="true"/>
|
||||
<div class="body">
|
||||
<p v-if="p.cw != null" class="cw">
|
||||
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
|
||||
@ -52,7 +33,7 @@
|
||||
</div>
|
||||
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
|
||||
</div>
|
||||
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
|
||||
@ -358,65 +339,10 @@ root(isDark)
|
||||
flex 1
|
||||
min-width 0
|
||||
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
white-space nowrap
|
||||
|
||||
> .header
|
||||
@media (min-width 500px)
|
||||
margin-bottom 2px
|
||||
|
||||
> .avatar
|
||||
flex-shrink 0
|
||||
margin-right 8px
|
||||
width 20px
|
||||
height 20px
|
||||
border-radius 100%
|
||||
|
||||
> .name
|
||||
display block
|
||||
margin 0 0.5em 0 0
|
||||
padding 0
|
||||
overflow hidden
|
||||
color isDark ? #fff : #627079
|
||||
font-weight bold
|
||||
text-decoration none
|
||||
text-overflow ellipsis
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
align-self center
|
||||
margin 0 0.5em 0 0
|
||||
padding 1px 6px
|
||||
font-size 0.8em
|
||||
color isDark ? #758188 : #aaa
|
||||
border solid 1px isDark ? #57616f : #ddd
|
||||
border-radius 3px
|
||||
|
||||
&.is-admin
|
||||
border-color isDark ? #d42c41 : #f56a7b
|
||||
color isDark ? #d42c41 : #f56a7b
|
||||
|
||||
> .username
|
||||
margin 0 0.5em 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
color isDark ? #606984 : #ccc
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
> *
|
||||
color isDark ? #606984 : #c0c0c0
|
||||
|
||||
> .mobile
|
||||
margin-right 6px
|
||||
|
||||
> .visibility
|
||||
margin-left 6px
|
||||
|
||||
> .body
|
||||
@media (min-width 700px)
|
||||
font-size 1.1em
|
||||
|
@ -46,6 +46,7 @@ import * as XDraggable from 'vuedraggable';
|
||||
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
||||
import getKao from '../../../common/scripts/get-kao';
|
||||
import parse from '../../../../../text/parse';
|
||||
import { host } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
@ -53,7 +54,25 @@ export default Vue.extend({
|
||||
MkVisibilityChooser
|
||||
},
|
||||
|
||||
props: ['reply', 'renote'],
|
||||
props: {
|
||||
reply: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
renote: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
initialText: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
instant: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
@ -111,6 +130,10 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.initialText) {
|
||||
this.text = this.initialText;
|
||||
}
|
||||
|
||||
if (this.reply && this.reply.user.host != null) {
|
||||
this.text = `@${this.reply.user.username}@${this.reply.user.host} `;
|
||||
}
|
||||
@ -123,6 +146,7 @@ export default Vue.extend({
|
||||
|
||||
// 自分は除外
|
||||
if (this.$store.state.i.username == x.username && x.host == null) return;
|
||||
if (this.$store.state.i.username == x.username && x.host == host) return;
|
||||
|
||||
// 重複は除外
|
||||
if (this.text.indexOf(`${mention} `) != -1) return;
|
||||
@ -250,8 +274,10 @@ export default Vue.extend({
|
||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||
viaMobile: viaMobile
|
||||
}).then(data => {
|
||||
this.$emit('note');
|
||||
this.$destroy();
|
||||
this.$emit('posted');
|
||||
this.$nextTick(() => {
|
||||
this.$destroy();
|
||||
});
|
||||
}).catch(err => {
|
||||
this.posting = false;
|
||||
});
|
||||
|
@ -1,132 +1,84 @@
|
||||
<template>
|
||||
<mk-ui>
|
||||
<span slot="header">%fa:cog%%i18n:@settings%</span>
|
||||
<main>
|
||||
<p v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></p>
|
||||
<main :data-darkmode="$store.state.device.darkmode">
|
||||
<div class="signin-as" v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></div>
|
||||
|
||||
<div>
|
||||
<x-profile/>
|
||||
|
||||
<md-card>
|
||||
<md-card-header>
|
||||
<div class="md-title">%fa:palette% %i18n:@design%</div>
|
||||
</md-card-header>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:palette% %i18n:@design%</div>
|
||||
|
||||
<md-card-content>
|
||||
<div>
|
||||
<md-switch v-model="darkmode">%i18n:@dark-mode%</md-switch>
|
||||
</div>
|
||||
<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
|
||||
<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</md-switch>
|
||||
</div>
|
||||
<div>
|
||||
<div>%i18n:@timeline%</div>
|
||||
<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
|
||||
<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
|
||||
<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="md-body-2">%i18n:@timeline%</div>
|
||||
<div>
|
||||
<div>%i18n:@post-style%</div>
|
||||
<ui-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</ui-radio>
|
||||
<ui-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</ui-radio>
|
||||
</div>
|
||||
</ui-card>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</md-switch>
|
||||
</div>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:cog% %i18n:@behavior%</div>
|
||||
<ui-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>
|
||||
<ui-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</ui-switch>
|
||||
<ui-switch v-model="loadRawImages">%i18n:@load-raw-images%</ui-switch>
|
||||
<ui-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</ui-switch>
|
||||
<ui-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</ui-switch>
|
||||
</ui-card>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</md-switch>
|
||||
</div>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:language% %i18n:@lang%</div>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</md-switch>
|
||||
</div>
|
||||
</div>
|
||||
<ui-select v-model="lang" placeholder="%i18n:@auto%">
|
||||
<optgroup label="%i18n:@recommended%">
|
||||
<option value="">%i18n:@auto%</option>
|
||||
</optgroup>
|
||||
|
||||
<div>
|
||||
<div class="md-body-2">%i18n:@post-style%</div>
|
||||
<optgroup label="%i18n:@specify-language%">
|
||||
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
|
||||
</optgroup>
|
||||
</ui-select>
|
||||
<span>%fa:info-circle% %i18n:@lang-tip%</span>
|
||||
</ui-card>
|
||||
|
||||
<md-radio v-model="postStyle" value="standard">%i18n:@post-style-standard%</md-radio>
|
||||
<md-radio v-model="postStyle" value="smart">%i18n:@post-style-smart%</md-radio>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:B twitter% %i18n:@twitter%</div>
|
||||
|
||||
<md-card>
|
||||
<md-card-header>
|
||||
<div class="md-title">%fa:cog% %i18n:@behavior%</div>
|
||||
</md-card-header>
|
||||
<p class="account" v-if="$store.state.i.twitter"><a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
|
||||
<p>
|
||||
<a :href="`${apiUrl}/connect/twitter`" target="_blank">{{ $store.state.i.twitter ? '%i18n:@twitter-reconnect%' : '%i18n:@twitter-connect%' }}</a>
|
||||
<span v-if="$store.state.i.twitter"> or </span>
|
||||
<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter">%i18n:@twitter-disconnect%</a>
|
||||
</p>
|
||||
</ui-card>
|
||||
|
||||
<md-card-content>
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll">%i18n:@fetch-on-scroll%</md-switch>
|
||||
</div>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:sync-alt% %i18n:@update%</div>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile">%i18n:@disable-via-mobile%</md-switch>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="loadRawImages">%i18n:@load-raw-images%</md-switch>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="$store.state.settings.loadRemoteMedia" @change="onChangeLoadRemoteMedia">%i18n:@load-remote-media%</md-switch>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="lightmode">%i18n:@i-am-under-limited-internet%</md-switch>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-card-header>
|
||||
<div class="md-title">%fa:language% %i18n:@lang%</div>
|
||||
</md-card-header>
|
||||
|
||||
<md-card-content>
|
||||
<md-field>
|
||||
<md-select v-model="lang" placeholder="%i18n:@auto%">
|
||||
<md-optgroup label="%i18n:@recommended%">
|
||||
<md-option value="">%i18n:@auto%</md-option>
|
||||
</md-optgroup>
|
||||
|
||||
<md-optgroup label="%i18n:@specify-language%">
|
||||
<md-option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</md-option>
|
||||
</md-optgroup>
|
||||
</md-select>
|
||||
</md-field>
|
||||
<span class="md-helper-text">%fa:info-circle% %i18n:@lang-tip%</span>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-card-header>
|
||||
<div class="md-title">%fa:B twitter% %i18n:@twitter%</div>
|
||||
</md-card-header>
|
||||
|
||||
<md-card-content>
|
||||
<p class="account" v-if="$store.state.i.twitter"><a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
|
||||
<p>
|
||||
<a :href="`${apiUrl}/connect/twitter`" target="_blank">{{ $store.state.i.twitter ? '%i18n:@twitter-reconnect%' : '%i18n:@twitter-connect%' }}</a>
|
||||
<span v-if="$store.state.i.twitter"> or </span>
|
||||
<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter">%i18n:@twitter-disconnect%</a>
|
||||
</p>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-card-header>
|
||||
<div class="md-title">%fa:sync-alt% %i18n:@update%</div>
|
||||
</md-card-header>
|
||||
|
||||
<md-card-content>
|
||||
<div>%i18n:@version% <i>{{ version }}</i></div>
|
||||
<template v-if="latestVersion !== undefined">
|
||||
<div>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></div>
|
||||
</template>
|
||||
<md-button class="md-raised md-primary" @click="checkForUpdate" :disabled="checkingForUpdate">
|
||||
<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
|
||||
<template v-else>%i18n:@check-for-updates%</template>
|
||||
</md-button>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<div>%i18n:@version% <i>{{ version }}</i></div>
|
||||
<template v-if="latestVersion !== undefined">
|
||||
<div>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></div>
|
||||
</template>
|
||||
<ui-button @click="checkForUpdate" :disabled="checkingForUpdate">
|
||||
<template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
|
||||
<template v-else>%i18n:@check-for-updates%</template>
|
||||
</ui-button>
|
||||
</ui-card>
|
||||
</div>
|
||||
<p><small>ver {{ version }} ({{ codename }})</small></p>
|
||||
|
||||
<footer>
|
||||
<small>ver {{ version }} ({{ codename }})</small>
|
||||
</footer>
|
||||
</main>
|
||||
</mk-ui>
|
||||
</template>
|
||||
@ -267,20 +219,22 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
root(isDark)
|
||||
padding 0 16px
|
||||
margin 0 auto
|
||||
max-width 500px
|
||||
width 100%
|
||||
|
||||
> div
|
||||
> *
|
||||
margin-bottom 16px
|
||||
|
||||
> p
|
||||
display block
|
||||
margin 24px
|
||||
> .signin-as
|
||||
margin 16px
|
||||
padding 16px
|
||||
text-align center
|
||||
color isDark ? #cad2da : #a2a9b1
|
||||
color isDark ? #49ab63 : #2c662d
|
||||
background isDark ? #273c34 : #fcfff5
|
||||
box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
|
||||
|
||||
> footer
|
||||
margin 16px
|
||||
text-align center
|
||||
color isDark ? #c9d2e0 : #888
|
||||
|
||||
main[data-darkmode]
|
||||
root(true)
|
||||
|
@ -1,62 +1,49 @@
|
||||
<template>
|
||||
<md-card>
|
||||
<md-card-header>
|
||||
<div class="md-title">%fa:pencil-alt% %i18n:@title%</div>
|
||||
</md-card-header>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:user% %i18n:@title%</div>
|
||||
|
||||
<md-card-content>
|
||||
<md-field>
|
||||
<label>%i18n:@name%</label>
|
||||
<md-input v-model="name" :disabled="saving" md-counter="30"/>
|
||||
</md-field>
|
||||
<ui-form :disabled="saving">
|
||||
<ui-input v-model="name" :max="30">
|
||||
<span>%i18n:@name%</span>
|
||||
</ui-input>
|
||||
|
||||
<md-field>
|
||||
<label>%i18n:@account%</label>
|
||||
<span class="md-prefix">@</span>
|
||||
<md-input v-model="username" readonly></md-input>
|
||||
<span class="md-suffix">@{{ host }}</span>
|
||||
</md-field>
|
||||
<ui-input v-model="username" readonly>
|
||||
<span>%i18n:@account%</span>
|
||||
<span slot="prefix">@</span>
|
||||
<span slot="suffix">@{{ host }}</span>
|
||||
</ui-input>
|
||||
|
||||
<md-field>
|
||||
<md-icon>%fa:map-marker-alt%</md-icon>
|
||||
<label>%i18n:@location%</label>
|
||||
<md-input v-model="location" :disabled="saving"/>
|
||||
</md-field>
|
||||
<ui-input v-model="location">
|
||||
<span>%i18n:@location%</span>
|
||||
<span slot="prefix">%fa:map-marker-alt%</span>
|
||||
</ui-input>
|
||||
|
||||
<md-field>
|
||||
<md-icon>%fa:birthday-cake%</md-icon>
|
||||
<label>%i18n:@birthday%</label>
|
||||
<md-input type="date" v-model="birthday" :disabled="saving"/>
|
||||
</md-field>
|
||||
<ui-input v-model="birthday" type="date">
|
||||
<span>%i18n:@birthday%</span>
|
||||
<span slot="prefix">%fa:birthday-cake%</span>
|
||||
</ui-input>
|
||||
|
||||
<md-field>
|
||||
<label>%i18n:@description%</label>
|
||||
<md-textarea v-model="description" :disabled="saving" md-counter="500"/>
|
||||
</md-field>
|
||||
<ui-textarea v-model="description" :max="500">
|
||||
<span>%i18n:@description%</span>
|
||||
</ui-textarea>
|
||||
|
||||
<md-field>
|
||||
<label>%i18n:@avatar%</label>
|
||||
<md-file @md-change="onAvatarChange"/>
|
||||
</md-field>
|
||||
<ui-input type="file" @change="onAvatarChange">
|
||||
<span>%i18n:@avatar%</span>
|
||||
<span slot="icon">%fa:image%</span>
|
||||
<span slot="text" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span>
|
||||
</ui-input>
|
||||
|
||||
<md-field>
|
||||
<label>%i18n:@banner%</label>
|
||||
<md-file @md-change="onBannerChange"/>
|
||||
</md-field>
|
||||
<ui-input type="file" @change="onBannerChange">
|
||||
<span>%i18n:@banner%</span>
|
||||
<span slot="icon">%fa:image%</span>
|
||||
<span slot="text" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span>
|
||||
</ui-input>
|
||||
|
||||
<md-dialog-alert
|
||||
:md-active.sync="uploading"
|
||||
md-content="%18n:!@uploading%"/>
|
||||
<ui-switch v-model="isCat">%i18n:@is-cat%</ui-switch>
|
||||
|
||||
<div>
|
||||
<md-switch v-model="isCat">%i18n:@is-cat%</md-switch>
|
||||
</div>
|
||||
</md-card-content>
|
||||
|
||||
<md-card-actions>
|
||||
<md-button class="md-primary" :disabled="saving" @click="save">%i18n:@save%</md-button>
|
||||
</md-card-actions>
|
||||
</md-card>
|
||||
<ui-button @click="save">%i18n:@save%</ui-button>
|
||||
</ui-form>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -77,7 +64,8 @@ export default Vue.extend({
|
||||
isBot: false,
|
||||
isCat: false,
|
||||
saving: false,
|
||||
uploading: false
|
||||
avatarUploading: false,
|
||||
bannerUploading: false
|
||||
};
|
||||
},
|
||||
|
||||
@ -95,7 +83,7 @@ export default Vue.extend({
|
||||
|
||||
methods: {
|
||||
onAvatarChange([file]) {
|
||||
this.uploading = true;
|
||||
this.avatarUploading = true;
|
||||
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
@ -108,16 +96,16 @@ export default Vue.extend({
|
||||
.then(response => response.json())
|
||||
.then(f => {
|
||||
this.avatarId = f.id;
|
||||
this.uploading = false;
|
||||
this.avatarUploading = false;
|
||||
})
|
||||
.catch(e => {
|
||||
this.uploading = false;
|
||||
this.avatarUploading = false;
|
||||
alert('%18n:!@upload-failed%');
|
||||
});
|
||||
},
|
||||
|
||||
onBannerChange([file]) {
|
||||
this.uploading = true;
|
||||
this.bannerUploading = true;
|
||||
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
@ -130,10 +118,10 @@ export default Vue.extend({
|
||||
.then(response => response.json())
|
||||
.then(f => {
|
||||
this.bannerId = f.id;
|
||||
this.uploading = false;
|
||||
this.bannerUploading = false;
|
||||
})
|
||||
.catch(e => {
|
||||
this.uploading = false;
|
||||
this.bannerUploading = false;
|
||||
alert('%18n:!@upload-failed%');
|
||||
});
|
||||
},
|
||||
|
56
src/client/app/mobile/views/pages/share.vue
Normal file
56
src/client/app/mobile/views/pages/share.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="azibmfpleajagva420swmu4c3r7ni7iw">
|
||||
<h1>Misskeyで共有</h1>
|
||||
<div>
|
||||
<mk-signin v-if="!$store.getters.isSignedIn"/>
|
||||
<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
|
||||
<p v-if="posted" class="posted">%fa:check%</p>
|
||||
</div>
|
||||
<ui-button class="close" v-if="posted" @click="close">閉じる</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
posted: false,
|
||||
text: new URLSearchParams(location.search).get('text')
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.azibmfpleajagva420swmu4c3r7ni7iw
|
||||
> h1
|
||||
margin 8px 0
|
||||
color #555
|
||||
font-size 20px
|
||||
text-align center
|
||||
|
||||
> div
|
||||
max-width 500px
|
||||
margin 0 auto
|
||||
|
||||
> .posted
|
||||
display block
|
||||
margin 0 auto
|
||||
padding 64px
|
||||
text-align center
|
||||
background #fff
|
||||
border-radius 6px
|
||||
width calc(100% - 32px)
|
||||
|
||||
> .close
|
||||
display block
|
||||
margin 16px auto
|
||||
width calc(100% - 32px)
|
||||
</style>
|
@ -1,57 +1,26 @@
|
||||
<template>
|
||||
<div class="signup">
|
||||
<h1>Misskeyをはじめる</h1>
|
||||
<p>いつでも、どこからでもMisskeyを利用できます。もちろん、無料です。</p>
|
||||
<div class="form">
|
||||
<p>新規登録</p>
|
||||
<div>
|
||||
<mk-signup/>
|
||||
</div>
|
||||
</div>
|
||||
<h1>📦 始めましょう</h1>
|
||||
<mk-signup/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
mounted() {
|
||||
document.documentElement.style.background = '#293946';
|
||||
}
|
||||
});
|
||||
export default Vue.extend({});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.signup
|
||||
padding 16px
|
||||
padding 32px
|
||||
margin 0 auto
|
||||
max-width 500px
|
||||
|
||||
h1
|
||||
margin 0
|
||||
padding 8px
|
||||
padding 8px 0 0 0
|
||||
font-size 1.5em
|
||||
font-weight normal
|
||||
color #c3c6ca
|
||||
|
||||
& + p
|
||||
margin 0 0 16px 0
|
||||
padding 0 8px 0 8px
|
||||
color #949fa9
|
||||
|
||||
.form
|
||||
background #fff
|
||||
border solid 1px rgba(#000, 0.2)
|
||||
border-radius 8px
|
||||
overflow hidden
|
||||
|
||||
> p
|
||||
margin 0
|
||||
padding 12px 20px
|
||||
color #555
|
||||
background #f5f5f5
|
||||
border-bottom solid 1px #ddd
|
||||
|
||||
> div
|
||||
padding 16px
|
||||
font-weight bold
|
||||
color #444
|
||||
|
||||
</style>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user