Compare commits
83 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
86b0dfdd33 | ||
![]() |
ab04f2fce0 | ||
![]() |
be9f836b21 | ||
![]() |
818bc96aab | ||
![]() |
14d12c21f2 | ||
![]() |
2053a041e5 | ||
![]() |
0534a0a41e | ||
![]() |
0ac5fdab49 | ||
![]() |
39099909bf | ||
![]() |
999ce8e366 | ||
![]() |
8678e30cc8 | ||
![]() |
8a59e9d9c8 | ||
![]() |
dddace9d6a | ||
![]() |
388cb7db3a | ||
![]() |
46b74b3e1c | ||
![]() |
d53e80c88a | ||
![]() |
d8a8f36676 | ||
![]() |
dafdbbf552 | ||
![]() |
52bc52293b | ||
![]() |
0733aefb64 | ||
![]() |
aac6dec5da | ||
![]() |
d44c59ea3e | ||
![]() |
9b3c3881c4 | ||
![]() |
cdd722dca0 | ||
![]() |
9ad7a80496 | ||
![]() |
b85597b15d | ||
![]() |
ebb98d975b | ||
![]() |
c1b320710b | ||
![]() |
1201794bef | ||
![]() |
dc58c9bd2f | ||
![]() |
9787da7240 | ||
![]() |
b0f989dbac | ||
![]() |
a0ec6b8ea7 | ||
![]() |
fac6868305 | ||
![]() |
ed8fa59639 | ||
![]() |
e8edda01a9 | ||
![]() |
380a369eca | ||
![]() |
781fffee42 | ||
![]() |
69b5de3346 | ||
![]() |
0d8c83f27c | ||
![]() |
8ca58de2ba | ||
![]() |
d8cd24fab0 | ||
![]() |
f918081168 | ||
![]() |
f88fb9bc1d | ||
![]() |
062fbd7d27 | ||
![]() |
6b6af008d0 | ||
![]() |
4d35def548 | ||
![]() |
b369d6bd5c | ||
![]() |
63dfe2726c | ||
![]() |
1002d29cc2 | ||
![]() |
868240666a | ||
![]() |
02a88fdc9c | ||
![]() |
bc4adf7107 | ||
![]() |
bd67785802 | ||
![]() |
68c90e8ebe | ||
![]() |
64519a9fd4 | ||
![]() |
d21da0211c | ||
![]() |
2e919b788f | ||
![]() |
2d2056f2bd | ||
![]() |
334dabc1de | ||
![]() |
dfa2c951d6 | ||
![]() |
e28d1c7569 | ||
![]() |
9ce0f96de3 | ||
![]() |
a408b19bbe | ||
![]() |
f9a17b8021 | ||
![]() |
5eeb200913 | ||
![]() |
f87981eeee | ||
![]() |
761ae807db | ||
![]() |
643a0e6b13 | ||
![]() |
e7e5f76e9e | ||
![]() |
247acd81a9 | ||
![]() |
a2457a6ac4 | ||
![]() |
af7a320493 | ||
![]() |
4dd8b7e85d | ||
![]() |
3a4392af40 | ||
![]() |
44f70f0009 | ||
![]() |
238c4cf181 | ||
![]() |
9171c49d85 | ||
![]() |
5e967e24ff | ||
![]() |
70ac07d60e | ||
![]() |
81ee670dc2 | ||
![]() |
faf215685b | ||
![]() |
83e9711274 |
@@ -20,9 +20,10 @@ ultimately sophisticated new type of mini-blog based SNS.
|
|||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
* Reactions
|
* Reactions
|
||||||
* User lists
|
* User lists
|
||||||
|
* Cusromizable column view (known as MisskeyDeck)
|
||||||
* Private messages
|
* Private messages
|
||||||
* Mute
|
* Mute
|
||||||
* Real time contents
|
* Streaming
|
||||||
* ActivityPub compatible
|
* ActivityPub compatible
|
||||||
|
|
||||||
and more! You can see it with your own eyes at [misskey.xyz](https://misskey.xyz).
|
and more! You can see it with your own eyes at [misskey.xyz](https://misskey.xyz).
|
||||||
|
@@ -3,16 +3,21 @@ const User = require('../built/models/user').default;
|
|||||||
|
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
const userId = new mongo.ObjectID(args[0]);
|
const user = args[0];
|
||||||
|
|
||||||
console.log(`Suspending ${userId}...`);
|
const q = user.startsWith('@') ? {
|
||||||
|
username: user.split('@')[1],
|
||||||
|
host: user.split('@')[2] || null
|
||||||
|
} : { _id: new mongo.ObjectID(user) };
|
||||||
|
|
||||||
User.update({ _id: userId }, {
|
console.log(`Suspending ${user}...`);
|
||||||
|
|
||||||
|
User.update(q, {
|
||||||
$set: {
|
$set: {
|
||||||
isSuspended: true
|
isSuspended: true
|
||||||
}
|
}
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
console.log(`Suspended ${userId}`);
|
console.log(`Suspended ${user}`);
|
||||||
}, e => {
|
}, e => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});
|
});
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "Spenden"
|
donation: "Spenden"
|
||||||
nav: "Navigation"
|
nav: "Navigation"
|
||||||
tips: "Tipps"
|
tips: "Tipps"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "Verbindung zum Server ist fehlgeschlagen"
|
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."
|
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."
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "Eine Datei hochladen"
|
upload: "Eine Datei hochladen"
|
||||||
url-upload: "Von einer URL hochladen"
|
url-upload: "Von einer URL hochladen"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "Verdunkeln"
|
dark: "Verdunkeln"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "Home"
|
home: "Home"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "Nachrichten"
|
messaging: "Nachrichten"
|
||||||
game: "Spielen"
|
game: "Spielen"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,9 +69,20 @@ common:
|
|||||||
donation: "Donation"
|
donation: "Donation"
|
||||||
nav: "Navigation"
|
nav: "Navigation"
|
||||||
tips: "Tips"
|
tips: "Tips"
|
||||||
|
deck:
|
||||||
|
widgets: "Widgets"
|
||||||
|
home: "Home"
|
||||||
|
local: "Local"
|
||||||
|
global: "Global"
|
||||||
|
notifications: "Notifications"
|
||||||
|
list: "List"
|
||||||
|
swap-left: "Move Left"
|
||||||
|
swap-right: "Move Right"
|
||||||
|
remove: "Remove"
|
||||||
|
add-column: "Add a column"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "Unable to connect to the server"
|
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."
|
description: "There is a problem either with your Internet connection, or the server may be down or under maintenance. Please {try again} later."
|
||||||
thanks: "Thank you for using Misskey."
|
thanks: "Thank you for using Misskey."
|
||||||
troubleshoot: "Troubleshoot"
|
troubleshoot: "Troubleshoot"
|
||||||
common/views/components/connect-failed.troubleshooter.vue:
|
common/views/components/connect-failed.troubleshooter.vue:
|
||||||
@@ -79,7 +90,7 @@ common/views/components/connect-failed.troubleshooter.vue:
|
|||||||
network: "Network connection"
|
network: "Network connection"
|
||||||
checking-network: "Checking network connection"
|
checking-network: "Checking network connection"
|
||||||
internet: "Internet connection"
|
internet: "Internet connection"
|
||||||
checking-internet: "Checking internet connection"
|
checking-internet: "Checking Internet connection"
|
||||||
server: "Server connection"
|
server: "Server connection"
|
||||||
checking-server: "Checking server connection"
|
checking-server: "Checking server connection"
|
||||||
finding: "Searching for issues"
|
finding: "Searching for issues"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "Upload a file"
|
upload: "Upload a file"
|
||||||
url-upload: "Upload from a URL"
|
url-upload: "Upload from a URL"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "Following"
|
following: "Following"
|
||||||
follow: "Follow"
|
follow: "Follow"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "Follow request"
|
follow-request: "Follow request"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "Fall in dark"
|
dark: "Fall in dark"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "Home"
|
home: "Home"
|
||||||
|
deck: "Deck"
|
||||||
messaging: "Messages"
|
messaging: "Messages"
|
||||||
game: "Play Othello"
|
game: "Play Othello"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "Hash (md5)"
|
hash: "Hash (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "Following"
|
following: "Following"
|
||||||
follow: "Follow"
|
follow: "Follow"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "Follow request"
|
follow-request: "Follow request"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "ファイルをアップロード"
|
upload: "ファイルをアップロード"
|
||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "闇に飲まれる"
|
dark: "闇に飲まれる"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "Impossible de se connecter au server."
|
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."
|
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."
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "Uploader un fichier"
|
upload: "Uploader un fichier"
|
||||||
url-upload: "Uploader d'un URL"
|
url-upload: "Uploader d'un URL"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "Fall in dark"
|
dark: "Fall in dark"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "Accueil"
|
home: "Accueil"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "Messages"
|
messaging: "Messages"
|
||||||
game: "Jeux"
|
game: "Jeux"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "Hash (md5)"
|
hash: "Hash (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "Suivre"
|
follow: "Suivre"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "ファイルをアップロード"
|
upload: "ファイルをアップロード"
|
||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "闇に飲まれる"
|
dark: "闇に飲まれる"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -76,6 +76,19 @@ common:
|
|||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
|
rename: "名前を変更"
|
||||||
|
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -331,7 +344,7 @@ desktop/views/components/drive.vue:
|
|||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
|
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -606,6 +619,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
|
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
|
|
||||||
@@ -780,7 +794,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
|
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "ファイルをアップロード"
|
upload: "ファイルをアップロード"
|
||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "闇に飲まれる"
|
dark: "闇に飲まれる"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "Dotacje"
|
donation: "Dotacje"
|
||||||
nav: "Nawigacja"
|
nav: "Nawigacja"
|
||||||
tips: "Wskazówki"
|
tips: "Wskazówki"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "Nie udało się połączyć z serwerem"
|
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."
|
description: "Wystąpił problem z Twoim połączeniem z Internetem, lub z serwerem. {Spróbuj ponownie} wkrótce."
|
||||||
@@ -290,10 +301,10 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "Wyślij plik"
|
upload: "Wyślij plik"
|
||||||
url-upload: "Wyślij z adresu URL"
|
url-upload: "Wyślij z adresu URL"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "Śledź"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "Oczekiwanie na pozwolenie"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "Poproś o śledzenie"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "Śledzący"
|
followers: "Śledzący"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@@ -516,13 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "Dysk"
|
drive: "Dysk"
|
||||||
favorites: "Ulubione"
|
favorites: "Ulubione"
|
||||||
lists: "Listy"
|
lists: "Listy"
|
||||||
follow-requests: "フォロー申請"
|
follow-requests: "Prośby o śledzenie"
|
||||||
customize: "Dostosuj"
|
customize: "Dostosuj"
|
||||||
settings: "Ustawienia"
|
settings: "Ustawienia"
|
||||||
signout: "Wyloguj się"
|
signout: "Wyloguj się"
|
||||||
dark: "Sprowadź ciemność"
|
dark: "Sprowadź ciemność"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "Strona główna"
|
home: "Strona główna"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "Wiadomości"
|
messaging: "Wiadomości"
|
||||||
game: "Gra"
|
game: "Gra"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -532,11 +544,11 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "Szukaj"
|
placeholder: "Szukaj"
|
||||||
desktop/views/components/received-follow-requests-window.vue:
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
title: "フォロー申請"
|
title: "Poproś o śledzenie"
|
||||||
accept: "承認"
|
accept: "Zatwierdź"
|
||||||
reject: "拒否"
|
reject: "Odmów"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
title: "リスト"
|
title: "Listy"
|
||||||
create-list: "Utwórz listę"
|
create-list: "Utwórz listę"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "Wpisy"
|
notes: "Wpisy"
|
||||||
@@ -661,10 +673,10 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "Hash (md5)"
|
hash: "Hash (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "Śledź"
|
follow: "Śledź"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "Oczekiwanie na pozwolenie"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "Poproś o śledzenie"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "Zacznij śledzić ludzi takich jak Ty"
|
title: "Zacznij śledzić ludzi takich jak Ty"
|
||||||
empty: "Nie znaleziono podobnych użytkowników."
|
empty: "Nie znaleziono podobnych użytkowników."
|
||||||
@@ -722,7 +734,7 @@ mobile/views/components/ui.nav.vue:
|
|||||||
timeline: "Oś czasu"
|
timeline: "Oś czasu"
|
||||||
notifications: "Powiadomienia"
|
notifications: "Powiadomienia"
|
||||||
messaging: "Wiadomości"
|
messaging: "Wiadomości"
|
||||||
follow-requests: "フォロー申請"
|
follow-requests: "Prośby o śledzenie"
|
||||||
search: "Szukaj"
|
search: "Szukaj"
|
||||||
drive: "Dysk"
|
drive: "Dysk"
|
||||||
favorites: "Ulubione"
|
favorites: "Ulubione"
|
||||||
@@ -761,9 +773,9 @@ mobile/views/pages/messaging.vue:
|
|||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "Wiadomości"
|
messaging: "Wiadomości"
|
||||||
mobile/views/pages/received-follow-requests.vue:
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
title: "フォロー申請"
|
title: "Prośby o śledzenie"
|
||||||
accept: "承認"
|
accept: "Zatwierdź"
|
||||||
reject: "拒否"
|
reject: "Odmów"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "Wpis"
|
title: "Wpis"
|
||||||
prev: "Poprzedni wpis"
|
prev: "Poprzedni wpis"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "ファイルをアップロード"
|
upload: "ファイルをアップロード"
|
||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "闇に飲まれる"
|
dark: "闇に飲まれる"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "ファイルをアップロード"
|
upload: "ファイルをアップロード"
|
||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "闇に飲まれる"
|
dark: "闇に飲まれる"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -69,6 +69,17 @@ common:
|
|||||||
donation: "寄付のお願い"
|
donation: "寄付のお願い"
|
||||||
nav: "ナビゲーション"
|
nav: "ナビゲーション"
|
||||||
tips: "ヒント"
|
tips: "ヒント"
|
||||||
|
deck:
|
||||||
|
widgets: "ウィジェット"
|
||||||
|
home: "ホーム"
|
||||||
|
local: "ローカル"
|
||||||
|
global: "グローバル"
|
||||||
|
notifications: "通知"
|
||||||
|
list: "リスト"
|
||||||
|
swap-left: "左に移動"
|
||||||
|
swap-right: "右に移動"
|
||||||
|
remove: "カラムを削除"
|
||||||
|
add-column: "カラムを追加"
|
||||||
common/views/components/connect-failed.vue:
|
common/views/components/connect-failed.vue:
|
||||||
title: "サーバーに接続できません"
|
title: "サーバーに接続できません"
|
||||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||||
@@ -290,7 +301,7 @@ desktop/views/components/drive.vue:
|
|||||||
upload: "ファイルをアップロード"
|
upload: "ファイルをアップロード"
|
||||||
url-upload: "URLからアップロード"
|
url-upload: "URLからアップロード"
|
||||||
desktop/views/components/follow-button.vue:
|
desktop/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
@@ -523,6 +534,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
dark: "闇に飲まれる"
|
dark: "闇に飲まれる"
|
||||||
desktop/views/components/ui.header.nav.vue:
|
desktop/views/components/ui.header.nav.vue:
|
||||||
home: "ホーム"
|
home: "ホーム"
|
||||||
|
deck: "デッキ"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
desktop/views/components/ui.header.notifications.vue:
|
desktop/views/components/ui.header.notifications.vue:
|
||||||
@@ -661,7 +673,7 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "ハッシュ (md5)"
|
hash: "ハッシュ (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
unfollow: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
follow-request: "フォロー申請"
|
follow-request: "フォロー申請"
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "2.25.1",
|
"version": "2.29.0",
|
||||||
"clientVersion": "1.0.6112",
|
"clientVersion": "1.0.6195",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
1
src/client/app/auth/assets/icon.svg
Normal file
1
src/client/app/auth/assets/icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="_clipPath_P6eAE2OaBltOJ3gHGVajfqsOnfv4xIns"><rect width="512" height="512"/></clipPath></defs><g clip-path="url(#_clipPath_P6eAE2OaBltOJ3gHGVajfqsOnfv4xIns)"><clipPath id="_clipPath_P6q7MZAUp3XpQhVgs2GuAbegX9v4gkom"><rect x="0" y="0" width="512" height="512" transform="matrix(1,0,0,1,0,0)" fill="rgb(255,255,255)"/></clipPath><g clip-path="url(#_clipPath_P6q7MZAUp3XpQhVgs2GuAbegX9v4gkom)"><g id="Group"><g id="g4502"><g id="g5125"><g id="text4489"><path d=" M 190.093 359.243 C 167.923 359.32 148.881 345.963 139.9 330.409 C 135.104 323.615 125.617 321.198 125.482 330.409 L 125.482 372.939 C 125.482 390.026 119.253 404.799 106.794 417.258 C 94.69 429.362 79.917 435.413 62.474 435.413 C 45.387 435.413 30.614 429.362 18.155 417.258 C 6.052 404.799 0 390.026 0 372.939 L 0 139.061 C 0 125.89 3.738 113.965 11.213 103.285 C 19.045 92.25 29.012 84.596 41.116 80.325 C 47.879 77.833 54.999 76.587 62.474 76.587 C 81.697 76.587 97.716 84.062 110.531 99.013 C 117.295 106.489 121.211 110.405 122.279 110.761 C 122.279 110.761 173.043 172.145 174.467 173.213 C 175.891 174.281 180.073 182.446 190.093 182.446 C 200.112 182.446 204.829 174.281 206.253 173.213 C 207.676 172.145 258.44 110.761 258.44 110.761 C 258.796 111.117 262.534 107.201 269.654 99.013 C 282.825 84.062 299.022 76.587 318.245 76.587 C 325.364 76.587 332.484 77.833 339.603 80.325 C 351.707 84.596 361.496 92.25 368.972 103.285 C 376.803 113.965 380.719 125.89 380.719 139.061 L 380.719 372.939 C 380.719 390.026 374.489 404.799 362.03 417.258 C 349.927 429.362 335.154 435.413 317.711 435.413 C 300.624 435.413 285.851 429.362 273.391 417.258 C 261.288 404.799 255.237 390.026 255.237 372.939 L 255.237 330.409 C 254.184 318.802 243.925 326.116 240.285 330.409 C 230.674 348.208 212.262 359.167 190.093 359.243 Z M 457.535 184.448 Q 435.109 184.448 419.09 168.963 Q 403.605 152.944 403.605 130.518 Q 403.605 108.091 419.09 92.606 Q 435.109 76.587 457.535 76.587 Q 479.962 76.587 495.981 92.606 Q 512 108.091 512 130.518 Q 512 152.944 495.981 168.963 Q 479.962 184.448 457.535 184.448 Z M 458.069 195.128 Q 480.496 195.128 495.981 211.147 Q 512 227.166 512 249.592 L 512 381.482 Q 512 403.909 495.981 419.928 Q 480.496 435.413 458.069 435.413 Q 435.643 435.413 419.624 419.928 Q 403.605 403.909 403.605 381.482 L 403.605 249.592 Q 403.605 227.166 419.624 211.147 Q 435.643 195.128 458.069 195.128 Z " fill-rule="evenodd" fill="rgb(157,157,157)"/></g></g></g></g></g></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
|
||||||
y="0px" width="1024px" height="512px" viewBox="0 256 1024 512" enable-background="new 0 256 1024 512" xml:space="preserve">
|
|
||||||
<polyline opacity="0.5" fill="none" stroke="#000000" stroke-width="34" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
|
||||||
896.5,608.5 800.5,416.5 704.5,608.5 608.5,416.5 512.5,608.5 416.5,416.5 320.5,608.5 224.5,416.5 128.5,608.5 "/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 646 B |
@@ -27,7 +27,7 @@
|
|||||||
<h1>サインインしてください</h1>
|
<h1>サインインしてください</h1>
|
||||||
<mk-signin/>
|
<mk-signin/>
|
||||||
</main>
|
</main>
|
||||||
<footer><img src="/assets/auth/logo.svg" alt="Misskey"/></footer>
|
<footer><img src="/assets/auth/icon.svg" alt="Misskey"/></footer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -144,8 +144,8 @@ export default Vue.extend({
|
|||||||
> footer
|
> footer
|
||||||
> img
|
> img
|
||||||
display block
|
display block
|
||||||
width 64px
|
width 32px
|
||||||
height 64px
|
height 32px
|
||||||
margin 0 auto
|
margin 16px auto
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -9,9 +9,9 @@ export default function<T extends object>(data: {
|
|||||||
widget: {
|
widget: {
|
||||||
type: Object
|
type: Object
|
||||||
},
|
},
|
||||||
isMobile: {
|
platform: {
|
||||||
type: Boolean,
|
type: String,
|
||||||
default: false
|
required: true
|
||||||
},
|
},
|
||||||
isCustomizeMode: {
|
isCustomizeMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -66,17 +66,10 @@ export default function<T extends object>(data: {
|
|||||||
|
|
||||||
this.bakeProps();
|
this.bakeProps();
|
||||||
|
|
||||||
if (this.isMobile) {
|
(this as any).api('i/update_widget', {
|
||||||
(this as any).api('i/update_mobile_home', {
|
id: this.id,
|
||||||
id: this.id,
|
data: this.props
|
||||||
data: this.props
|
});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
(this as any).api('i/update_home', {
|
|
||||||
id: this.id,
|
|
||||||
data: this.props
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import * as merge from 'object-assign-deep';
|
|
||||||
|
|
||||||
import Stream from './stream';
|
import Stream from './stream';
|
||||||
import StreamManager from './stream-manager';
|
import StreamManager from './stream-manager';
|
||||||
import MiOS from '../../../mios';
|
import MiOS from '../../../mios';
|
||||||
@@ -60,25 +58,18 @@ export class HomeStream extends Stream {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.on('home_updated', x => {
|
this.on('home_updated', x => {
|
||||||
if (x.home) {
|
os.store.commit('settings/setHome', x);
|
||||||
os.store.commit('settings/setHome', x.home);
|
|
||||||
} else {
|
|
||||||
os.store.commit('settings/setHomeWidget', {
|
|
||||||
id: x.id,
|
|
||||||
data: x.data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('mobile_home_updated', x => {
|
this.on('mobile_home_updated', x => {
|
||||||
if (x.home) {
|
os.store.commit('settings/setMobileHome', x);
|
||||||
os.store.commit('settings/setMobileHome', x.home);
|
});
|
||||||
} else {
|
|
||||||
os.store.commit('settings/setMobileHomeWidget', {
|
this.on('widgetUpdated', x => {
|
||||||
id: x.id,
|
os.store.commit('settings/setWidget', {
|
||||||
data: x.data
|
id: x.id,
|
||||||
});
|
data: x.data
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// トークンが再生成されたとき
|
// トークンが再生成されたとき
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
import analogClock from './analog-clock.vue';
|
import analogClock from './analog-clock.vue';
|
||||||
|
import menu from './menu.vue';
|
||||||
import signin from './signin.vue';
|
import signin from './signin.vue';
|
||||||
import signup from './signup.vue';
|
import signup from './signup.vue';
|
||||||
import forkit from './forkit.vue';
|
import forkit from './forkit.vue';
|
||||||
@@ -29,6 +30,7 @@ import Othello from './othello.vue';
|
|||||||
import welcomeTimeline from './welcome-timeline.vue';
|
import welcomeTimeline from './welcome-timeline.vue';
|
||||||
|
|
||||||
Vue.component('mk-analog-clock', analogClock);
|
Vue.component('mk-analog-clock', analogClock);
|
||||||
|
Vue.component('mk-menu', menu);
|
||||||
Vue.component('mk-signin', signin);
|
Vue.component('mk-signin', signin);
|
||||||
Vue.component('mk-signup', signup);
|
Vue.component('mk-signup', signup);
|
||||||
Vue.component('mk-forkit', forkit);
|
Vue.component('mk-forkit', forkit);
|
||||||
|
161
src/client/app/common/views/components/menu.vue
Normal file
161
src/client/app/common/views/components/menu.vue
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mk-menu">
|
||||||
|
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||||
|
<div class="popover" :class="{ compact }" 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>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import * as anime from 'animejs';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['source', 'compact', 'items'],
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const popover = this.$refs.popover as any;
|
||||||
|
|
||||||
|
const rect = this.source.getBoundingClientRect();
|
||||||
|
const width = popover.offsetWidth;
|
||||||
|
const height = popover.offsetHeight;
|
||||||
|
|
||||||
|
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';
|
||||||
|
} 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';
|
||||||
|
}
|
||||||
|
|
||||||
|
anime({
|
||||||
|
targets: this.$refs.backdrop,
|
||||||
|
opacity: 1,
|
||||||
|
duration: 100,
|
||||||
|
easing: 'linear'
|
||||||
|
});
|
||||||
|
|
||||||
|
anime({
|
||||||
|
targets: this.$refs.popover,
|
||||||
|
opacity: 1,
|
||||||
|
scale: [0.5, 1],
|
||||||
|
duration: 500
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clicked(fn) {
|
||||||
|
fn();
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
(this.$refs.backdrop as any).style.pointerEvents = 'none';
|
||||||
|
anime({
|
||||||
|
targets: this.$refs.backdrop,
|
||||||
|
opacity: 0,
|
||||||
|
duration: 200,
|
||||||
|
easing: 'linear'
|
||||||
|
});
|
||||||
|
|
||||||
|
(this.$refs.popover as any).style.pointerEvents = 'none';
|
||||||
|
anime({
|
||||||
|
targets: this.$refs.popover,
|
||||||
|
opacity: 0,
|
||||||
|
scale: 0.5,
|
||||||
|
duration: 200,
|
||||||
|
easing: 'easeInBack',
|
||||||
|
complete: () => {
|
||||||
|
this.$emit('closed');
|
||||||
|
this.$destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
$border-color = rgba(27, 31, 35, 0.15)
|
||||||
|
|
||||||
|
.mk-menu
|
||||||
|
position initial
|
||||||
|
|
||||||
|
> .backdrop
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
z-index 10000
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
background rgba(#000, 0.1)
|
||||||
|
opacity 0
|
||||||
|
|
||||||
|
> .popover
|
||||||
|
position absolute
|
||||||
|
z-index 10001
|
||||||
|
padding 8px 0
|
||||||
|
background #fff
|
||||||
|
border 1px solid $border-color
|
||||||
|
border-radius 4px
|
||||||
|
box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
|
||||||
|
transform scale(0.5)
|
||||||
|
opacity 0
|
||||||
|
|
||||||
|
$balloon-size = 16px
|
||||||
|
|
||||||
|
&:not(.compact)
|
||||||
|
margin-top $balloon-size
|
||||||
|
transform-origin center -($balloon-size)
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top -($balloon-size * 2)
|
||||||
|
left s('calc(50% - %s)', $balloon-size)
|
||||||
|
border-top solid $balloon-size transparent
|
||||||
|
border-left solid $balloon-size transparent
|
||||||
|
border-right solid $balloon-size transparent
|
||||||
|
border-bottom solid $balloon-size $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
|
||||||
|
border-left solid $balloon-size transparent
|
||||||
|
border-right solid $balloon-size transparent
|
||||||
|
border-bottom solid $balloon-size #fff
|
||||||
|
|
||||||
|
> button
|
||||||
|
display block
|
||||||
|
padding 8px 16px
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color $theme-color-foreground
|
||||||
|
background $theme-color
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
&:active
|
||||||
|
color $theme-color-foreground
|
||||||
|
background darken($theme-color, 10%)
|
||||||
|
|
||||||
|
> div
|
||||||
|
margin 8px 0
|
||||||
|
height 1px
|
||||||
|
background #eee
|
||||||
|
|
||||||
|
</style>
|
@@ -1,55 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-note-menu">
|
<div class="mk-note-menu" style="position:initial">
|
||||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
<mk-menu ref="menu" :source="source" :compact="compact" :items="items" @closed="$destroy"/>
|
||||||
<div class="popover" :class="{ compact }" ref="popover">
|
|
||||||
<button @click="favorite">%i18n:@favorite%</button>
|
|
||||||
<button v-if="note.userId == $store.state.i.id" @click="pin">%i18n:@pin%</button>
|
|
||||||
<button v-if="note.userId == $store.state.i.id" @click="del">%i18n:@delete%</button>
|
|
||||||
<a v-if="note.uri" :href="note.uri" target="_blank">%i18n:@remote%</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import * as anime from 'animejs';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['note', 'source', 'compact'],
|
props: ['note', 'source', 'compact'],
|
||||||
mounted() {
|
computed: {
|
||||||
this.$nextTick(() => {
|
items() {
|
||||||
const popover = this.$refs.popover as any;
|
const items = [];
|
||||||
|
items.push({
|
||||||
const rect = this.source.getBoundingClientRect();
|
content: '%i18n:@favorite%',
|
||||||
const width = popover.offsetWidth;
|
onClick: this.favorite
|
||||||
const height = popover.offsetHeight;
|
});
|
||||||
|
if (this.note.userId == this.$store.state.i.id) {
|
||||||
if (this.compact) {
|
items.push({
|
||||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
content: '%i18n:@pin%',
|
||||||
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
|
onClick: this.pin
|
||||||
popover.style.left = (x - (width / 2)) + 'px';
|
});
|
||||||
popover.style.top = (y - (height / 2)) + 'px';
|
items.push({
|
||||||
} else {
|
content: '%i18n:@delete%',
|
||||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
onClick: this.del
|
||||||
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
});
|
||||||
popover.style.left = (x - (width / 2)) + 'px';
|
|
||||||
popover.style.top = y + 'px';
|
|
||||||
}
|
}
|
||||||
|
if (this.note.uri) {
|
||||||
anime({
|
items.push({
|
||||||
targets: this.$refs.backdrop,
|
content: '%i18n:@remote%',
|
||||||
opacity: 1,
|
onClick: () => {
|
||||||
duration: 100,
|
window.open(this.note.uri, '_blank');
|
||||||
easing: 'linear'
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
anime({
|
return items;
|
||||||
targets: this.$refs.popover,
|
}
|
||||||
opacity: 1,
|
|
||||||
scale: [0.5, 1],
|
|
||||||
duration: 500
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
pin() {
|
pin() {
|
||||||
@@ -78,98 +64,8 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
(this.$refs.backdrop as any).style.pointerEvents = 'none';
|
this.$refs.menu.close();
|
||||||
anime({
|
|
||||||
targets: this.$refs.backdrop,
|
|
||||||
opacity: 0,
|
|
||||||
duration: 200,
|
|
||||||
easing: 'linear'
|
|
||||||
});
|
|
||||||
|
|
||||||
(this.$refs.popover as any).style.pointerEvents = 'none';
|
|
||||||
anime({
|
|
||||||
targets: this.$refs.popover,
|
|
||||||
opacity: 0,
|
|
||||||
scale: 0.5,
|
|
||||||
duration: 200,
|
|
||||||
easing: 'easeInBack',
|
|
||||||
complete: () => this.$destroy()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
@import '~const.styl'
|
|
||||||
|
|
||||||
$border-color = rgba(27, 31, 35, 0.15)
|
|
||||||
|
|
||||||
.mk-note-menu
|
|
||||||
position initial
|
|
||||||
|
|
||||||
> .backdrop
|
|
||||||
position fixed
|
|
||||||
top 0
|
|
||||||
left 0
|
|
||||||
z-index 10000
|
|
||||||
width 100%
|
|
||||||
height 100%
|
|
||||||
background rgba(#000, 0.1)
|
|
||||||
opacity 0
|
|
||||||
|
|
||||||
> .popover
|
|
||||||
position absolute
|
|
||||||
z-index 10001
|
|
||||||
padding 8px 0
|
|
||||||
background #fff
|
|
||||||
border 1px solid $border-color
|
|
||||||
border-radius 4px
|
|
||||||
box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
|
|
||||||
transform scale(0.5)
|
|
||||||
opacity 0
|
|
||||||
|
|
||||||
$balloon-size = 16px
|
|
||||||
|
|
||||||
&:not(.compact)
|
|
||||||
margin-top $balloon-size
|
|
||||||
transform-origin center -($balloon-size)
|
|
||||||
|
|
||||||
&:before
|
|
||||||
content ""
|
|
||||||
display block
|
|
||||||
position absolute
|
|
||||||
top -($balloon-size * 2)
|
|
||||||
left s('calc(50% - %s)', $balloon-size)
|
|
||||||
border-top solid $balloon-size transparent
|
|
||||||
border-left solid $balloon-size transparent
|
|
||||||
border-right solid $balloon-size transparent
|
|
||||||
border-bottom solid $balloon-size $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
|
|
||||||
border-left solid $balloon-size transparent
|
|
||||||
border-right solid $balloon-size transparent
|
|
||||||
border-bottom solid $balloon-size #fff
|
|
||||||
|
|
||||||
> button
|
|
||||||
> a
|
|
||||||
display block
|
|
||||||
padding 8px 16px
|
|
||||||
width 100%
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
color $theme-color-foreground
|
|
||||||
background $theme-color
|
|
||||||
text-decoration none
|
|
||||||
|
|
||||||
&:active
|
|
||||||
color $theme-color-foreground
|
|
||||||
background darken($theme-color, 10%)
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="mkw-broadcast"
|
<div class="mkw-broadcast"
|
||||||
:data-found="broadcasts.length != 0"
|
:data-found="broadcasts.length != 0"
|
||||||
:data-melt="props.design == 1"
|
:data-melt="props.design == 1"
|
||||||
:data-mobile="isMobile"
|
:data-mobile="platform == 'mobile'"
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<svg height="32" version="1.1" viewBox="0 0 32 32" width="32">
|
<svg height="32" version="1.1" viewBox="0 0 32 32" width="32">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mkw-calendar" :data-special="special" :data-mobile="isMobile">
|
<div class="mkw-calendar" :data-special="special" :data-mobile="platform == 'mobile'">
|
||||||
<mk-widget-container :naked="props.design == 1" :show-header="false">
|
<mk-widget-container :naked="props.design == 1" :show-header="false">
|
||||||
<div class="mkw-calendar--body">
|
<div class="mkw-calendar--body">
|
||||||
<div class="calendar" :data-is-holiday="isHoliday">
|
<div class="calendar" :data-is-holiday="isHoliday">
|
||||||
@@ -67,7 +67,7 @@ export default define({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
func() {
|
func() {
|
||||||
if (this.isMobile) return;
|
if (this.platform == 'mobile') return;
|
||||||
if (this.props.design == 2) {
|
if (this.props.design == 2) {
|
||||||
this.props.design = 0;
|
this.props.design = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mkw-donation" :data-mobile="isMobile">
|
<div class="mkw-donation" :data-mobile="platform == 'mobile'">
|
||||||
<article>
|
<article>
|
||||||
<h1>%fa:heart%%i18n:@title%</h1>
|
<h1>%fa:heart%%i18n:@title%</h1>
|
||||||
<p>
|
<p>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<template slot="header">%fa:rss-square%RSS</template>
|
<template slot="header">%fa:rss-square%RSS</template>
|
||||||
<button slot="func" title="設定" @click="setting">%fa:cog%</button>
|
<button slot="func" title="設定" @click="setting">%fa:cog%</button>
|
||||||
|
|
||||||
<div class="mkw-rss--body" :data-mobile="isMobile">
|
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
|
||||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||||
<div class="feed" v-else>
|
<div class="feed" v-else>
|
||||||
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
|
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mkw-slideshow" :data-mobile="isMobile">
|
<div class="mkw-slideshow" :data-mobile="platform == 'mobile'">
|
||||||
<div @click="choose">
|
<div @click="choose">
|
||||||
<p v-if="props.folder === undefined">
|
<p v-if="props.folder === undefined">
|
||||||
<template v-if="isCustomizeMode">フォルダを指定するには、カスタマイズモードを終了してください</template>
|
<template v-if="isCustomizeMode">フォルダを指定するには、カスタマイズモードを終了してください</template>
|
||||||
|
@@ -6,7 +6,7 @@ export default (os: OS) => opts => {
|
|||||||
const o = opts || {};
|
const o = opts || {};
|
||||||
if (o.renote) {
|
if (o.renote) {
|
||||||
const vm = os.new(RenoteFormWindow, {
|
const vm = os.new(RenoteFormWindow, {
|
||||||
renote: o.renote
|
note: o.renote
|
||||||
});
|
});
|
||||||
document.body.appendChild(vm.$el);
|
document.body.appendChild(vm.$el);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -23,6 +23,7 @@ import updateAvatar from './api/update-avatar';
|
|||||||
import updateBanner from './api/update-banner';
|
import updateBanner from './api/update-banner';
|
||||||
|
|
||||||
import MkIndex from './views/pages/index.vue';
|
import MkIndex from './views/pages/index.vue';
|
||||||
|
import MkDeck from './views/pages/deck/deck.vue';
|
||||||
import MkUser from './views/pages/user/user.vue';
|
import MkUser from './views/pages/user/user.vue';
|
||||||
import MkFavorites from './views/pages/favorites.vue';
|
import MkFavorites from './views/pages/favorites.vue';
|
||||||
import MkSelectDrive from './views/pages/selectdrive.vue';
|
import MkSelectDrive from './views/pages/selectdrive.vue';
|
||||||
@@ -50,6 +51,7 @@ init(async (launch) => {
|
|||||||
mode: 'history',
|
mode: 'history',
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/', name: 'index', component: MkIndex },
|
{ path: '/', name: 'index', component: MkIndex },
|
||||||
|
{ path: '/deck', name: 'deck', component: MkDeck },
|
||||||
{ path: '/i/customize-home', component: MkHomeCustomize },
|
{ path: '/i/customize-home', component: MkHomeCustomize },
|
||||||
{ path: '/i/favorites', component: MkFavorites },
|
{ path: '/i/favorites', component: MkFavorites },
|
||||||
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
||||||
|
@@ -42,8 +42,3 @@ html
|
|||||||
|
|
||||||
&:active
|
&:active
|
||||||
background-color $theme-color
|
background-color $theme-color
|
||||||
|
|
||||||
body
|
|
||||||
display flex
|
|
||||||
flex-direction column
|
|
||||||
min-height 100%
|
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
>
|
>
|
||||||
<template v-if="!wait">
|
<template v-if="!wait">
|
||||||
<template v-if="u.hasPendingFollowRequestFromYou">%fa:hourglass-half%<template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
|
<template v-if="u.hasPendingFollowRequestFromYou">%fa:hourglass-half%<template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
|
||||||
<template v-else-if="u.isFollowing">%fa:minus%<template v-if="size == 'big'"> %i18n:@unfollow%</template></template>
|
<template v-else-if="u.isFollowing">%fa:minus%<template v-if="size == 'big'"> %i18n:@following%</template></template>
|
||||||
<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
|
<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
|
||||||
<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow%</template></template>
|
<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow%</template></template>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -47,7 +47,7 @@
|
|||||||
:key="place"
|
:key="place"
|
||||||
>
|
>
|
||||||
<div v-for="widget in widgets[place]" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
|
<div v-for="widget in widgets[place]" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
|
||||||
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true"/>
|
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="desktop"/>
|
||||||
</div>
|
</div>
|
||||||
</x-draggable>
|
</x-draggable>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-for="place in ['left', 'right']" :class="place">
|
<div v-for="place in ['left', 'right']" :class="place">
|
||||||
<component v-for="widget in widgets[place]" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" @chosen="warp"/>
|
<component v-for="widget in widgets[place]" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" @chosen="warp" platform="desktop"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
|
<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
|
||||||
@@ -76,6 +76,50 @@ import Vue from 'vue';
|
|||||||
import * as XDraggable from 'vuedraggable';
|
import * as XDraggable from 'vuedraggable';
|
||||||
import * as uuid from 'uuid';
|
import * as uuid from 'uuid';
|
||||||
|
|
||||||
|
const defaultDesktopHomeWidgets = {
|
||||||
|
left: [
|
||||||
|
'profile',
|
||||||
|
'calendar',
|
||||||
|
'activity',
|
||||||
|
'rss',
|
||||||
|
'trends',
|
||||||
|
'photo-stream',
|
||||||
|
'version'
|
||||||
|
],
|
||||||
|
right: [
|
||||||
|
'broadcast',
|
||||||
|
'notifications',
|
||||||
|
'users',
|
||||||
|
'polls',
|
||||||
|
'server',
|
||||||
|
'donation',
|
||||||
|
'nav',
|
||||||
|
'tips'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
//#region Construct home data
|
||||||
|
const _defaultDesktopHomeWidgets = [];
|
||||||
|
|
||||||
|
defaultDesktopHomeWidgets.left.forEach(widget => {
|
||||||
|
_defaultDesktopHomeWidgets.push({
|
||||||
|
name: widget,
|
||||||
|
id: uuid(),
|
||||||
|
place: 'left',
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
defaultDesktopHomeWidgets.right.forEach(widget => {
|
||||||
|
_defaultDesktopHomeWidgets.push({
|
||||||
|
name: widget,
|
||||||
|
id: uuid(),
|
||||||
|
place: 'right',
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//#endregion
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XDraggable
|
XDraggable
|
||||||
@@ -103,7 +147,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
home(): any[] {
|
home(): any[] {
|
||||||
return this.$store.state.settings.home;
|
return this.$store.state.settings.home || [];
|
||||||
},
|
},
|
||||||
left(): any[] {
|
left(): any[] {
|
||||||
return this.home.filter(w => w.place == 'left');
|
return this.home.filter(w => w.place == 'left');
|
||||||
@@ -119,6 +163,16 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.$store.state.settings.home == null) {
|
||||||
|
this.api('i/update_home', {
|
||||||
|
home: _defaultDesktopHomeWidgets
|
||||||
|
}).then(() => {
|
||||||
|
this.$store.commit('settings/setHome', _defaultDesktopHomeWidgets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = (this as any).os.stream.getConnection();
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
this.connectionId = (this as any).os.stream.use();
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
@@ -74,7 +74,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
||||||
window.addEventListener('scroll', this.onScroll);
|
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
@@ -206,7 +206,7 @@ root(isDark)
|
|||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
overflow-wrap break-word
|
overflow-wrap break-word
|
||||||
font-size 0.9em
|
font-size 12px
|
||||||
border-bottom solid 1px isDark ? #1c2023 : rgba(#000, 0.05)
|
border-bottom solid 1px isDark ? #1c2023 : rgba(#000, 0.05)
|
||||||
|
|
||||||
&:last-child
|
&:last-child
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
|
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
|
||||||
<span slot="header">%fa:envelope R% %i18n:@title%</span>
|
<span slot="header">%fa:envelope R% %i18n:@title%</span>
|
||||||
|
|
||||||
<div data-id="c1136cec-1278-49b1-9ea7-412c1ef794f4" :data-darkmode="$store.state.device.darkmode">
|
<div class="slpqaxdoxhvglersgjukmvizkqbmbokc" :data-darkmode="$store.state.device.darkmode">
|
||||||
<div v-for="req in requests">
|
<div v-for="req in requests">
|
||||||
<router-link :key="req.id" :to="req.follower | userPage">{{ req.follower | userName }}</router-link>
|
<router-link :key="req.id" :to="req.follower | userPage">{{ req.follower | userName }}</router-link>
|
||||||
<span>
|
<span>
|
||||||
@@ -63,10 +63,10 @@ root(isDark)
|
|||||||
> span
|
> span
|
||||||
margin 0 0 0 auto
|
margin 0 0 0 auto
|
||||||
|
|
||||||
[data-id="c1136cec-1278-49b1-9ea7-412c1ef794f4"][data-darkmode]
|
.slpqaxdoxhvglersgjukmvizkqbmbokc[data-darkmode]
|
||||||
root(true)
|
root(true)
|
||||||
|
|
||||||
[data-id="c1136cec-1278-49b1-9ea7-412c1ef794f4"]:not([data-darkmode])
|
.slpqaxdoxhvglersgjukmvizkqbmbokc:not([data-darkmode])
|
||||||
root(false)
|
root(false)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -40,6 +40,8 @@
|
|||||||
<button class="ui button" @click="customizeHome" style="margin-bottom: 16px">%i18n:@customize%</button>
|
<button class="ui button" @click="customizeHome" style="margin-bottom: 16px">%i18n:@customize%</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="div">
|
<div class="div">
|
||||||
|
<button class="ui" @click="updateWallpaper">%i18n:@choose-wallpaper%</button>
|
||||||
|
<button class="ui" @click="deleteWallpaper">%i18n:@delete-wallpaper%</button>
|
||||||
<mk-switch v-model="darkmode" text="%i18n:@dark-mode%"/>
|
<mk-switch v-model="darkmode" text="%i18n:@dark-mode%"/>
|
||||||
<mk-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons" text="%i18n:@circle-icons%"/>
|
<mk-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons" text="%i18n:@circle-icons%"/>
|
||||||
<mk-switch v-model="$store.state.settings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="%i18n:@gradient-window-header%"/>
|
<mk-switch v-model="$store.state.settings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="%i18n:@gradient-window-header%"/>
|
||||||
@@ -293,6 +295,20 @@ export default Vue.extend({
|
|||||||
this.$router.push('/i/customize-home');
|
this.$router.push('/i/customize-home');
|
||||||
this.$emit('done');
|
this.$emit('done');
|
||||||
},
|
},
|
||||||
|
updateWallpaper() {
|
||||||
|
(this as any).apis.chooseDriveFile({
|
||||||
|
multiple: false
|
||||||
|
}).then(file => {
|
||||||
|
(this as any).api('i/update', {
|
||||||
|
wallpaperId: file.id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteWallpaper() {
|
||||||
|
(this as any).api('i/update', {
|
||||||
|
wallpaperId: null
|
||||||
|
});
|
||||||
|
},
|
||||||
onChangeFetchOnScroll(v) {
|
onChangeFetchOnScroll(v) {
|
||||||
this.$store.dispatch('settings/set', {
|
this.$store.dispatch('settings/set', {
|
||||||
key: 'fetchOnScroll',
|
key: 'fetchOnScroll',
|
||||||
|
@@ -8,6 +8,12 @@
|
|||||||
<p>%i18n:@home%</p>
|
<p>%i18n:@home%</p>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="deck" :class="{ active: $route.name == 'deck' }">
|
||||||
|
<router-link to="/deck">
|
||||||
|
%fa:columns%
|
||||||
|
<p>%i18n:@deck% <small>(beta)</small></p>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
<li class="messaging">
|
<li class="messaging">
|
||||||
<a @click="messaging">
|
<a @click="messaging">
|
||||||
%fa:comments%
|
%fa:comments%
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-ui">
|
<div class="mk-ui" :style="style">
|
||||||
<x-header class="header"/>
|
<x-header class="header"/>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
@@ -16,6 +16,15 @@ export default Vue.extend({
|
|||||||
components: {
|
components: {
|
||||||
XHeader
|
XHeader
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
style(): any {
|
||||||
|
if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {};
|
||||||
|
return {
|
||||||
|
backgroundColor: this.$store.state.i.wallpaperColor && this.$store.state.i.wallpaperColor.length == 3 ? `rgb(${ this.$store.state.i.wallpaperColor.join(',') })` : null,
|
||||||
|
backgroundImage: `url(${ this.$store.state.i.wallpaperUrl })`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('keydown', this.onKeydown);
|
document.addEventListener('keydown', this.onKeydown);
|
||||||
},
|
},
|
||||||
@@ -37,7 +46,20 @@ export default Vue.extend({
|
|||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-ui
|
.mk-ui
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
flex 1
|
||||||
|
background-size cover
|
||||||
|
background-position center
|
||||||
|
background-attachment fixed
|
||||||
|
|
||||||
> .header
|
> .header
|
||||||
@media (max-width 1000px)
|
@media (max-width 1000px)
|
||||||
display none
|
display none
|
||||||
|
|
||||||
|
> .content
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
flex 1
|
||||||
|
overflow hidden
|
||||||
</style>
|
</style>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
|
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
|
||||||
<span slot="header">%fa:list% %i18n:@title%</span>
|
<span slot="header">%fa:list% %i18n:@title%</span>
|
||||||
|
|
||||||
<div data-id="6e4caea3-d8f9-4ab7-96de-ab67fe8d5c82" :data-darkmode="$store.state.device.darkmode">
|
<div class="xkxvokkjlptzyewouewmceqcxhpgzprp" :data-darkmode="$store.state.device.darkmode">
|
||||||
<button class="ui" @click="add">%i18n:@create-list%</button>
|
<button class="ui" @click="add">%i18n:@create-list%</button>
|
||||||
<a v-for="list in lists" :key="list.id" @click="choice(list)">{{ list.title }}</a>
|
<a v-for="list in lists" :key="list.id" @click="choice(list)">{{ list.title }}</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,10 +60,10 @@ root(isDark)
|
|||||||
border solid 1px isDark ? #1c2023 : #eee
|
border solid 1px isDark ? #1c2023 : #eee
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
|
||||||
[data-id="6e4caea3-d8f9-4ab7-96de-ab67fe8d5c82"][data-darkmode]
|
.xkxvokkjlptzyewouewmceqcxhpgzprp[data-darkmode]
|
||||||
root(true)
|
root(true)
|
||||||
|
|
||||||
[data-id="6e4caea3-d8f9-4ab7-96de-ab67fe8d5c82"]:not([data-darkmode])
|
.xkxvokkjlptzyewouewmceqcxhpgzprp:not([data-darkmode])
|
||||||
root(false)
|
root(false)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -36,7 +36,7 @@ export default Vue.extend({
|
|||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
root(isDark)
|
root(isDark)
|
||||||
background isDark ? #282C37 : #fff
|
background isDark ? #282C37 : #fff
|
||||||
border solid 1px rgba(#000, 0.075)
|
border solid 1px rgba(#000, isDark ? 0.2 : 0.075)
|
||||||
border-radius 6px
|
border-radius 6px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
|
|
||||||
|
195
src/client/app/desktop/views/pages/deck/deck.column.vue
Normal file
195
src/client/app/desktop/views/pages/deck/deck.column.vue
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow }">
|
||||||
|
<header :class="{ indicate }">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
<button ref="menu" @click="showMenu">%fa:caret-down%</button>
|
||||||
|
</header>
|
||||||
|
<div ref="body">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Menu from '../../../../common/views/components/menu.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
type: Array,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
naked: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
narrow: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
indicate: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
column: this,
|
||||||
|
isScrollTop: this.isScrollTop,
|
||||||
|
indicate: v => this.indicate = v
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$refs.body.addEventListener('scroll', this.onScroll, { passive: true });
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$refs.body.removeEventListener('scroll', this.onScroll);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
isScrollTop() {
|
||||||
|
return this.$refs.body.scrollTop == 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
if (this.isScrollTop()) {
|
||||||
|
this.$emit('top');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$store.state.settings.fetchOnScroll !== false) {
|
||||||
|
const current = this.$refs.body.scrollTop + this.$refs.body.clientHeight;
|
||||||
|
if (current > this.$refs.body.scrollHeight - 1) this.$emit('bottom');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showMenu() {
|
||||||
|
const items = [{
|
||||||
|
content: '%fa:pencil-alt% %i18n:common.deck.rename%',
|
||||||
|
onClick: () => {
|
||||||
|
(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 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, null, {
|
||||||
|
content: '%fa:arrow-left% %i18n:common.deck.swap-left%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/swapLeftDeckColumn', this.id);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
content: '%fa:arrow-right% %i18n:common.deck.swap-right%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/swapRightDeckColumn', this.id);
|
||||||
|
}
|
||||||
|
}, null, {
|
||||||
|
content: '%fa:trash-alt R% %i18n:common.deck.remove%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/removeDeckColumn', this.id);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (this.menu) {
|
||||||
|
items.unshift(null);
|
||||||
|
this.menu.reverse().forEach(i => items.unshift(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.os.new(Menu, {
|
||||||
|
source: this.$refs.menu,
|
||||||
|
compact: false,
|
||||||
|
items
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
$header-height = 42px
|
||||||
|
|
||||||
|
flex 1
|
||||||
|
min-width 330px
|
||||||
|
max-width 330px
|
||||||
|
height 100%
|
||||||
|
background isDark ? #282C37 : #fff
|
||||||
|
border-radius 6px
|
||||||
|
box-shadow 0 2px 16px rgba(#000, 0.1)
|
||||||
|
overflow hidden
|
||||||
|
|
||||||
|
&.narrow
|
||||||
|
min-width 285px
|
||||||
|
max-width 285px
|
||||||
|
|
||||||
|
&.naked
|
||||||
|
background rgba(#000, isDark ? 0.25 : 0.1)
|
||||||
|
|
||||||
|
> header
|
||||||
|
background transparent
|
||||||
|
box-shadow none
|
||||||
|
|
||||||
|
if !isDark
|
||||||
|
> button
|
||||||
|
color #bbb
|
||||||
|
|
||||||
|
> header
|
||||||
|
z-index 1
|
||||||
|
line-height $header-height
|
||||||
|
padding 0 16px
|
||||||
|
font-size 14px
|
||||||
|
color isDark ? #e3e5e8 : #888
|
||||||
|
background isDark ? #313543 : #fff
|
||||||
|
box-shadow 0 1px rgba(#000, 0.15)
|
||||||
|
|
||||||
|
&.indicate
|
||||||
|
box-shadow 0 3px 0 0 $theme-color
|
||||||
|
|
||||||
|
> span
|
||||||
|
[data-fa]
|
||||||
|
margin-right 8px
|
||||||
|
|
||||||
|
> button
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
width $header-height
|
||||||
|
line-height $header-height
|
||||||
|
color isDark ? #9baec8 : #ccc
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color isDark ? #b2c1d5 : #aaa
|
||||||
|
|
||||||
|
&:active
|
||||||
|
color isDark ? #b2c1d5 : #999
|
||||||
|
|
||||||
|
> div
|
||||||
|
height "calc(100% - %s)" % $header-height
|
||||||
|
overflow auto
|
||||||
|
overflow-x hidden
|
||||||
|
|
||||||
|
.dnpfarvgbnfmyzbdquhhzyxcmstpdqzs[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.dnpfarvgbnfmyzbdquhhzyxcmstpdqzs:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
118
src/client/app/desktop/views/pages/deck/deck.list-tl.vue
Normal file
118
src/client/app/desktop/views/pages/deck/deck.list-tl.vue
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<x-notes ref="timeline" :more="existMore ? more : null"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XNotes from './deck.notes.vue';
|
||||||
|
import { UserListStream } from '../../../../common/scripts/streaming/user-list';
|
||||||
|
|
||||||
|
const fetchLimit = 10;
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XNotes
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
list: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
mediaOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
moreFetching: false,
|
||||||
|
existMore: false,
|
||||||
|
connection: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
mediaOnly() {
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.connection) this.connection.close();
|
||||||
|
this.connection = new UserListStream((this as any).os, this.$store.state.i, this.list.id);
|
||||||
|
this.connection.on('note', this.onNote);
|
||||||
|
this.connection.on('userAdded', this.onUserAdded);
|
||||||
|
this.connection.on('userRemoved', this.onUserRemoved);
|
||||||
|
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.connection.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
this.fetching = true;
|
||||||
|
|
||||||
|
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
|
||||||
|
(this as any).api('notes/user-list-timeline', {
|
||||||
|
listId: this.list.id,
|
||||||
|
limit: fetchLimit + 1,
|
||||||
|
mediaOnly: this.mediaOnly,
|
||||||
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||||
|
}).then(notes => {
|
||||||
|
if (notes.length == fetchLimit + 1) {
|
||||||
|
notes.pop();
|
||||||
|
this.existMore = true;
|
||||||
|
}
|
||||||
|
res(notes);
|
||||||
|
this.fetching = false;
|
||||||
|
this.$emit('loaded');
|
||||||
|
}, rej);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
more() {
|
||||||
|
this.moreFetching = true;
|
||||||
|
|
||||||
|
const promise = (this as any).api('notes/user-list-timeline', {
|
||||||
|
listId: this.list.id,
|
||||||
|
limit: fetchLimit + 1,
|
||||||
|
untilId: (this.$refs.timeline as any).tail().id,
|
||||||
|
mediaOnly: this.mediaOnly,
|
||||||
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.then(notes => {
|
||||||
|
if (notes.length == fetchLimit + 1) {
|
||||||
|
notes.pop();
|
||||||
|
} else {
|
||||||
|
this.existMore = false;
|
||||||
|
}
|
||||||
|
notes.forEach(n => (this.$refs.timeline as any).append(n));
|
||||||
|
this.moreFetching = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
onNote(note) {
|
||||||
|
if (this.mediaOnly && note.media.length == 0) return;
|
||||||
|
|
||||||
|
// Prepend a note
|
||||||
|
(this.$refs.timeline as any).prepend(note);
|
||||||
|
},
|
||||||
|
onUserAdded() {
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
onUserRemoved() {
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
153
src/client/app/desktop/views/pages/deck/deck.note.sub.vue
Normal file
153
src/client/app/desktop/views/pages/deck/deck.note.sub.vue
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<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>
|
||||||
|
<div class="body">
|
||||||
|
<mk-sub-note-content class="text" :note="note"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: {
|
||||||
|
note: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// TODO
|
||||||
|
truncate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
root(isDark)
|
||||||
|
display flex
|
||||||
|
padding 16px
|
||||||
|
font-size 10px
|
||||||
|
background isDark ? #21242d : #fcfcfc
|
||||||
|
|
||||||
|
&.smart
|
||||||
|
> .main
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
> header
|
||||||
|
align-items center
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
flex-shrink 0
|
||||||
|
display block
|
||||||
|
margin 0 8px 0 0
|
||||||
|
width 38px
|
||||||
|
height 38px
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .main
|
||||||
|
flex 1
|
||||||
|
min-width 0
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
align-items baseline
|
||||||
|
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
|
||||||
|
|
||||||
|
> .text
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
color isDark ? #959ba7 : #717171
|
||||||
|
|
||||||
|
pre
|
||||||
|
max-height 120px
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
.fnlfosztlhtptnongximhlbykxblytcq[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.fnlfosztlhtptnongximhlbykxblytcq:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
513
src/client/app/desktop/views/pages/deck/deck.note.vue
Normal file
513
src/client/app/desktop/views/pages/deck/deck.note.vue
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
<template>
|
||||||
|
<div 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>
|
||||||
|
<div class="renote" v-if="isRenote">
|
||||||
|
<mk-avatar class="avatar" :user="note.user"/>
|
||||||
|
%fa:retweet%
|
||||||
|
<span>{{ '%i18n:@reposted-by%'.substr(0, '%i18n:@reposted-by%'.indexOf('{')) }}</span>
|
||||||
|
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
||||||
|
<span>{{ '%i18n:@reposted-by%'.substr('%i18n:@reposted-by%'.indexOf('}') + 1) }}</span>
|
||||||
|
<mk-time :time="note.createdAt"/>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
<div class="body">
|
||||||
|
<p v-if="p.cw != null" class="cw">
|
||||||
|
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
|
||||||
|
<span class="toggle" @click="showContent = !showContent">{{ showContent ? '%i18n:@less%' : '%i18n:@more%' }}</span>
|
||||||
|
</p>
|
||||||
|
<div class="content" v-show="p.cw == null || showContent">
|
||||||
|
<div class="text">
|
||||||
|
<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
|
||||||
|
<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
|
||||||
|
<a class="reply" v-if="p.reply">%fa:reply%</a>
|
||||||
|
<mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i"/>
|
||||||
|
<a class="rp" v-if="p.renote != null">RP:</a>
|
||||||
|
</div>
|
||||||
|
<div class="media" v-if="p.media.length > 0">
|
||||||
|
<mk-media-list :media-list="p.media"/>
|
||||||
|
</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>
|
||||||
|
</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"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
|
||||||
|
<button @click="reply">
|
||||||
|
<template v-if="p.reply">%fa:reply-all%</template>
|
||||||
|
<template v-else>%fa:reply%</template>
|
||||||
|
</button>
|
||||||
|
<button @click="renote" title="Renote">%fa:retweet%</button>
|
||||||
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">%fa:plus%</button>
|
||||||
|
<button class="menu" @click="menu" ref="menuButton">%fa:ellipsis-h%</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import parse from '../../../../../../text/parse';
|
||||||
|
import canHideText from '../../../../common/scripts/can-hide-text';
|
||||||
|
|
||||||
|
import MkNoteMenu from '../../../../common/views/components/note-menu.vue';
|
||||||
|
import MkReactionPicker from '../../../../common/views/components/reaction-picker.vue';
|
||||||
|
import XSub from './deck.note.sub.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XSub
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['note'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showContent: false,
|
||||||
|
connection: null,
|
||||||
|
connectionId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isRenote(): boolean {
|
||||||
|
return (this.note.renote &&
|
||||||
|
this.note.text == null &&
|
||||||
|
this.note.mediaIds.length == 0 &&
|
||||||
|
this.note.poll == null);
|
||||||
|
},
|
||||||
|
|
||||||
|
p(): any {
|
||||||
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
|
},
|
||||||
|
|
||||||
|
urls(): string[] {
|
||||||
|
if (this.p.text) {
|
||||||
|
const ast = parse(this.p.text);
|
||||||
|
return ast
|
||||||
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
|
.map(t => t.url);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.$store.getters.isSignedIn) {
|
||||||
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.capture(true);
|
||||||
|
|
||||||
|
if (this.$store.getters.isSignedIn) {
|
||||||
|
this.connection.on('_connected_', this.onStreamConnected);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.decapture(true);
|
||||||
|
|
||||||
|
if (this.$store.getters.isSignedIn) {
|
||||||
|
this.connection.off('_connected_', this.onStreamConnected);
|
||||||
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
canHideText,
|
||||||
|
|
||||||
|
capture(withHandler = false) {
|
||||||
|
if (this.$store.getters.isSignedIn) {
|
||||||
|
this.connection.send({
|
||||||
|
type: 'capture',
|
||||||
|
id: this.p.id
|
||||||
|
});
|
||||||
|
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
decapture(withHandler = false) {
|
||||||
|
if (this.$store.getters.isSignedIn) {
|
||||||
|
this.connection.send({
|
||||||
|
type: 'decapture',
|
||||||
|
id: this.p.id
|
||||||
|
});
|
||||||
|
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onStreamConnected() {
|
||||||
|
this.capture();
|
||||||
|
},
|
||||||
|
|
||||||
|
onStreamNoteUpdated(data) {
|
||||||
|
const note = data.note;
|
||||||
|
if (note.id == this.note.id) {
|
||||||
|
this.$emit('update:note', note);
|
||||||
|
} else if (note.id == this.note.renoteId) {
|
||||||
|
this.note.renote = note;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reply() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
renote() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
renote: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
react() {
|
||||||
|
(this as any).os.new(MkReactionPicker, {
|
||||||
|
source: this.$refs.reactButton,
|
||||||
|
note: this.p,
|
||||||
|
compact: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
menu() {
|
||||||
|
(this as any).os.new(MkNoteMenu, {
|
||||||
|
source: this.$refs.menuButton,
|
||||||
|
note: this.p,
|
||||||
|
compact: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
font-size 12px
|
||||||
|
border-bottom solid 1px isDark ? #1c2023 : #eaeaea
|
||||||
|
|
||||||
|
&:last-of-type
|
||||||
|
border-bottom none
|
||||||
|
|
||||||
|
&.smart
|
||||||
|
> article
|
||||||
|
> .main
|
||||||
|
> header
|
||||||
|
align-items center
|
||||||
|
margin-bottom 4px
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
display flex
|
||||||
|
align-items center
|
||||||
|
padding 8px 16px
|
||||||
|
line-height 28px
|
||||||
|
white-space pre
|
||||||
|
color #9dbb00
|
||||||
|
background isDark ? linear-gradient(to bottom, #314027 0%, #282c37 100%) : linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
flex-shrink 0
|
||||||
|
display inline-block
|
||||||
|
width 20px
|
||||||
|
height 20px
|
||||||
|
margin 0 8px 0 0
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
> span
|
||||||
|
flex-shrink 0
|
||||||
|
|
||||||
|
&:last-of-type
|
||||||
|
margin-right 8px
|
||||||
|
|
||||||
|
.name
|
||||||
|
overflow hidden
|
||||||
|
flex-shrink 1
|
||||||
|
text-overflow ellipsis
|
||||||
|
white-space nowrap
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
> .mk-time
|
||||||
|
display block
|
||||||
|
margin-left auto
|
||||||
|
flex-shrink 0
|
||||||
|
font-size 0.9em
|
||||||
|
|
||||||
|
& + article
|
||||||
|
padding-top 8px
|
||||||
|
|
||||||
|
> article
|
||||||
|
display flex
|
||||||
|
padding 16px 16px 9px
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
flex-shrink 0
|
||||||
|
display block
|
||||||
|
margin 0 10px 8px 0
|
||||||
|
width 42px
|
||||||
|
height 42px
|
||||||
|
border-radius 6px
|
||||||
|
//position -webkit-sticky
|
||||||
|
//position sticky
|
||||||
|
//top 62px
|
||||||
|
|
||||||
|
> .main
|
||||||
|
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
|
||||||
|
cursor default
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow-wrap break-word
|
||||||
|
color isDark ? #fff : #717171
|
||||||
|
|
||||||
|
> .text
|
||||||
|
margin-right 8px
|
||||||
|
|
||||||
|
> .toggle
|
||||||
|
display inline-block
|
||||||
|
padding 4px 8px
|
||||||
|
font-size 0.7em
|
||||||
|
color isDark ? #393f4f : #fff
|
||||||
|
background isDark ? #687390 : #b1b9c1
|
||||||
|
border-radius 2px
|
||||||
|
cursor pointer
|
||||||
|
user-select none
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background isDark ? #707b97 : #bbc4ce
|
||||||
|
|
||||||
|
> .content
|
||||||
|
|
||||||
|
> .text
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow-wrap break-word
|
||||||
|
color isDark ? #fff : #717171
|
||||||
|
|
||||||
|
>>> .title
|
||||||
|
display block
|
||||||
|
margin-bottom 4px
|
||||||
|
padding 4px
|
||||||
|
font-size 90%
|
||||||
|
text-align center
|
||||||
|
background isDark ? #2f3944 : #eef1f3
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
>>> .code
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
>>> .quote
|
||||||
|
margin 8px
|
||||||
|
padding 6px 12px
|
||||||
|
color isDark ? #6f808e : #aaa
|
||||||
|
border-left solid 3px isDark ? #637182 : #eee
|
||||||
|
|
||||||
|
> .reply
|
||||||
|
margin-right 8px
|
||||||
|
color isDark ? #99abbf : #717171
|
||||||
|
|
||||||
|
> .rp
|
||||||
|
margin-left 4px
|
||||||
|
font-style oblique
|
||||||
|
color #a0bf46
|
||||||
|
|
||||||
|
[data-is-me]:after
|
||||||
|
content "you"
|
||||||
|
padding 0 4px
|
||||||
|
margin-left 4px
|
||||||
|
font-size 80%
|
||||||
|
color $theme-color-foreground
|
||||||
|
background $theme-color
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
.mk-url-preview
|
||||||
|
margin-top 8px
|
||||||
|
|
||||||
|
> .tags
|
||||||
|
margin 4px 0 0 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
display inline-block
|
||||||
|
margin 0 8px 0 0
|
||||||
|
padding 2px 8px 2px 16px
|
||||||
|
font-size 90%
|
||||||
|
color #8d969e
|
||||||
|
background isDark ? #313543 : #edf0f3
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 4px
|
||||||
|
width 8px
|
||||||
|
height 8px
|
||||||
|
margin auto 0
|
||||||
|
background isDark ? #282c37 : #fff
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
> .media
|
||||||
|
> img
|
||||||
|
display block
|
||||||
|
max-width 100%
|
||||||
|
|
||||||
|
> .location
|
||||||
|
margin 4px 0
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .map
|
||||||
|
width 100%
|
||||||
|
height 200px
|
||||||
|
|
||||||
|
&:empty
|
||||||
|
display none
|
||||||
|
|
||||||
|
> .mk-poll
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
padding 16px
|
||||||
|
border dashed 1px isDark ? #4e945e : #c0dac6
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .app
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> footer
|
||||||
|
> button
|
||||||
|
margin 0
|
||||||
|
padding 8px
|
||||||
|
background transparent
|
||||||
|
border none
|
||||||
|
box-shadow none
|
||||||
|
font-size 1em
|
||||||
|
color isDark ? #606984 : #ddd
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
&:not(:last-child)
|
||||||
|
margin-right 28px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color isDark ? #9198af : #666
|
||||||
|
|
||||||
|
> .count
|
||||||
|
display inline
|
||||||
|
margin 0 0 0 8px
|
||||||
|
color #999
|
||||||
|
|
||||||
|
&.reacted
|
||||||
|
color $theme-color
|
||||||
|
|
||||||
|
.zyjjkidcqjnlegkqebitfviomuqmseqk[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.zyjjkidcqjnlegkqebitfviomuqmseqk:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
236
src/client/app/desktop/views/pages/deck/deck.notes.vue
Normal file
236
src/client/app/desktop/views/pages/deck/deck.notes.vue
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
<template>
|
||||||
|
<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
|
||||||
|
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
|
||||||
|
|
||||||
|
<div v-if="!fetching && requestInitPromise != null">
|
||||||
|
<p>%i18n:@error%</p>
|
||||||
|
<button @click="resolveInitPromise">%i18n:@retry%</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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)"/>
|
||||||
|
<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
|
||||||
|
<span>%fa:angle-up%{{ note._datetext }}</span>
|
||||||
|
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
|
||||||
|
<footer v-if="more">
|
||||||
|
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||||
|
<template v-if="!moreFetching">%i18n:@load-more%</template>
|
||||||
|
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<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;
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XNote
|
||||||
|
},
|
||||||
|
|
||||||
|
inject: ['column', 'isScrollTop', 'indicate'],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
more: {
|
||||||
|
type: Function,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
rootEl: null,
|
||||||
|
requestInitPromise: null as () => Promise<any[]>,
|
||||||
|
notes: [],
|
||||||
|
queue: [],
|
||||||
|
unreadCount: 0,
|
||||||
|
fetching: true,
|
||||||
|
moreFetching: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
_notes(): any[] {
|
||||||
|
return (this.notes as any).map(note => {
|
||||||
|
const date = new Date(note.createdAt).getDate();
|
||||||
|
const month = new Date(note.createdAt).getMonth() + 1;
|
||||||
|
note._date = date;
|
||||||
|
note._datetext = `${month}月 ${date}日`;
|
||||||
|
return note;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.column.$on('top', this.onTop);
|
||||||
|
this.column.$on('bottom', this.onBottom);
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.column.$off('top', this.onTop);
|
||||||
|
this.column.$off('bottom', this.onBottom);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
(this.$el as any).children[0].focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
onNoteUpdated(i, note) {
|
||||||
|
Vue.set((this as any).notes, i, note);
|
||||||
|
},
|
||||||
|
|
||||||
|
init(promiseGenerator: () => Promise<any[]>) {
|
||||||
|
this.requestInitPromise = promiseGenerator;
|
||||||
|
this.resolveInitPromise();
|
||||||
|
},
|
||||||
|
|
||||||
|
resolveInitPromise() {
|
||||||
|
this.queue = [];
|
||||||
|
this.notes = [];
|
||||||
|
this.fetching = true;
|
||||||
|
|
||||||
|
const promise = this.requestInitPromise();
|
||||||
|
|
||||||
|
promise.then(notes => {
|
||||||
|
this.notes = notes;
|
||||||
|
this.requestInitPromise = null;
|
||||||
|
this.fetching = false;
|
||||||
|
}, e => {
|
||||||
|
this.fetching = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
prepend(note, silent = false) {
|
||||||
|
//#region 弾く
|
||||||
|
const isMyNote = note.userId == this.$store.state.i.id;
|
||||||
|
const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
|
||||||
|
|
||||||
|
if (this.$store.state.settings.showMyRenotes === false) {
|
||||||
|
if (isMyNote && isPureRenote) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$store.state.settings.showRenotedMyNotes === false) {
|
||||||
|
if (isPureRenote && (note.renote.userId == this.$store.state.i.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
if (this.isScrollTop()) {
|
||||||
|
// Prepend the note
|
||||||
|
this.notes.unshift(note);
|
||||||
|
|
||||||
|
// オーバーフローしたら古い投稿は捨てる
|
||||||
|
if (this.notes.length >= displayLimit) {
|
||||||
|
this.notes = this.notes.slice(0, displayLimit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.queue.push(note);
|
||||||
|
this.indicate(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
append(note) {
|
||||||
|
this.notes.push(note);
|
||||||
|
},
|
||||||
|
|
||||||
|
tail() {
|
||||||
|
return this.notes[this.notes.length - 1];
|
||||||
|
},
|
||||||
|
|
||||||
|
releaseQueue() {
|
||||||
|
this.queue.forEach(n => this.prepend(n, true));
|
||||||
|
this.queue = [];
|
||||||
|
this.indicate(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadMore() {
|
||||||
|
if (this.more == null) return;
|
||||||
|
if (this.moreFetching) return;
|
||||||
|
|
||||||
|
this.moreFetching = true;
|
||||||
|
await this.more();
|
||||||
|
this.moreFetching = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onTop() {
|
||||||
|
this.releaseQueue();
|
||||||
|
},
|
||||||
|
|
||||||
|
onBottom() {
|
||||||
|
this.loadMore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
.transition
|
||||||
|
.mk-notes-enter
|
||||||
|
.mk-notes-leave-to
|
||||||
|
opacity 0
|
||||||
|
transform translateY(-30px)
|
||||||
|
|
||||||
|
> *
|
||||||
|
transition transform .3s ease, opacity .3s ease
|
||||||
|
|
||||||
|
> .date
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
line-height 32px
|
||||||
|
font-size 14px
|
||||||
|
text-align center
|
||||||
|
color isDark ? #666b79 : #aaa
|
||||||
|
background isDark ? #242731 : #fdfdfd
|
||||||
|
border-bottom solid 1px isDark ? #1c2023 : #eaeaea
|
||||||
|
|
||||||
|
span
|
||||||
|
margin 0 16px
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
margin-right 8px
|
||||||
|
|
||||||
|
> footer
|
||||||
|
> button
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 16px
|
||||||
|
width 100%
|
||||||
|
text-align center
|
||||||
|
color #ccc
|
||||||
|
background isDark ? #282C37 : #fff
|
||||||
|
border-top solid 1px isDark ? #1c2023 : #eaeaea
|
||||||
|
border-bottom-left-radius 6px
|
||||||
|
border-bottom-right-radius 6px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background isDark ? #2e3440 : #f5f5f5
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background isDark ? #21242b : #eee
|
||||||
|
|
||||||
|
.eamppglmnmimdhrlzhplwpvyeaqmmhxu[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.eamppglmnmimdhrlzhplwpvyeaqmmhxu:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
179
src/client/app/desktop/views/pages/deck/deck.notification.vue
Normal file
179
src/client/app/desktop/views/pages/deck/deck.notification.vue
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dsfykdcjpuwfvpefwufddclpjhzktmpw">
|
||||||
|
<div class="notification reaction" v-if="notification.type == 'reaction'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<mk-reaction-icon :reaction="notification.reaction"/>
|
||||||
|
<router-link :to="notification.user | userPage">{{ notification.user | userName }}</router-link>
|
||||||
|
<mk-time :time="notification.createdAt"/>
|
||||||
|
</header>
|
||||||
|
<router-link class="note-ref" :to="notification.note | notePage">
|
||||||
|
%fa:quote-left%{{ getNoteSummary(notification.note) }}
|
||||||
|
%fa:quote-right%
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification renote" v-if="notification.type == 'renote'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
%fa:retweet%
|
||||||
|
<router-link :to="notification.user | userPage">{{ notification.user | userName }}</router-link>
|
||||||
|
<mk-time :time="notification.createdAt"/>
|
||||||
|
</header>
|
||||||
|
<router-link class="note-ref" :to="notification.note | notePage">
|
||||||
|
%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification follow" v-if="notification.type == 'follow'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
%fa:user-plus%
|
||||||
|
<router-link :to="notification.user | userPage">{{ notification.user | userName }}</router-link>
|
||||||
|
<mk-time :time="notification.createdAt"/>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification followRequest" v-if="notification.type == 'receiveFollowRequest'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
%fa:user-clock%
|
||||||
|
<router-link :to="notification.user | userPage">{{ notification.user | userName }}</router-link>
|
||||||
|
<mk-time :time="notification.createdAt"/>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification poll_vote" v-if="notification.type == 'poll_vote'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
%fa:chart-pie%
|
||||||
|
<router-link :to="notification.user | userPage">{{ notification.user | userName }}</router-link>
|
||||||
|
<mk-time :time="notification.createdAt"/>
|
||||||
|
</header>
|
||||||
|
<router-link class="note-ref" :to="notification.note | notePage">
|
||||||
|
%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="notification.type == 'quote'">
|
||||||
|
<x-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="notification.type == 'reply'">
|
||||||
|
<x-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="notification.type == 'mention'">
|
||||||
|
<x-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import getNoteSummary from '../../../../../../renderers/get-note-summary';
|
||||||
|
import XNote from './deck.note.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XNote
|
||||||
|
},
|
||||||
|
props: ['notification'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
getNoteSummary
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNoteUpdated(note) {
|
||||||
|
switch (this.notification.type) {
|
||||||
|
case 'quote':
|
||||||
|
case 'reply':
|
||||||
|
case 'mention':
|
||||||
|
Vue.set(this.notification, 'note', note);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
root(isDark)
|
||||||
|
> .notification
|
||||||
|
padding 16px
|
||||||
|
font-size 12px
|
||||||
|
overflow-wrap break-word
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
display block
|
||||||
|
float left
|
||||||
|
width 36px
|
||||||
|
height 36px
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
|
> div
|
||||||
|
float right
|
||||||
|
width calc(100% - 36px)
|
||||||
|
padding-left 8px
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
align-items baseline
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
|
i, .mk-reaction-icon
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
> .mk-time
|
||||||
|
margin-left auto
|
||||||
|
color isDark ? #606984 : #c0c0c0
|
||||||
|
font-size 0.9em
|
||||||
|
|
||||||
|
> .note-preview
|
||||||
|
color isDark ? #fff : #717171
|
||||||
|
|
||||||
|
> .note-ref
|
||||||
|
color isDark ? #fff : #717171
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
font-size 1em
|
||||||
|
font-weight normal
|
||||||
|
font-style normal
|
||||||
|
display inline-block
|
||||||
|
margin-right 3px
|
||||||
|
|
||||||
|
&.renote
|
||||||
|
> div > header i
|
||||||
|
color #77B255
|
||||||
|
|
||||||
|
&.follow
|
||||||
|
> div > header i
|
||||||
|
color #53c7ce
|
||||||
|
|
||||||
|
&.receiveFollowRequest
|
||||||
|
> div > header i
|
||||||
|
color #888
|
||||||
|
|
||||||
|
.dsfykdcjpuwfvpefwufddclpjhzktmpw[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.dsfykdcjpuwfvpefwufddclpjhzktmpw:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<x-column :id="column.id" :name="name">
|
||||||
|
<span slot="header">%fa:bell R%{{ name }}</span>
|
||||||
|
|
||||||
|
<x-notifications/>
|
||||||
|
</x-column>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XColumn from './deck.column.vue';
|
||||||
|
import XNotifications from './deck.notifications.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XColumn,
|
||||||
|
XNotifications
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
column: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
name(): string {
|
||||||
|
if (this.column.name) return this.column.name;
|
||||||
|
return '%i18n:common.deck.notifications%';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
179
src/client/app/desktop/views/pages/deck/deck.notifications.vue
Normal file
179
src/client/app/desktop/views/pages/deck/deck.notifications.vue
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<div class="oxynyeqmfvracxnglgulyqfgqxnxmehl">
|
||||||
|
<transition-group name="mk-notifications" class="transition notifications">
|
||||||
|
<template v-for="(notification, i) in _notifications">
|
||||||
|
<x-notification class="notification" :notification="notification" :key="notification.id"/>
|
||||||
|
<p class="date" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date" :key="notification.id + '-time'">
|
||||||
|
<span>%fa:angle-up%{{ notification._datetext }}</span>
|
||||||
|
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
||||||
|
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
|
||||||
|
</button>
|
||||||
|
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
|
||||||
|
<p class="loading" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XNotification from './deck.notification.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XNotification
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
fetchingMoreNotifications: false,
|
||||||
|
notifications: [],
|
||||||
|
moreNotifications: false,
|
||||||
|
connection: null,
|
||||||
|
connectionId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
_notifications(): any[] {
|
||||||
|
return (this.notifications as any).map(notification => {
|
||||||
|
const date = new Date(notification.createdAt).getDate();
|
||||||
|
const month = new Date(notification.createdAt).getMonth() + 1;
|
||||||
|
notification._date = date;
|
||||||
|
notification._datetext = `${month}月 ${date}日`;
|
||||||
|
return notification;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
|
||||||
|
this.connection.on('notification', this.onNotification);
|
||||||
|
|
||||||
|
const max = 10;
|
||||||
|
|
||||||
|
(this as any).api('i/notifications', {
|
||||||
|
limit: max + 1
|
||||||
|
}).then(notifications => {
|
||||||
|
if (notifications.length == max + 1) {
|
||||||
|
this.moreNotifications = true;
|
||||||
|
notifications.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifications = notifications;
|
||||||
|
this.fetching = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.connection.off('notification', this.onNotification);
|
||||||
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchMoreNotifications() {
|
||||||
|
this.fetchingMoreNotifications = true;
|
||||||
|
|
||||||
|
const max = 30;
|
||||||
|
|
||||||
|
(this as any).api('i/notifications', {
|
||||||
|
limit: max + 1,
|
||||||
|
untilId: this.notifications[this.notifications.length - 1].id
|
||||||
|
}).then(notifications => {
|
||||||
|
if (notifications.length == max + 1) {
|
||||||
|
this.moreNotifications = true;
|
||||||
|
notifications.pop();
|
||||||
|
} else {
|
||||||
|
this.moreNotifications = false;
|
||||||
|
}
|
||||||
|
this.notifications = this.notifications.concat(notifications);
|
||||||
|
this.fetchingMoreNotifications = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onNotification(notification) {
|
||||||
|
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||||
|
this.connection.send({
|
||||||
|
type: 'read_notification',
|
||||||
|
id: notification.id
|
||||||
|
});
|
||||||
|
|
||||||
|
this.notifications.unshift(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
root(isDark)
|
||||||
|
|
||||||
|
.transition
|
||||||
|
.mk-notifications-enter
|
||||||
|
.mk-notifications-leave-to
|
||||||
|
opacity 0
|
||||||
|
transform translateY(-30px)
|
||||||
|
|
||||||
|
> *
|
||||||
|
transition transform .3s ease, opacity .3s ease
|
||||||
|
|
||||||
|
> .notifications
|
||||||
|
|
||||||
|
> .notification:not(:last-child)
|
||||||
|
border-bottom solid 1px isDark ? #1c2023 : #eaeaea
|
||||||
|
|
||||||
|
> .date
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
line-height 32px
|
||||||
|
text-align center
|
||||||
|
font-size 0.8em
|
||||||
|
color isDark ? #666b79 : #aaa
|
||||||
|
background isDark ? #242731 : #fdfdfd
|
||||||
|
border-bottom solid 1px isDark ? #1c2023 : #eaeaea
|
||||||
|
|
||||||
|
span
|
||||||
|
margin 0 16px
|
||||||
|
|
||||||
|
i
|
||||||
|
margin-right 8px
|
||||||
|
|
||||||
|
> .more
|
||||||
|
display block
|
||||||
|
width 100%
|
||||||
|
padding 16px
|
||||||
|
color #555
|
||||||
|
border-top solid 1px rgba(#000, 0.05)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background rgba(#000, 0.025)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background rgba(#000, 0.05)
|
||||||
|
|
||||||
|
&.fetching
|
||||||
|
cursor wait
|
||||||
|
|
||||||
|
> [data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
> .empty
|
||||||
|
margin 0
|
||||||
|
padding 16px
|
||||||
|
text-align center
|
||||||
|
color #aaa
|
||||||
|
|
||||||
|
> .loading
|
||||||
|
margin 0
|
||||||
|
padding 16px
|
||||||
|
text-align center
|
||||||
|
color #aaa
|
||||||
|
|
||||||
|
> [data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
.oxynyeqmfvracxnglgulyqfgqxnxmehl[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.oxynyeqmfvracxnglgulyqfgqxnxmehl:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
73
src/client/app/desktop/views/pages/deck/deck.tl-column.vue
Normal file
73
src/client/app/desktop/views/pages/deck/deck.tl-column.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XColumn from './deck.column.vue';
|
||||||
|
import XTl from './deck.tl.vue';
|
||||||
|
import XListTl from './deck.list-tl.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XColumn,
|
||||||
|
XTl,
|
||||||
|
XListTl
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
column: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
edit: false,
|
||||||
|
menu: [{
|
||||||
|
content: '%fa:cog% %i18n:@edit%',
|
||||||
|
onClick: () => {
|
||||||
|
this.edit = !this.edit;
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
name(): string {
|
||||||
|
if (this.column.name) return this.column.name;
|
||||||
|
|
||||||
|
switch (this.column.type) {
|
||||||
|
case 'home': return '%i18n:common.deck.home%';
|
||||||
|
case 'local': return '%i18n:common.deck.local%';
|
||||||
|
case 'global': return '%i18n:common.deck.global%';
|
||||||
|
case 'list': return this.column.list.title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onChangeSettings(v) {
|
||||||
|
this.$store.dispatch('settings/saveDeck');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
147
src/client/app/desktop/views/pages/deck/deck.tl.vue
Normal file
147
src/client/app/desktop/views/pages/deck/deck.tl.vue
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<x-notes ref="timeline" :more="existMore ? more : null"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XNotes from './deck.notes.vue';
|
||||||
|
|
||||||
|
const fetchLimit = 10;
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XNotes
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'home'
|
||||||
|
},
|
||||||
|
mediaOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
moreFetching: false,
|
||||||
|
existMore: false,
|
||||||
|
connection: null,
|
||||||
|
connectionId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
mediaOnly() {
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
stream(): any {
|
||||||
|
return this.src == 'home'
|
||||||
|
? (this as any).os.stream
|
||||||
|
: this.src == 'local'
|
||||||
|
? (this as any).os.streams.localTimelineStream
|
||||||
|
: (this as any).os.streams.globalTimelineStream;
|
||||||
|
},
|
||||||
|
|
||||||
|
endpoint(): string {
|
||||||
|
return this.src == 'home'
|
||||||
|
? 'notes/timeline'
|
||||||
|
: this.src == 'local'
|
||||||
|
? 'notes/local-timeline'
|
||||||
|
: 'notes/global-timeline';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.connection = this.stream.getConnection();
|
||||||
|
this.connectionId = this.stream.use();
|
||||||
|
|
||||||
|
this.connection.on('note', this.onNote);
|
||||||
|
if (this.src == 'home') {
|
||||||
|
this.connection.on('follow', this.onChangeFollowing);
|
||||||
|
this.connection.on('unfollow', this.onChangeFollowing);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.connection.off('note', this.onNote);
|
||||||
|
if (this.src == 'home') {
|
||||||
|
this.connection.off('follow', this.onChangeFollowing);
|
||||||
|
this.connection.off('unfollow', this.onChangeFollowing);
|
||||||
|
}
|
||||||
|
this.stream.dispose(this.connectionId);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
this.fetching = true;
|
||||||
|
|
||||||
|
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
|
||||||
|
(this as any).api(this.endpoint, {
|
||||||
|
limit: fetchLimit + 1,
|
||||||
|
mediaOnly: this.mediaOnly,
|
||||||
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||||
|
}).then(notes => {
|
||||||
|
if (notes.length == fetchLimit + 1) {
|
||||||
|
notes.pop();
|
||||||
|
this.existMore = true;
|
||||||
|
}
|
||||||
|
res(notes);
|
||||||
|
this.fetching = false;
|
||||||
|
this.$emit('loaded');
|
||||||
|
}, rej);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
more() {
|
||||||
|
this.moreFetching = true;
|
||||||
|
|
||||||
|
const promise = (this as any).api(this.endpoint, {
|
||||||
|
limit: fetchLimit + 1,
|
||||||
|
mediaOnly: this.mediaOnly,
|
||||||
|
untilId: (this.$refs.timeline as any).tail().id,
|
||||||
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.then(notes => {
|
||||||
|
if (notes.length == fetchLimit + 1) {
|
||||||
|
notes.pop();
|
||||||
|
} else {
|
||||||
|
this.existMore = false;
|
||||||
|
}
|
||||||
|
notes.forEach(n => (this.$refs.timeline as any).append(n));
|
||||||
|
this.moreFetching = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
onNote(note) {
|
||||||
|
if (this.mediaOnly && note.media.length == 0) return;
|
||||||
|
|
||||||
|
// Prepend a note
|
||||||
|
(this.$refs.timeline as any).prepend(note);
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeFollowing() {
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
(this.$refs.timeline as any).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
185
src/client/app/desktop/views/pages/deck/deck.vue
Normal file
185
src/client/app/desktop/views/pages/deck/deck.vue
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<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>
|
||||||
|
<button ref="add" @click="add" title="%i18n:common.deck.add-column%">%fa:plus%</button>
|
||||||
|
</div>
|
||||||
|
</mk-ui>
|
||||||
|
</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';
|
||||||
|
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
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
columns() {
|
||||||
|
if (this.$store.state.settings.deck == null) return [];
|
||||||
|
return this.$store.state.settings.deck.columns;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.$store.state.settings.deck == null) {
|
||||||
|
const deck = {
|
||||||
|
columns: [/*{
|
||||||
|
type: 'widgets',
|
||||||
|
widgets: []
|
||||||
|
}, */{
|
||||||
|
id: uuid(),
|
||||||
|
type: 'home'
|
||||||
|
}, {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'notifications'
|
||||||
|
}, {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'local'
|
||||||
|
}, {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'global'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$store.dispatch('settings/set', {
|
||||||
|
key: 'deck',
|
||||||
|
value: deck
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
document.documentElement.style.overflow = 'hidden';
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
document.documentElement.style.overflow = 'auto';
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
add() {
|
||||||
|
this.os.new(Menu, {
|
||||||
|
source: this.$refs.add,
|
||||||
|
compact: true,
|
||||||
|
items: [{
|
||||||
|
content: '%i18n:common.deck.home%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/addDeckColumn', {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'home'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
content: '%i18n:common.deck.local%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/addDeckColumn', {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'local'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
content: '%i18n:common.deck.global%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/addDeckColumn', {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'global'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
content: '%i18n:common.deck.list%',
|
||||||
|
onClick: () => {
|
||||||
|
const w = (this as any).os.new(MkUserListsWindow);
|
||||||
|
w.$once('choosen', list => {
|
||||||
|
this.$store.dispatch('settings/addDeckColumn', {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'list',
|
||||||
|
list: list
|
||||||
|
});
|
||||||
|
w.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
content: '%i18n:common.deck.notifications%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/addDeckColumn', {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'notifications'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
content: '%i18n:common.deck.widgets%',
|
||||||
|
onClick: () => {
|
||||||
|
this.$store.dispatch('settings/addDeckColumn', {
|
||||||
|
id: uuid(),
|
||||||
|
type: 'widgets',
|
||||||
|
widgets: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" module>
|
||||||
|
.root
|
||||||
|
height 100vh
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
display flex
|
||||||
|
flex 1
|
||||||
|
padding 16px 0 16px 16px
|
||||||
|
overflow auto
|
||||||
|
|
||||||
|
> div
|
||||||
|
margin-right 8px
|
||||||
|
|
||||||
|
&:last-of-type
|
||||||
|
margin-right 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
&:first-child
|
||||||
|
margin-left auto
|
||||||
|
|
||||||
|
&:last-child
|
||||||
|
margin-right auto
|
||||||
|
|
||||||
|
> button
|
||||||
|
padding 0 16px
|
||||||
|
color isDark ? #93a0a5 : #888
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color isDark ? #b8c5ca : #777
|
||||||
|
|
||||||
|
&:active
|
||||||
|
color isDark ? #fff : #555
|
||||||
|
|
||||||
|
.qlvquzbjribqcaozciifydkngcwtyzje[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.qlvquzbjribqcaozciifydkngcwtyzje:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
159
src/client/app/desktop/views/pages/deck/deck.widgets-column.vue
Normal file
159
src/client/app/desktop/views/pages/deck/deck.widgets-column.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wtdtxvecapixsepjtcupubtsmometobz">
|
||||||
|
<x-column :id="column.id" :menu="menu" :naked="true" :narrow="true" :name="name">
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XColumn from './deck.column.vue';
|
||||||
|
import * as XDraggable from 'vuedraggable';
|
||||||
|
import * as uuid from 'uuid';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XColumn,
|
||||||
|
XDraggable
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
column: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
edit: false,
|
||||||
|
menu: null,
|
||||||
|
widgetAdderSelected: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
name(): string {
|
||||||
|
if (this.column.name) return this.column.name;
|
||||||
|
return '%i18n:common.deck.widgets%';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.menu = [{
|
||||||
|
content: '%fa:cog% %i18n:@edit%',
|
||||||
|
onClick: () => {
|
||||||
|
this.edit = !this.edit;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
widgetFunc(id) {
|
||||||
|
const w = this.$refs[id][0];
|
||||||
|
if (w.func) w.func();
|
||||||
|
},
|
||||||
|
|
||||||
|
onWidgetSort() {
|
||||||
|
this.saveWidgets();
|
||||||
|
},
|
||||||
|
|
||||||
|
addWidget() {
|
||||||
|
this.$store.dispatch('settings/addDeckWidget', {
|
||||||
|
id: this.column.id,
|
||||||
|
widget: {
|
||||||
|
name: this.widgetAdderSelected,
|
||||||
|
id: uuid(),
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeWidget(widget) {
|
||||||
|
this.$store.dispatch('settings/removeDeckWidget', {
|
||||||
|
id: this.column.id,
|
||||||
|
widget
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
saveWidgets() {
|
||||||
|
this.$store.dispatch('settings/saveDeck');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
.gqpwvtwtprsbmnssnbicggtwqhmylhnq
|
||||||
|
.widget, .customize-container
|
||||||
|
margin 8px
|
||||||
|
|
||||||
|
&:first-of-type
|
||||||
|
margin-top 0
|
||||||
|
|
||||||
|
.customize-container
|
||||||
|
background #fff
|
||||||
|
|
||||||
|
> header
|
||||||
|
color isDark ? #fff : #000
|
||||||
|
|
||||||
|
.wtdtxvecapixsepjtcupubtsmometobz[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.wtdtxvecapixsepjtcupubtsmometobz:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@@ -46,7 +46,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('keydown', this.onDocumentKeydown);
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.addEventListener('scroll', this.onScroll);
|
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||||
|
|
||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
|
@@ -37,7 +37,7 @@ export default Vue.extend({
|
|||||||
mounted() {
|
mounted() {
|
||||||
if (this.user.bannerUrl) {
|
if (this.user.bannerUrl) {
|
||||||
window.addEventListener('load', this.onScroll);
|
window.addEventListener('load', this.onScroll);
|
||||||
window.addEventListener('scroll', this.onScroll);
|
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||||
window.addEventListener('resize', this.onScroll);
|
window.addEventListener('resize', this.onScroll);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -83,9 +83,7 @@ export default Vue.extend({
|
|||||||
@import url(https://fonts.googleapis.com/earlyaccess/notosansjp.css);
|
@import url(https://fonts.googleapis.com/earlyaccess/notosansjp.css);
|
||||||
|
|
||||||
root(isDark)
|
root(isDark)
|
||||||
display flex
|
min-height 100vh
|
||||||
flex-direction column
|
|
||||||
flex 1
|
|
||||||
background-image isDark ? url('/assets/welcome-bg.dark.svg') : url('/assets/welcome-bg.light.svg')
|
background-image isDark ? url('/assets/welcome-bg.dark.svg') : url('/assets/welcome-bg.light.svg')
|
||||||
background-size cover
|
background-size cover
|
||||||
background-position center
|
background-position center
|
||||||
|
@@ -73,12 +73,12 @@ export default class MiOS extends EventEmitter {
|
|||||||
public app: Vue;
|
public app: Vue;
|
||||||
|
|
||||||
public new(vm, props) {
|
public new(vm, props) {
|
||||||
const w = new vm({
|
const x = new vm({
|
||||||
parent: this.app,
|
parent: this.app,
|
||||||
propsData: props
|
propsData: props
|
||||||
}).$mount();
|
}).$mount();
|
||||||
document.body.appendChild(w.$el);
|
document.body.appendChild(x.$el);
|
||||||
return w;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
>
|
>
|
||||||
<template v-if="!wait">
|
<template v-if="!wait">
|
||||||
<template v-if="u.hasPendingFollowRequestFromYou">%fa:hourglass-half% %i18n:@request-pending%</template>
|
<template v-if="u.hasPendingFollowRequestFromYou">%fa:hourglass-half% %i18n:@request-pending%</template>
|
||||||
<template v-else-if="u.isFollowing">%fa:minus% %i18n:@unfollow%</template>
|
<template v-else-if="u.isFollowing">%fa:minus% %i18n:@following%</template>
|
||||||
<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus% %i18n:@follow-request%</template>
|
<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus% %i18n:@follow-request%</template>
|
||||||
<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus% %i18n:@follow%</template>
|
<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus% %i18n:@follow%</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -102,6 +102,7 @@ export default Vue.extend({
|
|||||||
min-width 150px
|
min-width 150px
|
||||||
line-height 36px
|
line-height 36px
|
||||||
font-size 14px
|
font-size 14px
|
||||||
|
font-weight bold
|
||||||
color $theme-color
|
color $theme-color
|
||||||
background transparent
|
background transparent
|
||||||
outline none
|
outline none
|
||||||
|
@@ -81,7 +81,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
||||||
window.addEventListener('scroll', this.onScroll);
|
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<span class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</span>
|
<span class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</span>
|
||||||
<span class="geo" v-if="geo">%fa:map-marker-alt%</span>
|
<span class="geo" v-if="geo">%fa:map-marker-alt%</span>
|
||||||
<button class="submit" :disabled="posting" @click="post">{{ submitText }}</button>
|
<button class="submit" :disabled="!canPost" @click="post">{{ submitText }}</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
|
@@ -35,13 +35,13 @@
|
|||||||
<span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
|
<span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
|
||||||
</header>
|
</header>
|
||||||
<div @click="widgetFunc(widget.id)">
|
<div @click="widgetFunc(widget.id)">
|
||||||
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" :is-mobile="true"/>
|
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="mobile"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</x-draggable>
|
</x-draggable>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" :is-mobile="true"/>
|
<component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" platform="mobile"/>
|
||||||
</template>
|
</template>
|
||||||
</main>
|
</main>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
|
@@ -5,8 +5,9 @@ import MiOS from './mios';
|
|||||||
import { hostname } from './config';
|
import { hostname } from './config';
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
home: [],
|
home: null,
|
||||||
mobileHome: [],
|
mobileHome: [],
|
||||||
|
deck: null,
|
||||||
fetchOnScroll: true,
|
fetchOnScroll: true,
|
||||||
showMaps: true,
|
showMaps: true,
|
||||||
showPostFormOnTopOfTl: false,
|
showPostFormOnTopOfTl: false,
|
||||||
@@ -123,13 +124,6 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
state.home = data;
|
state.home = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
setHomeWidget(state, x) {
|
|
||||||
const w = state.home.find(w => w.id == x.id);
|
|
||||||
if (w) {
|
|
||||||
w.data = x.data;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
addHomeWidget(state, widget) {
|
addHomeWidget(state, widget) {
|
||||||
state.home.unshift(widget);
|
state.home.unshift(widget);
|
||||||
},
|
},
|
||||||
@@ -138,11 +132,36 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
state.mobileHome = data;
|
state.mobileHome = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
setMobileHomeWidget(state, x) {
|
setWidget(state, x) {
|
||||||
const w = state.mobileHome.find(w => w.id == x.id);
|
let w;
|
||||||
if (w) {
|
|
||||||
w.data = x.data;
|
//#region Decktop home
|
||||||
|
if (state.home) {
|
||||||
|
w = state.home.find(w => w.id == x.id);
|
||||||
|
if (w) {
|
||||||
|
w.data = x.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Mobile home
|
||||||
|
if (state.mobileHome) {
|
||||||
|
w = state.mobileHome.find(w => w.id == x.id);
|
||||||
|
if (w) {
|
||||||
|
w.data = x.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Deck
|
||||||
|
if (state.deck && state.deck.columns) {
|
||||||
|
state.deck.columns.filter(c => c.type == 'widgets').forEach(c => {
|
||||||
|
c.widgets.forEach(w => {
|
||||||
|
if (w.id == x.id) w.data = x.data;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
},
|
},
|
||||||
|
|
||||||
addMobileHomeWidget(state, widget) {
|
addMobileHomeWidget(state, widget) {
|
||||||
@@ -151,11 +170,70 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
|
|
||||||
removeMobileHomeWidget(state, widget) {
|
removeMobileHomeWidget(state, widget) {
|
||||||
state.mobileHome = state.mobileHome.filter(w => w.id != widget.id);
|
state.mobileHome = state.mobileHome.filter(w => w.id != widget.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
addDeckColumn(state, column) {
|
||||||
|
if (state.deck.columns == null) state.deck.columns = [];
|
||||||
|
state.deck.columns.push(column);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeDeckColumn(state, id) {
|
||||||
|
if (state.deck.columns == null) return;
|
||||||
|
state.deck.columns = state.deck.columns.filter(c => c.id != id);
|
||||||
|
},
|
||||||
|
|
||||||
|
swapLeftDeckColumn(state, id) {
|
||||||
|
if (state.deck.columns == null) return;
|
||||||
|
state.deck.columns.some((c, i) => {
|
||||||
|
if (c.id == id) {
|
||||||
|
const left = state.deck.columns[i - 1];
|
||||||
|
if (left) {
|
||||||
|
state.deck.columns[i - 1] = state.deck.columns[i];
|
||||||
|
state.deck.columns[i] = left;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
swapRightDeckColumn(state, id) {
|
||||||
|
if (state.deck.columns == null) return;
|
||||||
|
state.deck.columns.some((c, i) => {
|
||||||
|
if (c.id == id) {
|
||||||
|
const right = state.deck.columns[i + 1];
|
||||||
|
if (right) {
|
||||||
|
state.deck.columns[i + 1] = state.deck.columns[i];
|
||||||
|
state.deck.columns[i] = right;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addDeckWidget(state, x) {
|
||||||
|
if (state.deck.columns == null) return;
|
||||||
|
const column = state.deck.columns.find(c => c.id == x.id);
|
||||||
|
if (column == null) return;
|
||||||
|
column.widgets.unshift(x.widget);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeDeckWidget(state, x) {
|
||||||
|
if (state.deck.columns == null) return;
|
||||||
|
const column = state.deck.columns.find(c => c.id == x.id);
|
||||||
|
if (column == null) return;
|
||||||
|
column.widgets = column.widgets.filter(w => w.id != x.widget.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
renameDeckColumn(state, x) {
|
||||||
|
const column = state.deck.columns.find(c => c.id == x.id);
|
||||||
|
if (column == null) return;
|
||||||
|
column.name = x.name;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
merge(ctx, settings) {
|
merge(ctx, settings) {
|
||||||
|
if (settings == null) return;
|
||||||
Object.entries(settings).forEach(([key, value]) => {
|
Object.entries(settings).forEach(([key, value]) => {
|
||||||
ctx.commit('set', { key, value });
|
ctx.commit('set', { key, value });
|
||||||
});
|
});
|
||||||
@@ -172,6 +250,48 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
saveDeck(ctx) {
|
||||||
|
os.api('i/update_client_setting', {
|
||||||
|
name: 'deck',
|
||||||
|
value: ctx.state.deck
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addDeckColumn(ctx, column) {
|
||||||
|
ctx.commit('addDeckColumn', column);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
|
removeDeckColumn(ctx, id) {
|
||||||
|
ctx.commit('removeDeckColumn', id);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
|
swapLeftDeckColumn(ctx, id) {
|
||||||
|
ctx.commit('swapLeftDeckColumn', id);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
|
swapRightDeckColumn(ctx, id) {
|
||||||
|
ctx.commit('swapRightDeckColumn', id);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
|
addDeckWidget(ctx, x) {
|
||||||
|
ctx.commit('addDeckWidget', x);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
|
removeDeckWidget(ctx, x) {
|
||||||
|
ctx.commit('removeDeckWidget', x);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
|
renameDeckColumn(ctx, x) {
|
||||||
|
ctx.commit('renameDeckColumn', x);
|
||||||
|
ctx.dispatch('saveDeck');
|
||||||
|
},
|
||||||
|
|
||||||
addHomeWidget(ctx, widget) {
|
addHomeWidget(ctx, widget) {
|
||||||
ctx.commit('addHomeWidget', widget);
|
ctx.commit('addHomeWidget', widget);
|
||||||
|
|
||||||
|
@@ -48,6 +48,7 @@ type IUserBase = {
|
|||||||
usernameLower: string;
|
usernameLower: string;
|
||||||
avatarId: mongo.ObjectID;
|
avatarId: mongo.ObjectID;
|
||||||
bannerId: mongo.ObjectID;
|
bannerId: mongo.ObjectID;
|
||||||
|
wallpaperId: mongo.ObjectID;
|
||||||
data: any;
|
data: any;
|
||||||
description: string;
|
description: string;
|
||||||
pinnedNoteId: mongo.ObjectID;
|
pinnedNoteId: mongo.ObjectID;
|
||||||
@@ -412,6 +413,10 @@ export const pack = (
|
|||||||
? `${config.drive_url}/${_user.bannerId}`
|
? `${config.drive_url}/${_user.bannerId}`
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
_user.wallpaperUrl = _user.wallpaperId != null
|
||||||
|
? `${config.drive_url}/${_user.wallpaperId}`
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
||||||
delete _user.avatarId;
|
delete _user.avatarId;
|
||||||
delete _user.bannerId;
|
delete _user.bannerId;
|
||||||
|
@@ -189,6 +189,11 @@ const endpoints: Endpoint[] = [
|
|||||||
withCredential: true,
|
withCredential: true,
|
||||||
secure: true
|
secure: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'i/update_widget',
|
||||||
|
withCredential: true,
|
||||||
|
secure: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'i/change_password',
|
name: 'i/change_password',
|
||||||
withCredential: true,
|
withCredential: true,
|
||||||
|
@@ -49,5 +49,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
|
|||||||
create(follower, followee);
|
create(follower, followee);
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res(await pack(followee, user));
|
res(await pack(followee._id, user));
|
||||||
});
|
});
|
||||||
|
@@ -49,5 +49,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
|
|||||||
deleteFollowing(follower, followee);
|
deleteFollowing(follower, followee);
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res(await pack(followee, user));
|
res(await pack(followee._id, user));
|
||||||
});
|
});
|
||||||
|
@@ -45,6 +45,11 @@ module.exports = async (params, user, app) => new Promise(async (res, rej) => {
|
|||||||
if (bannerIdErr) return rej('invalid bannerId param');
|
if (bannerIdErr) return rej('invalid bannerId param');
|
||||||
if (bannerId !== undefined) updates.bannerId = bannerId;
|
if (bannerId !== undefined) updates.bannerId = bannerId;
|
||||||
|
|
||||||
|
// Get 'wallpaperId' parameter
|
||||||
|
const [wallpaperId, wallpaperIdErr] = $.type(ID).optional().nullable().get(params.wallpaperId);
|
||||||
|
if (wallpaperIdErr) return rej('invalid wallpaperId param');
|
||||||
|
if (wallpaperId !== undefined) updates.wallpaperId = wallpaperId;
|
||||||
|
|
||||||
// Get 'isLocked' parameter
|
// Get 'isLocked' parameter
|
||||||
const [isLocked, isLockedErr] = $.bool.optional().get(params.isLocked);
|
const [isLocked, isLockedErr] = $.bool.optional().get(params.isLocked);
|
||||||
if (isLockedErr) return rej('invalid isLocked param');
|
if (isLockedErr) return rej('invalid isLocked param');
|
||||||
@@ -85,6 +90,16 @@ module.exports = async (params, user, app) => new Promise(async (res, rej) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wallpaperId) {
|
||||||
|
const wallpaper = await DriveFile.findOne({
|
||||||
|
_id: wallpaperId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (wallpaper != null && wallpaper.metadata.properties.avgColor) {
|
||||||
|
updates.wallpaperColor = wallpaper.metadata.properties.avgColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await User.update(user._id, {
|
await User.update(user._id, {
|
||||||
$set: updates
|
$set: updates
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* Module dependencies
|
|
||||||
*/
|
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import User from '../../../../models/user';
|
import User from '../../../../models/user';
|
||||||
import event from '../../../../publishers/stream';
|
import event from '../../../../publishers/stream';
|
||||||
@@ -13,50 +10,16 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
|
|||||||
.have('id', $.str)
|
.have('id', $.str)
|
||||||
.have('place', $.str)
|
.have('place', $.str)
|
||||||
.have('data', $.obj))
|
.have('data', $.obj))
|
||||||
.optional()
|
|
||||||
.get(params.home);
|
.get(params.home);
|
||||||
if (homeErr) return rej('invalid home param');
|
if (homeErr) return rej('invalid home param');
|
||||||
|
|
||||||
// Get 'id' parameter
|
await User.update(user._id, {
|
||||||
const [id, idErr] = $.str.optional().get(params.id);
|
$set: {
|
||||||
if (idErr) return rej('invalid id param');
|
'clientSettings.home': home
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get 'data' parameter
|
res();
|
||||||
const [data, dataErr] = $.obj.optional().get(params.data);
|
|
||||||
if (dataErr) return rej('invalid data param');
|
|
||||||
|
|
||||||
if (home) {
|
event(user._id, 'home_updated', home);
|
||||||
await User.update(user._id, {
|
|
||||||
$set: {
|
|
||||||
'clientSettings.home': home
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res();
|
|
||||||
|
|
||||||
event(user._id, 'home_updated', {
|
|
||||||
home
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
|
|
||||||
|
|
||||||
const _home = user.clientSettings.home;
|
|
||||||
const widget = _home.find(w => w.id == id);
|
|
||||||
|
|
||||||
if (widget == null) return rej('widget not found');
|
|
||||||
|
|
||||||
widget.data = data;
|
|
||||||
|
|
||||||
await User.update(user._id, {
|
|
||||||
$set: {
|
|
||||||
'clientSettings.home': _home
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res();
|
|
||||||
|
|
||||||
event(user._id, 'home_updated', {
|
|
||||||
id, data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* Module dependencies
|
|
||||||
*/
|
|
||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import User from '../../../../models/user';
|
import User from '../../../../models/user';
|
||||||
import event from '../../../../publishers/stream';
|
import event from '../../../../publishers/stream';
|
||||||
@@ -12,49 +9,16 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
|
|||||||
.have('name', $.str)
|
.have('name', $.str)
|
||||||
.have('id', $.str)
|
.have('id', $.str)
|
||||||
.have('data', $.obj))
|
.have('data', $.obj))
|
||||||
.optional().get(params.home);
|
.get(params.home);
|
||||||
if (homeErr) return rej('invalid home param');
|
if (homeErr) return rej('invalid home param');
|
||||||
|
|
||||||
// Get 'id' parameter
|
await User.update(user._id, {
|
||||||
const [id, idErr] = $.str.optional().get(params.id);
|
$set: {
|
||||||
if (idErr) return rej('invalid id param');
|
'clientSettings.mobileHome': home
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get 'data' parameter
|
res();
|
||||||
const [data, dataErr] = $.obj.optional().get(params.data);
|
|
||||||
if (dataErr) return rej('invalid data param');
|
|
||||||
|
|
||||||
if (home) {
|
event(user._id, 'mobile_home_updated', home);
|
||||||
await User.update(user._id, {
|
|
||||||
$set: {
|
|
||||||
'clientSettings.mobileHome': home
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res();
|
|
||||||
|
|
||||||
event(user._id, 'mobile_home_updated', {
|
|
||||||
home
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
|
|
||||||
|
|
||||||
const _home = user.clientSettings.mobileHome || [];
|
|
||||||
const widget = _home.find(w => w.id == id);
|
|
||||||
|
|
||||||
if (widget == null) return rej('widget not found');
|
|
||||||
|
|
||||||
widget.data = data;
|
|
||||||
|
|
||||||
await User.update(user._id, {
|
|
||||||
$set: {
|
|
||||||
'clientSettings.mobileHome': _home
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res();
|
|
||||||
|
|
||||||
event(user._id, 'mobile_home_updated', {
|
|
||||||
id, data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
79
src/server/api/endpoints/i/update_widget.ts
Normal file
79
src/server/api/endpoints/i/update_widget.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import $ from 'cafy';
|
||||||
|
import User from '../../../../models/user';
|
||||||
|
import event from '../../../../publishers/stream';
|
||||||
|
|
||||||
|
module.exports = async (params, user) => new Promise(async (res, rej) => {
|
||||||
|
// Get 'id' parameter
|
||||||
|
const [id, idErr] = $.str.get(params.id);
|
||||||
|
if (idErr) return rej('invalid id param');
|
||||||
|
|
||||||
|
// Get 'data' parameter
|
||||||
|
const [data, dataErr] = $.obj.get(params.data);
|
||||||
|
if (dataErr) return rej('invalid data param');
|
||||||
|
|
||||||
|
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
|
||||||
|
|
||||||
|
let widget;
|
||||||
|
|
||||||
|
//#region Desktop home
|
||||||
|
if (widget == null && user.clientSettings.home) {
|
||||||
|
const desktopHome = user.clientSettings.home;
|
||||||
|
widget = desktopHome.find(w => w.id == id);
|
||||||
|
if (widget) {
|
||||||
|
widget.data = data;
|
||||||
|
|
||||||
|
await User.update(user._id, {
|
||||||
|
$set: {
|
||||||
|
'clientSettings.home': desktopHome
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Mobile home
|
||||||
|
if (widget == null && user.clientSettings.mobileHome) {
|
||||||
|
const mobileHome = user.clientSettings.mobileHome;
|
||||||
|
widget = mobileHome.find(w => w.id == id);
|
||||||
|
if (widget) {
|
||||||
|
widget.data = data;
|
||||||
|
|
||||||
|
await User.update(user._id, {
|
||||||
|
$set: {
|
||||||
|
'clientSettings.mobileHome': mobileHome
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Deck
|
||||||
|
if (widget == null && user.clientSettings.deck && user.clientSettings.deck.columns) {
|
||||||
|
const deck = user.clientSettings.deck;
|
||||||
|
deck.columns.filter(c => c.type == 'widgets').forEach(c => {
|
||||||
|
c.widgets.forEach(w => {
|
||||||
|
if (w.id == id) widget = w;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (widget) {
|
||||||
|
widget.data = data;
|
||||||
|
|
||||||
|
await User.update(user._id, {
|
||||||
|
$set: {
|
||||||
|
'clientSettings.deck': deck
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
if (widget) {
|
||||||
|
event(user._id, 'widgetUpdated', {
|
||||||
|
id, data
|
||||||
|
});
|
||||||
|
|
||||||
|
res();
|
||||||
|
} else {
|
||||||
|
rej('widget not found');
|
||||||
|
}
|
||||||
|
});
|
@@ -35,6 +35,10 @@ module.exports = async (params, user) => {
|
|||||||
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
|
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get 'mediaOnly' parameter
|
||||||
|
const [mediaOnly, mediaOnlyErr] = $.bool.optional().get(params.mediaOnly);
|
||||||
|
if (mediaOnlyErr) throw 'invalid mediaOnly param';
|
||||||
|
|
||||||
// ミュートしているユーザーを取得
|
// ミュートしているユーザーを取得
|
||||||
const mutedUserIds = user ? (await Mute.find({
|
const mutedUserIds = user ? (await Mute.find({
|
||||||
muterId: user._id
|
muterId: user._id
|
||||||
@@ -64,6 +68,10 @@ module.exports = async (params, user) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mediaOnly) {
|
||||||
|
query.mediaIds = { $exists: true, $ne: [] };
|
||||||
|
}
|
||||||
|
|
||||||
if (sinceId) {
|
if (sinceId) {
|
||||||
sort._id = 1;
|
sort._id = 1;
|
||||||
query._id = {
|
query._id = {
|
||||||
|
@@ -35,6 +35,10 @@ module.exports = async (params, user) => {
|
|||||||
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
|
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get 'mediaOnly' parameter
|
||||||
|
const [mediaOnly, mediaOnlyErr] = $.bool.optional().get(params.mediaOnly);
|
||||||
|
if (mediaOnlyErr) throw 'invalid mediaOnly param';
|
||||||
|
|
||||||
// ミュートしているユーザーを取得
|
// ミュートしているユーザーを取得
|
||||||
const mutedUserIds = user ? (await Mute.find({
|
const mutedUserIds = user ? (await Mute.find({
|
||||||
muterId: user._id
|
muterId: user._id
|
||||||
@@ -67,6 +71,10 @@ module.exports = async (params, user) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mediaOnly) {
|
||||||
|
query.mediaIds = { $exists: true, $ne: [] };
|
||||||
|
}
|
||||||
|
|
||||||
if (sinceId) {
|
if (sinceId) {
|
||||||
sort._id = 1;
|
sort._id = 1;
|
||||||
query._id = {
|
query._id = {
|
||||||
|
@@ -44,6 +44,10 @@ module.exports = async (params, user, app) => {
|
|||||||
const [includeRenotedMyNotes = true, includeRenotedMyNotesErr] = $.bool.optional().get(params.includeRenotedMyNotes);
|
const [includeRenotedMyNotes = true, includeRenotedMyNotesErr] = $.bool.optional().get(params.includeRenotedMyNotes);
|
||||||
if (includeRenotedMyNotesErr) throw 'invalid includeRenotedMyNotes param';
|
if (includeRenotedMyNotesErr) throw 'invalid includeRenotedMyNotes param';
|
||||||
|
|
||||||
|
// Get 'mediaOnly' parameter
|
||||||
|
const [mediaOnly, mediaOnlyErr] = $.bool.optional().get(params.mediaOnly);
|
||||||
|
if (mediaOnlyErr) throw 'invalid mediaOnly param';
|
||||||
|
|
||||||
const [followings, mutedUserIds] = await Promise.all([
|
const [followings, mutedUserIds] = await Promise.all([
|
||||||
// フォローを取得
|
// フォローを取得
|
||||||
// Fetch following
|
// Fetch following
|
||||||
@@ -137,6 +141,12 @@ module.exports = async (params, user, app) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mediaOnly) {
|
||||||
|
query.$and.push({
|
||||||
|
mediaIds: { $exists: true, $ne: [] }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (sinceId) {
|
if (sinceId) {
|
||||||
sort._id = 1;
|
sort._id = 1;
|
||||||
query._id = {
|
query._id = {
|
||||||
|
@@ -44,6 +44,10 @@ module.exports = async (params, user, app) => {
|
|||||||
const [includeRenotedMyNotes = true, includeRenotedMyNotesErr] = $.bool.optional().get(params.includeRenotedMyNotes);
|
const [includeRenotedMyNotes = true, includeRenotedMyNotesErr] = $.bool.optional().get(params.includeRenotedMyNotes);
|
||||||
if (includeRenotedMyNotesErr) throw 'invalid includeRenotedMyNotes param';
|
if (includeRenotedMyNotesErr) throw 'invalid includeRenotedMyNotes param';
|
||||||
|
|
||||||
|
// Get 'mediaOnly' parameter
|
||||||
|
const [mediaOnly, mediaOnlyErr] = $.bool.optional().get(params.mediaOnly);
|
||||||
|
if (mediaOnlyErr) throw 'invalid mediaOnly param';
|
||||||
|
|
||||||
// Get 'listId' parameter
|
// Get 'listId' parameter
|
||||||
const [listId, listIdErr] = $.type(ID).get(params.listId);
|
const [listId, listIdErr] = $.type(ID).get(params.listId);
|
||||||
if (listIdErr) throw 'invalid listId param';
|
if (listIdErr) throw 'invalid listId param';
|
||||||
@@ -146,6 +150,12 @@ module.exports = async (params, user, app) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mediaOnly) {
|
||||||
|
query.$and.push({
|
||||||
|
mediaIds: { $exists: true, $ne: [] }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (sinceId) {
|
if (sinceId) {
|
||||||
sort._id = 1;
|
sort._id = 1;
|
||||||
query._id = {
|
query._id = {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import * as uuid from 'uuid';
|
|
||||||
import * as Koa from 'koa';
|
import * as Koa from 'koa';
|
||||||
import * as bcrypt from 'bcryptjs';
|
import * as bcrypt from 'bcryptjs';
|
||||||
import { generate as generateKeypair } from '../../../crypto_key';
|
import { generate as generateKeypair } from '../../../crypto_key';
|
||||||
@@ -11,28 +10,6 @@ recaptcha.init({
|
|||||||
secret_key: config.recaptcha.secret_key
|
secret_key: config.recaptcha.secret_key
|
||||||
});
|
});
|
||||||
|
|
||||||
const home = {
|
|
||||||
left: [
|
|
||||||
'profile',
|
|
||||||
'calendar',
|
|
||||||
'activity',
|
|
||||||
'rss',
|
|
||||||
'trends',
|
|
||||||
'photo-stream',
|
|
||||||
'version'
|
|
||||||
],
|
|
||||||
right: [
|
|
||||||
'broadcast',
|
|
||||||
'notifications',
|
|
||||||
'users',
|
|
||||||
'polls',
|
|
||||||
'server',
|
|
||||||
'donation',
|
|
||||||
'nav',
|
|
||||||
'tips'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async (ctx: Koa.Context) => {
|
export default async (ctx: Koa.Context) => {
|
||||||
// Verify recaptcha
|
// Verify recaptcha
|
||||||
// ただしテスト時はこの機構は障害となるため無効にする
|
// ただしテスト時はこの機構は障害となるため無効にする
|
||||||
@@ -82,28 +59,6 @@ export default async (ctx: Koa.Context) => {
|
|||||||
// Generate secret
|
// Generate secret
|
||||||
const secret = generateUserToken();
|
const secret = generateUserToken();
|
||||||
|
|
||||||
//#region Construct home data
|
|
||||||
const homeData = [];
|
|
||||||
|
|
||||||
home.left.forEach(widget => {
|
|
||||||
homeData.push({
|
|
||||||
name: widget,
|
|
||||||
id: uuid(),
|
|
||||||
place: 'left',
|
|
||||||
data: {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
home.right.forEach(widget => {
|
|
||||||
homeData.push({
|
|
||||||
name: widget,
|
|
||||||
id: uuid(),
|
|
||||||
place: 'right',
|
|
||||||
data: {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
// Create account
|
// Create account
|
||||||
const account: IUser = await User.insert({
|
const account: IUser = await User.insert({
|
||||||
avatarId: null,
|
avatarId: null,
|
||||||
@@ -135,9 +90,6 @@ export default async (ctx: Koa.Context) => {
|
|||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
autoWatch: true
|
autoWatch: true
|
||||||
},
|
|
||||||
clientSettings: {
|
|
||||||
home: homeData
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -221,7 +221,9 @@ export default async (user: IUser, data: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish note to global timeline stream
|
// Publish note to global timeline stream
|
||||||
publishGlobalTimelineStream(noteObj);
|
if (note.visibility == 'public' && note.replyId == null) {
|
||||||
|
publishGlobalTimelineStream(noteObj);
|
||||||
|
}
|
||||||
|
|
||||||
if (note.visibility == 'specified') {
|
if (note.visibility == 'specified') {
|
||||||
data.visibleUsers.forEach(async u => {
|
data.visibleUsers.forEach(async u => {
|
||||||
|
Reference in New Issue
Block a user