Compare commits
178 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 | |||
255c07d1ab | |||
83e9711274 | |||
0aa9201770 | |||
534e43f72d | |||
8f50080647 | |||
cdc70875e5 | |||
e6962d6fab | |||
3703563939 | |||
e81b145735 | |||
7f4145ee56 | |||
3967cf40b3 | |||
84b0d56c4c | |||
e081d8d4ca | |||
b6ad7149d8 | |||
0f36f60cb4 | |||
1284eef9e2 | |||
dec264ee6a | |||
e25e1d88d6 | |||
60a7f7f146 | |||
897f7a031d | |||
4feff8835c | |||
8dfd892b71 | |||
9e8cfd76c8 | |||
a6a4bb6599 | |||
5ca8a0d886 | |||
6840496791 | |||
0128831649 | |||
56fa24e401 | |||
e011870a60 | |||
8d78ee08c1 | |||
2752319e50 | |||
a26c19cbd2 | |||
f14571dc42 | |||
484d17f53f | |||
924119651a | |||
c6d49dacbc | |||
0be790fa31 | |||
b7f6eb8290 | |||
f1bda0b2e1 | |||
bae44b4708 | |||
35115607bc | |||
51255bb446 | |||
bd758a156e | |||
51929fb607 | |||
9599a31239 | |||
9fdb125960 | |||
534c0a6001 | |||
58bfcfda91 | |||
8d0802f05d | |||
5cd8c5d229 | |||
fa3c4436d9 | |||
d32d95918c | |||
34899757d5 | |||
111dbdcd7f | |||
0c38509f1c | |||
652aa1f69b | |||
dc380c38da | |||
8555e04f50 | |||
c23bbf81f1 | |||
7dd7de8ff6 | |||
2ca8bafde3 | |||
79f6c3f1ca | |||
fce0b315cf | |||
56c7a8f2e4 | |||
5ef2f157f2 | |||
738afbe475 | |||
791a81a4c7 | |||
aa82d7a2c9 | |||
f57d2e54d2 | |||
fea1a2e51b | |||
bda5347f1e | |||
98d9c37922 | |||
e3bde41a25 | |||
5fb2f7749d | |||
a56bdf2372 | |||
9d991df32f | |||
c4a3f89d1c | |||
ea223bab51 | |||
dd94392317 | |||
baa2845916 | |||
97ae4ea13e | |||
d1c5f0c70f | |||
95bff3005f | |||
c0b06496b1 | |||
2105e1f259 | |||
e546414c2f | |||
1f4660a930 | |||
a2165c2e01 | |||
1af920739f | |||
868e8228f0 | |||
2bbc74560d | |||
5d2caa456d | |||
9069a99a15 | |||
fa56a44d85 | |||
248acaee75 | |||
ef75f12abe |
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -47,7 +47,14 @@ You need to generate config file via `npm run config` command.
|
|||||||
|
|
||||||
*5.* Build Misskey
|
*5.* Build Misskey
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
We need to use `node-gyp` to build the `crypto` module.
|
|
||||||
|
Build misskey with the following:
|
||||||
|
|
||||||
|
`npm run build`
|
||||||
|
|
||||||
|
If you're on Debian, you will need to install the `build-essential` package.
|
||||||
|
|
||||||
|
If you're still encountering errors about some modules, use node-gyp:
|
||||||
|
|
||||||
1. `npm install -g node-gyp`
|
1. `npm install -g node-gyp`
|
||||||
2. `node-gyp configure`
|
2. `node-gyp configure`
|
||||||
|
155
locales/de.yml
155
locales/de.yml
@ -36,39 +36,50 @@ common:
|
|||||||
confused: "Verwirrt"
|
confused: "Verwirrt"
|
||||||
pudding: "Pudding"
|
pudding: "Pudding"
|
||||||
note-placeholders:
|
note-placeholders:
|
||||||
a: "今どうしてる?"
|
a: "Was machst du gerade?"
|
||||||
b: "何かありましたか?"
|
b: "Was ist so passiert?"
|
||||||
c: "何をお考えですか?"
|
c: "Was geht dir durch den Kopf?"
|
||||||
d: "言いたいことは?"
|
d: "Willst du etwas sagen?"
|
||||||
e: "ここに書いてください"
|
e: "Schreib hier etwas!"
|
||||||
f: "あなたが書くのを待っています..."
|
f: "Warte darauf, das du schreibst."
|
||||||
delete: "Löschen"
|
delete: "Löschen"
|
||||||
loading: "Laden"
|
loading: "Laden"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden"
|
update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden"
|
||||||
my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet."
|
my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet."
|
||||||
widgets:
|
widgets:
|
||||||
analog-clock: "アナログ時計"
|
analog-clock: "Analoge Uhr"
|
||||||
profile: "プロフィール"
|
profile: "Profil"
|
||||||
calendar: "カレンダー"
|
calendar: "Kalender"
|
||||||
timemachine: "カレンダー(タイムマシン)"
|
timemachine: "Kalender (Zeitmaschiene)"
|
||||||
activity: "アクティビティ"
|
activity: "Aktivitäten"
|
||||||
rss: "RSSリーダー"
|
rss: "RSS Leser"
|
||||||
memo: "メモ"
|
memo: "Notizen"
|
||||||
trends: "トレンド"
|
trends: "Trends"
|
||||||
photo-stream: "フォトストリーム"
|
photo-stream: "Bilder"
|
||||||
slideshow: "スライドショー"
|
slideshow: "Diashow"
|
||||||
version: "バージョン"
|
version: "Version"
|
||||||
broadcast: "ブロードキャスト"
|
broadcast: "ブロードキャスト"
|
||||||
notifications: "通知"
|
notifications: "Benachrichtigungen"
|
||||||
users: "おすすめユーザー"
|
users: "Empfohlene Benutzer"
|
||||||
polls: "投票"
|
polls: "Umfragen"
|
||||||
post-form: "投稿フォーム"
|
post-form: "投稿フォーム"
|
||||||
messaging: "メッセージ"
|
messaging: "Nachrichten"
|
||||||
server: "サーバー情報"
|
server: "Server-Info"
|
||||||
donation: "寄付のお願い"
|
donation: "Spenden"
|
||||||
nav: "ナビゲーション"
|
nav: "Navigation"
|
||||||
tips: "ヒント"
|
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."
|
||||||
@ -123,8 +134,8 @@ common/views/components/nav.vue:
|
|||||||
common/views/components/note-menu.vue:
|
common/views/components/note-menu.vue:
|
||||||
favorite: "Diese Anmerkung favorisieren"
|
favorite: "Diese Anmerkung favorisieren"
|
||||||
pin: "An die Profilseite pinnen"
|
pin: "An die Profilseite pinnen"
|
||||||
delete: "削除"
|
delete: "Löschen"
|
||||||
delete-confirm: "この投稿を削除しますか?"
|
delete-confirm: "Diesen Post löschen?"
|
||||||
remote: "Auf Quelle anzeigen"
|
remote: "Auf Quelle anzeigen"
|
||||||
common/views/components/poll.vue:
|
common/views/components/poll.vue:
|
||||||
vote-to: "Stimme für '{}'"
|
vote-to: "Stimme für '{}'"
|
||||||
@ -185,14 +196,14 @@ common/views/components/twitter-setting.vue:
|
|||||||
common/views/components/uploader.vue:
|
common/views/components/uploader.vue:
|
||||||
waiting: "Warten"
|
waiting: "Warten"
|
||||||
common/views/components/visibility-chooser.vue:
|
common/views/components/visibility-chooser.vue:
|
||||||
public: "公開"
|
public: "Öffentlich"
|
||||||
home: "ホーム"
|
home: "Home"
|
||||||
home-desc: "ホームタイムラインにのみ公開"
|
home-desc: "ホームタイムラインにのみ公開"
|
||||||
followers: "フォロワー"
|
followers: "Folgende"
|
||||||
followers-desc: "自分のフォロワーにのみ公開"
|
followers-desc: "Nur für diejenigen sichtbar, die dir folgen"
|
||||||
specified: "ダイレクト"
|
specified: "Direkt"
|
||||||
specified-desc: "指定したユーザーにのみ公開"
|
specified-desc: "Poste nur für bestimmte Benutzer"
|
||||||
private: "非公開"
|
private: "Privat"
|
||||||
common/views/widgets/broadcast.vue:
|
common/views/widgets/broadcast.vue:
|
||||||
fetching: "Laden"
|
fetching: "Laden"
|
||||||
no-broadcasts: "Keine Broadcasts"
|
no-broadcasts: "Keine Broadcasts"
|
||||||
@ -208,9 +219,9 @@ common/views/widgets/server.vue:
|
|||||||
title: "Serverinformationen"
|
title: "Serverinformationen"
|
||||||
toggle: "Sicht umschalten"
|
toggle: "Sicht umschalten"
|
||||||
common/views/widgets/memo.vue:
|
common/views/widgets/memo.vue:
|
||||||
title: "メモ"
|
title: "Notizen"
|
||||||
memo: "ここに書いて!"
|
memo: "Schreib hier!"
|
||||||
save: "保存"
|
save: "Speichern"
|
||||||
desktop/views/components/activity.chart.vue:
|
desktop/views/components/activity.chart.vue:
|
||||||
total: "Schwarz ... komplett"
|
total: "Schwarz ... komplett"
|
||||||
notes: "Blau ... Hinweise"
|
notes: "Blau ... Hinweise"
|
||||||
@ -249,29 +260,29 @@ desktop/views/components/drive.file.vue:
|
|||||||
copy-url: "URL kopieren"
|
copy-url: "URL kopieren"
|
||||||
download: "Download"
|
download: "Download"
|
||||||
else-files: "その他..."
|
else-files: "その他..."
|
||||||
set-as-avatar: "アイコンに設定"
|
set-as-avatar: "Als Avatar festlegen"
|
||||||
set-as-banner: "バナーに設定"
|
set-as-banner: "Setze als Banner"
|
||||||
open-in-app: "アプリで開く"
|
open-in-app: "In der App öffnen"
|
||||||
add-app: "アプリを追加"
|
add-app: "App hinzufügen"
|
||||||
rename-file: "ファイル名の変更"
|
rename-file: "Datei umbennen"
|
||||||
input-new-file-name: "新しいファイル名を入力してください"
|
input-new-file-name: "Geben Sie den neuen Dateinamen an"
|
||||||
copied: "コピー完了"
|
copied: "Kopieren erfolgreich"
|
||||||
copied-url-to-clipboard: "URLをクリップボードにコピーしました"
|
copied-url-to-clipboard: "URL wurde in die Zwischenablage kopiert"
|
||||||
desktop/views/components/drive.folder.vue:
|
desktop/views/components/drive.folder.vue:
|
||||||
unable-to-process: "操作を完了できません"
|
unable-to-process: "Der Vorgang konnte nicht beendet werden"
|
||||||
circular-reference-detected: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。"
|
circular-reference-detected: "Das Zielverzeichnis ist ein Unterverzeichnis des Verzeichnisses welches du verschieben möchtest"
|
||||||
unhandled-error: "不明なエラー"
|
unhandled-error: "Unbekannter Fehler"
|
||||||
contextmenu:
|
contextmenu:
|
||||||
move-to-this-folder: "このフォルダへ移動"
|
move-to-this-folder: "Verschiebe in diesen Ordner"
|
||||||
show-in-new-window: "新しいウィンドウで表示"
|
show-in-new-window: "In einem neuen Fenster anzeigen"
|
||||||
rename: "名前を変更"
|
rename: "Umbenennen"
|
||||||
rename-folder: "フォルダ名の変更"
|
rename-folder: "Ordner umbenennen"
|
||||||
input-new-folder-name: "新しいフォルダ名を入力してください"
|
input-new-folder-name: "Namen für neuen Ordner eingeben"
|
||||||
desktop/views/components/drive.nav-folder.vue:
|
desktop/views/components/drive.nav-folder.vue:
|
||||||
drive: "ドライブ"
|
drive: "Laufwerk"
|
||||||
desktop/views/components/drive.vue:
|
desktop/views/components/drive.vue:
|
||||||
search: "検索"
|
search: "Suchen"
|
||||||
load-more: "もっと読み込む"
|
load-more: "Mehr laden"
|
||||||
empty-draghover: "Herzlich Willkommen!"
|
empty-draghover: "Herzlich Willkommen!"
|
||||||
empty-drive: "Dein Speicher ist leer"
|
empty-drive: "Dein Speicher ist leer"
|
||||||
empty-drive-description: "Du kannst rechts klicken und \"Datei hochladen\" auswählen oder eine Datei per Drag and Drop auf das Fenster ziehen."
|
empty-drive-description: "Du kannst rechts klicken und \"Datei hochladen\" auswählen oder eine Datei per Drag and Drop auf das Fenster ziehen."
|
||||||
@ -290,12 +301,14 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
empty: "フォロワーはいないようです。"
|
empty: "Dir scheint niemand zu folgen."
|
||||||
desktop/views/components/following-window.vue:
|
desktop/views/components/following-window.vue:
|
||||||
following: "{} のフォロー"
|
following: "{} のフォロー"
|
||||||
desktop/views/components/following.vue:
|
desktop/views/components/following.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "Speicher"
|
drive: "Speicher"
|
||||||
favorites: "Favoriten"
|
favorites: "Favoriten"
|
||||||
lists: "Listen"
|
lists: "Listen"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "Anpassen"
|
customize: "Anpassen"
|
||||||
settings: "Einstellungen"
|
settings: "Einstellungen"
|
||||||
signout: "Ausloggen"
|
signout: "Ausloggen"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "Einen neuen Post erstellen"
|
post: "Einen neuen Post erstellen"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "Suchen"
|
placeholder: "Suchen"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
151
locales/en.yml
151
locales/en.yml
@ -5,7 +5,7 @@ meta:
|
|||||||
common:
|
common:
|
||||||
misskey: "A planet of fediverse"
|
misskey: "A planet of fediverse"
|
||||||
about-title: "A ⭐ of fediverse."
|
about-title: "A ⭐ of fediverse."
|
||||||
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
about: "Thanks for finding Misskey. Misskey is a <b>decentralized microblogging platform</b> born on Earth. Since it exists within Fediverse (a universe where various social media platforms are organized) it is mutually linked with other social media platforms. Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet?"
|
||||||
time:
|
time:
|
||||||
unknown: "unknown"
|
unknown: "unknown"
|
||||||
future: "future"
|
future: "future"
|
||||||
@ -45,8 +45,8 @@ common:
|
|||||||
delete: "Delete"
|
delete: "Delete"
|
||||||
loading: "Loading"
|
loading: "Loading"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
update-available: "A new version of Misskey is now available({newer}, current is {current}). Reload the page to apply the update."
|
update-available: "A new version of Misskey is now available({newer}, the current version is {current}). Reload the page to apply updates."
|
||||||
my-token-regenerated: "Your token has been generated. You will now get logged out."
|
my-token-regenerated: "Your token has been renewed so you will be signed out."
|
||||||
widgets:
|
widgets:
|
||||||
analog-clock: "Analog clock"
|
analog-clock: "Analog clock"
|
||||||
profile: "Profile"
|
profile: "Profile"
|
||||||
@ -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,12 +90,12 @@ 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: "Finding a problem"
|
finding: "Searching for issues"
|
||||||
no-network: "There is no Network connection"
|
no-network: "No connection"
|
||||||
no-network-desc: "Please make sure you are connected to the Network."
|
no-network-desc: "Please make sure you are connected to the network."
|
||||||
no-internet: "There is no Internet connection"
|
no-internet: "There is no Internet connection"
|
||||||
no-internet-desc: "Please make sure you are connected to the Internet."
|
no-internet-desc: "Please make sure you are connected to the Internet."
|
||||||
no-server: "Unable to connect to the Misskey server"
|
no-server: "Unable to connect to the Misskey server"
|
||||||
@ -94,19 +105,19 @@ common/views/components/connect-failed.troubleshooter.vue:
|
|||||||
flush: "Clean cache"
|
flush: "Clean cache"
|
||||||
set-version: "Specify version"
|
set-version: "Specify version"
|
||||||
common/views/components/messaging.vue:
|
common/views/components/messaging.vue:
|
||||||
search-user: "Find an user"
|
search-user: "Find a user"
|
||||||
you: "You"
|
you: "You"
|
||||||
no-history: "No history"
|
no-history: "No history"
|
||||||
common/views/components/messaging-room.vue:
|
common/views/components/messaging-room.vue:
|
||||||
empty: "No conversations"
|
empty: "You haven't messaged this user"
|
||||||
more: "More"
|
more: "Read more"
|
||||||
no-history: "There is no more history"
|
no-history: "There is no more history"
|
||||||
resize-form: "Drag to resize"
|
resize-form: "Drag to resize"
|
||||||
new-message: "New message"
|
new-message: "New message"
|
||||||
common/views/components/messaging-room.form.vue:
|
common/views/components/messaging-room.form.vue:
|
||||||
input-message-here: "Enter message here"
|
input-message-here: "Enter message here"
|
||||||
send: "Send"
|
send: "Send"
|
||||||
attach-from-local: "Attach files from your pc"
|
attach-from-local: "Attach files from your PC"
|
||||||
attach-from-drive: "Attach files from your Drive"
|
attach-from-drive: "Attach files from your Drive"
|
||||||
common/views/components/messaging-room.message.vue:
|
common/views/components/messaging-room.message.vue:
|
||||||
is-read: "Read"
|
is-read: "Read"
|
||||||
@ -138,7 +149,7 @@ common/views/components/poll-editor.vue:
|
|||||||
choice-n: "Choice {}"
|
choice-n: "Choice {}"
|
||||||
remove: "Remove this choice"
|
remove: "Remove this choice"
|
||||||
add: "+ Add a choice"
|
add: "+ Add a choice"
|
||||||
destroy: "Destroy this poll"
|
destroy: "Cancel this poll"
|
||||||
common/views/components/reaction-picker.vue:
|
common/views/components/reaction-picker.vue:
|
||||||
choose-reaction: "Choose a reaction"
|
choose-reaction: "Choose a reaction"
|
||||||
common/views/components/signin.vue:
|
common/views/components/signin.vue:
|
||||||
@ -158,10 +169,10 @@ common/views/components/signup.vue:
|
|||||||
too-long: "Please enter up to 20 characters."
|
too-long: "Please enter up to 20 characters."
|
||||||
password: "Password"
|
password: "Password"
|
||||||
password-placeholder: "We recommend more than 8 characters."
|
password-placeholder: "We recommend more than 8 characters."
|
||||||
weak-password: "Weak"
|
weak-password: "Weak password"
|
||||||
normal-password: "So so"
|
normal-password: "Fair password"
|
||||||
strong-password: "Strong"
|
strong-password: "Strong password"
|
||||||
retype: "Type again"
|
retype: "Re-enter"
|
||||||
retype-placeholder: "Confirm your password"
|
retype-placeholder: "Confirm your password"
|
||||||
password-matched: "OK"
|
password-matched: "OK"
|
||||||
password-not-matched: "Doesn't match"
|
password-not-matched: "Doesn't match"
|
||||||
@ -178,9 +189,9 @@ common/views/components/stream-indicator.vue:
|
|||||||
common/views/components/twitter-setting.vue:
|
common/views/components/twitter-setting.vue:
|
||||||
description: "If you connect your Twitter account to your Misskey account, you will be able to see your Twitter account information on your profile and you can sign-in using Twitter."
|
description: "If you connect your Twitter account to your Misskey account, you will be able to see your Twitter account information on your profile and you can sign-in using Twitter."
|
||||||
connected-to: "You are connected to this Twitter account"
|
connected-to: "You are connected to this Twitter account"
|
||||||
detail: "Detail..."
|
detail: "Details..."
|
||||||
reconnect: "Reconnect"
|
reconnect: "Reconnect"
|
||||||
connect: "Link your twitter account"
|
connect: "Link your Twitter account"
|
||||||
disconnect: "Disconnect"
|
disconnect: "Disconnect"
|
||||||
common/views/components/uploader.vue:
|
common/views/components/uploader.vue:
|
||||||
waiting: "Waiting"
|
waiting: "Waiting"
|
||||||
@ -200,7 +211,7 @@ common/views/widgets/broadcast.vue:
|
|||||||
next: "Next"
|
next: "Next"
|
||||||
common/views/widgets/donation.vue:
|
common/views/widgets/donation.vue:
|
||||||
title: "Donation"
|
title: "Donation"
|
||||||
text: "To keep Misskey up and running we spend money for our domain name, servers and so on.. We don't get any money from it, and we would really appreciate it if you could donate. If you're interested contact {}. Thank you for your contribution!"
|
text: "To keep Misskey up and running we spend money for our domain name, servers and so on. Since we don't get money from advertisements, we count on donations from all of you. If you're interested contact {}. Thank you for your contribution!"
|
||||||
common/views/widgets/photo-stream.vue:
|
common/views/widgets/photo-stream.vue:
|
||||||
title: "Photostream"
|
title: "Photostream"
|
||||||
no-photos: "No photos"
|
no-photos: "No photos"
|
||||||
@ -223,7 +234,7 @@ desktop/views/components/calendar.vue:
|
|||||||
title: "{1} / {2}"
|
title: "{1} / {2}"
|
||||||
prev: "Previous month"
|
prev: "Previous month"
|
||||||
next: "Next month"
|
next: "Next month"
|
||||||
go: "Click to naviguate"
|
go: "Click to navigate"
|
||||||
desktop/views/components/choose-file-from-drive-window.vue:
|
desktop/views/components/choose-file-from-drive-window.vue:
|
||||||
choose-file: "Choosing files"
|
choose-file: "Choosing files"
|
||||||
upload: "Upload files from your PC"
|
upload: "Upload files from your PC"
|
||||||
@ -290,19 +301,21 @@ 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: "Unfollow"
|
following: "Following"
|
||||||
follow: "Follow"
|
follow: "Follow"
|
||||||
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "Follow request"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "Followers of {}"
|
followers: "{}'s followers"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
empty: "Seems that you don’t have any followers."
|
empty: "Seems like you don’t have any followers."
|
||||||
desktop/views/components/following-window.vue:
|
desktop/views/components/following-window.vue:
|
||||||
following: "Following of {}"
|
following: "Following {}"
|
||||||
desktop/views/components/following.vue:
|
desktop/views/components/following.vue:
|
||||||
empty: "You don’t follow anyone."
|
empty: "You don’t follow anyone."
|
||||||
desktop/views/components/friends-maker.vue:
|
desktop/views/components/friends-maker.vue:
|
||||||
title: "Recommended users:"
|
title: "Recommended users:"
|
||||||
empty: "Similar users weren’t found."
|
empty: "Couldn't find any recommended users."
|
||||||
fetching: "Loading…"
|
fetching: "Loading…"
|
||||||
refresh: "More"
|
refresh: "More"
|
||||||
close: "Close"
|
close: "Close"
|
||||||
@ -323,16 +336,16 @@ desktop/views/components/note-detail.vue:
|
|||||||
more: "Load more conversations"
|
more: "Load more conversations"
|
||||||
private: "this post is private"
|
private: "this post is private"
|
||||||
deleted: "this post has been deleted"
|
deleted: "this post has been deleted"
|
||||||
reposted-by: "Renoted by {}"
|
reposted-by: "Reposted by {}"
|
||||||
location: "Location"
|
location: "Location"
|
||||||
renote: "Renote"
|
renote: "Repost"
|
||||||
add-reaction: "Add a reaction"
|
add-reaction: "Add a reaction"
|
||||||
desktop/views/components/notes.note.vue:
|
desktop/views/components/notes.note.vue:
|
||||||
reposted-by: "Reposted by {}"
|
reposted-by: "Reposted by {}"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
renote: "Renote"
|
renote: "Repost"
|
||||||
add-reaction: "Add a reaction"
|
add-reaction: "Add a reaction"
|
||||||
detail: "Show detail"
|
detail: "Show details"
|
||||||
private: "this post is private"
|
private: "this post is private"
|
||||||
deleted: "this post has been deleted"
|
deleted: "this post has been deleted"
|
||||||
desktop/views/components/notes.vue:
|
desktop/views/components/notes.vue:
|
||||||
@ -346,20 +359,20 @@ desktop/views/components/post-form.vue:
|
|||||||
quote-placeholder: "Quote this note..."
|
quote-placeholder: "Quote this note..."
|
||||||
submit: "Post"
|
submit: "Post"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
renote: "Renote"
|
renote: "Repost"
|
||||||
posted: "Posted!"
|
posted: "Posted!"
|
||||||
replied: "Replied!"
|
replied: "Replied!"
|
||||||
reposted: "Reposted!"
|
reposted: "Reposted!"
|
||||||
note-failed: "Failed to note"
|
note-failed: "Failed to note"
|
||||||
reply-failed: "Failed to reply"
|
reply-failed: "Failed to reply"
|
||||||
renote-failed: "Failed to renote"
|
renote-failed: "Failed to repost"
|
||||||
posting: "Posting"
|
posting: "Posting"
|
||||||
attach-media-from-local: "Attach media from your pc"
|
attach-media-from-local: "Attach media from your PC"
|
||||||
attach-media-from-drive: "Attach media from your Drive"
|
attach-media-from-drive: "Attach media from your Drive"
|
||||||
attach-cancel: "Cancel attachment"
|
attach-cancel: "Cancel attachment"
|
||||||
insert-a-kao: "v(‘ω’)v"
|
insert-a-kao: "v(‘ω’)v"
|
||||||
create-poll: "Create a poll"
|
create-poll: "Create a poll"
|
||||||
text-remain: "{} chars remaining"
|
text-remain: "{} characters remaining"
|
||||||
desktop/views/components/post-form-window.vue:
|
desktop/views/components/post-form-window.vue:
|
||||||
note: "New note"
|
note: "New note"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
@ -370,12 +383,12 @@ desktop/views/components/progress-dialog.vue:
|
|||||||
desktop/views/components/renote-form.vue:
|
desktop/views/components/renote-form.vue:
|
||||||
quote: "Quote..."
|
quote: "Quote..."
|
||||||
cancel: "Cancel"
|
cancel: "Cancel"
|
||||||
renote: "Renote"
|
renote: "Repost"
|
||||||
reposting: "Reposting..."
|
reposting: "Reposting..."
|
||||||
success: "Reposted!"
|
success: "Reposted!"
|
||||||
failure: "Failed to Renote"
|
failure: "Repost failed"
|
||||||
desktop/views/components/renote-form-window.vue:
|
desktop/views/components/renote-form-window.vue:
|
||||||
title: "Are you sure you want to renote this note?"
|
title: "Are you sure you want to repost this?"
|
||||||
desktop/views/components/settings-window.vue:
|
desktop/views/components/settings-window.vue:
|
||||||
settings: "Settings"
|
settings: "Settings"
|
||||||
desktop/views/components/settings.vue:
|
desktop/views/components/settings.vue:
|
||||||
@ -405,22 +418,22 @@ desktop/views/components/settings.vue:
|
|||||||
gradient-window-header: "Use gradients on window headers"
|
gradient-window-header: "Use gradients on window headers"
|
||||||
post-form-on-timeline: "Display post form at the top of the timeline"
|
post-form-on-timeline: "Display post form at the top of the timeline"
|
||||||
show-reply-target: "Display reply target"
|
show-reply-target: "Display reply target"
|
||||||
show-my-renotes: "Show my renote in the timeline"
|
show-my-renotes: "Show my reposts in the timeline"
|
||||||
show-renoted-my-notes: "Show renoted my post in the timeline"
|
show-renoted-my-notes: "Show my posts that have been shared in the timeline"
|
||||||
show-maps: "Show the map"
|
show-maps: "Show the map"
|
||||||
show-maps-desc: "Show the map of the location attached to the post."
|
show-maps-desc: "Automatically show the map of the location attached to the post."
|
||||||
sound: "Sound"
|
sound: "Sound"
|
||||||
enable-sounds: "Enable sound"
|
enable-sounds: "Enable sound"
|
||||||
enable-sounds-desc: "Play a sound when you received a post/message. This setting is stored in the browser."
|
enable-sounds-desc: "Play a sound when you receive a post/message. This setting is stored in the browser."
|
||||||
volume: "Volume"
|
volume: "Volume"
|
||||||
test: "Test"
|
test: "Test"
|
||||||
mobile: "Mobile"
|
mobile: "Mobile"
|
||||||
disable-via-mobile: "Not mark the post as 'from mobile'"
|
disable-via-mobile: "Don't mark the post as 'from mobile'"
|
||||||
language: "Language"
|
language: "Language"
|
||||||
pick-language: "Select a language"
|
pick-language: "Select a language"
|
||||||
recommended: "Recommended"
|
recommended: "Recommended"
|
||||||
auto: "Auto"
|
auto: "Auto"
|
||||||
specify-language: "Specify the language"
|
specify-language: "Specify language"
|
||||||
language-desc: "You need to reload the page for the changes to take effect."
|
language-desc: "You need to reload the page for the changes to take effect."
|
||||||
cache: "Cache"
|
cache: "Cache"
|
||||||
clean-cache: "Cleanup"
|
clean-cache: "Cleanup"
|
||||||
@ -438,11 +451,11 @@ desktop/views/components/settings.vue:
|
|||||||
do-update: "Check for update"
|
do-update: "Check for update"
|
||||||
update-settings: "Advanced settings"
|
update-settings: "Advanced settings"
|
||||||
prevent-update: "Postpone updates (not recommended)"
|
prevent-update: "Postpone updates (not recommended)"
|
||||||
prevent-update-desc: "You may reflect updates even if you select this setting. This setting is valid only this device."
|
prevent-update-desc: "Even if you turn this setting on updates may apply. This setting is valid only for this device."
|
||||||
no-updates: "No updates available"
|
no-updates: "No updates available"
|
||||||
no-updates-desc: "Your Misskey is up to date."
|
no-updates-desc: "Your Misskey is up to date."
|
||||||
update-available: "New version is available!"
|
update-available: "New version is available!"
|
||||||
update-available-desc: "To reload the page and updates are applied."
|
update-available-desc: "The updates will apply if you reload the page again."
|
||||||
advanced-settings: "Advanced"
|
advanced-settings: "Advanced"
|
||||||
debug-mode: "Enable the debug mode"
|
debug-mode: "Enable the debug mode"
|
||||||
debug-mode-desc: "This setting is stored in the browser."
|
debug-mode-desc: "This setting is stored in the browser."
|
||||||
@ -476,8 +489,8 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "Regenerate the token"
|
regenerate-token: "Regenerate the token"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "Please enter the password"
|
enter-password: "Please enter the password"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "No authorized apps"
|
no-apps: "No linked applications"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "No muted users"
|
no-users: "No muted users"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
favorites: "Favorites"
|
favorites: "Favorites"
|
||||||
lists: "Lists"
|
lists: "Lists"
|
||||||
|
follow-requests: "Follow requests"
|
||||||
customize: "Customize"
|
customize: "Customize"
|
||||||
settings: "Settings"
|
settings: "Settings"
|
||||||
signout: "Sign out"
|
signout: "Sign out"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "Compose new Post"
|
post: "Compose new Post"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "Search"
|
placeholder: "Search"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "Follow requests"
|
||||||
|
accept: "Accept"
|
||||||
|
reject: "Reject"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "User lists"
|
||||||
create-list: "Create list"
|
create-list: "Create list"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "Posts"
|
notes: "Posts"
|
||||||
@ -633,13 +653,13 @@ mobile/views/components/drive.vue:
|
|||||||
load-more: "Load more"
|
load-more: "Load more"
|
||||||
nothing-in-drive: "Nothing"
|
nothing-in-drive: "Nothing"
|
||||||
folder-is-empty: "This folder is empty"
|
folder-is-empty: "This folder is empty"
|
||||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
prompt: "What do you want to do? (Please enter a number): <1 → Upload a file | 2 → Upload a file from a URL | 3 → Create a folder | 4 → Change this folder's name | 5 → Move this folder | 6 → Delete this folder>"
|
||||||
deletion-alert: "Sorry! Deleting a folder is not yet implemented."
|
deletion-alert: "Sorry! Deleting a folder is not yet implemented."
|
||||||
folder-name: "Folder name"
|
folder-name: "Folder name"
|
||||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
root-rename-alert: "You're in the root; it can't be renamed because it's not a folder. Navigate to a folder you want to rename and try again."
|
||||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
root-move-alert: "You're in the root; it can't be moved because it's not a folder. Navigate to a folder you want to move and try again."
|
||||||
url-prompt: "URL of file you want to upload"
|
url-prompt: "URL of file you want to upload"
|
||||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
uploading: "Upload requested. It may take some time for the upload to complete."
|
||||||
mobile/views/components/drive-file-detail.vue:
|
mobile/views/components/drive-file-detail.vue:
|
||||||
rename: "Rename"
|
rename: "Rename"
|
||||||
mobile/views/components/drive-file-chooser.vue:
|
mobile/views/components/drive-file-chooser.vue:
|
||||||
@ -653,8 +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:
|
||||||
|
following: "Following"
|
||||||
follow: "Follow"
|
follow: "Follow"
|
||||||
unfollow: "Unfollow"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "Follow request"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "Let's follow users"
|
title: "Let's follow users"
|
||||||
empty: "Featured user was not found."
|
empty: "Featured user was not found."
|
||||||
@ -662,7 +684,7 @@ mobile/views/components/friends-maker.vue:
|
|||||||
refresh: "See more"
|
refresh: "See more"
|
||||||
close: "Close"
|
close: "Close"
|
||||||
mobile/views/components/note.vue:
|
mobile/views/components/note.vue:
|
||||||
reposted-by: "Renoted by {}"
|
reposted-by: "Reposted by {}"
|
||||||
more: "See more"
|
more: "See more"
|
||||||
less: "Hide"
|
less: "Hide"
|
||||||
private: "this post is private"
|
private: "this post is private"
|
||||||
@ -671,7 +693,7 @@ mobile/views/components/note.vue:
|
|||||||
mobile/views/components/note-detail.vue:
|
mobile/views/components/note-detail.vue:
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
reaction: "Reaction"
|
reaction: "Reaction"
|
||||||
reposted-by: "Renoted by {}"
|
reposted-by: "Reposted by {}"
|
||||||
private: "this post is private"
|
private: "this post is private"
|
||||||
deleted: "this post has been deleted"
|
deleted: "this post has been deleted"
|
||||||
location: "Location"
|
location: "Location"
|
||||||
@ -693,11 +715,11 @@ mobile/views/components/post-form.vue:
|
|||||||
add-visible-user: "Add a user"
|
add-visible-user: "Add a user"
|
||||||
submit: "Post"
|
submit: "Post"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
renote: "Renote"
|
renote: "Repost"
|
||||||
quote-placeholder: "Quote this post... (optional)"
|
quote-placeholder: "Quote this post... (optional)"
|
||||||
reply-placeholder: "Reply to this note..."
|
reply-placeholder: "Reply to this note..."
|
||||||
cw-placeholder: "内容への注釈 (オプション)"
|
cw-placeholder: "Comments about content (optional)"
|
||||||
location-alert: "お使いの端末は位置情報に対応していません"
|
location-alert: "Your device does not support location services"
|
||||||
error: "Error"
|
error: "Error"
|
||||||
username-prompt: "Enter user name"
|
username-prompt: "Enter user name"
|
||||||
mobile/views/components/sub-note-content.vue:
|
mobile/views/components/sub-note-content.vue:
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "No notes"
|
empty: "No notes"
|
||||||
load-more: "More"
|
load-more: "More"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "Home"
|
timeline: "Timeline"
|
||||||
notifications: "Notifications"
|
notifications: "Notifications"
|
||||||
messaging: "Messages"
|
messaging: "Messages"
|
||||||
|
follow-requests: "Follow requests"
|
||||||
search: "Search"
|
search: "Search"
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
favorites: "Favorites"
|
favorites: "Favorites"
|
||||||
|
user-lists: "Lists"
|
||||||
widgets: "Widgets"
|
widgets: "Widgets"
|
||||||
game: "Games"
|
game: "Games"
|
||||||
darkmode: "Dark mode"
|
darkmode: "Dark mode"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "More"
|
load-more: "More"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "Favorites"
|
title: "Favorites"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "Lists"
|
||||||
|
enter-list-name: "Enter list name"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
more: "Load more"
|
more: "Load more"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "Messaging"
|
messaging: "Messaging"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "Messaging"
|
messaging: "Messaging"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "Follow requests"
|
||||||
|
accept: "Accept"
|
||||||
|
reject: "Reject"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "Post"
|
title: "Post"
|
||||||
prev: "Previous note"
|
prev: "Previous note"
|
||||||
@ -784,8 +815,8 @@ mobile/views/pages/settings.vue:
|
|||||||
circle-icons: "Use circle icons"
|
circle-icons: "Use circle icons"
|
||||||
timeline: "Timeline"
|
timeline: "Timeline"
|
||||||
show-reply-target: "Show reply target"
|
show-reply-target: "Show reply target"
|
||||||
show-my-renotes: "Show my renotes"
|
show-my-renotes: "Show my reposts"
|
||||||
show-renoted-my-notes: "Show renoted my notes"
|
show-renoted-my-notes: "Show my reposted posts"
|
||||||
post-style: "Post design"
|
post-style: "Post design"
|
||||||
post-style-standard: "Standard"
|
post-style-standard: "Standard"
|
||||||
post-style-smart: "Smart"
|
post-style-smart: "Smart"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "新規投稿"
|
post: "新規投稿"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,8 +489,8 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "Regenerer le token"
|
regenerate-token: "Regenerer le token"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "Veuillez entrer le mot de passe"
|
enter-password: "Veuillez entrer le mot de passe"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "Aucune application authorisée"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "Aucun utilisateurs mis en sourdine"
|
no-users: "Aucun utilisateurs mis en sourdine"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
favorites: "Favorites"
|
favorites: "Favorites"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "Modifications"
|
customize: "Modifications"
|
||||||
settings: "Réglages"
|
settings: "Réglages"
|
||||||
signout: "Déconnexion"
|
signout: "Déconnexion"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "Composer un nouveau post"
|
post: "Composer un nouveau post"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "Chercher"
|
placeholder: "Chercher"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "Suivre"
|
follow: "Suivre"
|
||||||
unfollow: "Ne plus suivre"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "Pas de notes"
|
empty: "Pas de notes"
|
||||||
load-more: "Afficher plus"
|
load-more: "Afficher plus"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "Accueil"
|
timeline: "タイムライン"
|
||||||
notifications: "Notifications"
|
notifications: "Notifications"
|
||||||
messaging: "Messages"
|
messaging: "Messages"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "Rechercher"
|
search: "Rechercher"
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "Afficher plus"
|
load-more: "Afficher plus"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "Messagerie"
|
messaging: "Messagerie"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "Messagerie"
|
messaging: "Messagerie"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "Post"
|
title: "Post"
|
||||||
prev: "Note précedante"
|
prev: "Note précedante"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "新規投稿"
|
post: "新規投稿"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -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,8 +344,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
|
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
@ -551,7 +566,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
|
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
|
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
@ -596,6 +611,7 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
@ -603,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: "ゲーム"
|
||||||
|
|
||||||
@ -615,7 +632,13 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
|
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
|
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
@ -771,8 +794,10 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
|
|
||||||
mobile/views/components/follow-button.vue:
|
mobile/views/components/follow-button.vue:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
|
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
@ -838,12 +863,14 @@ mobile/views/components/timeline.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
|
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -863,6 +890,10 @@ mobile/views/components/users-list.vue:
|
|||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
|
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -884,6 +915,11 @@ mobile/views/pages/messaging.vue:
|
|||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
|
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "新規投稿"
|
post: "新規投稿"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -3,8 +3,8 @@ meta:
|
|||||||
lang: "język polski"
|
lang: "język polski"
|
||||||
divider: ""
|
divider: ""
|
||||||
common:
|
common:
|
||||||
misskey: "A planet of fediverse"
|
misskey: "Planeta Fediwersum"
|
||||||
about-title: "A ⭐ of fediverse."
|
about-title: "⭐ Fediwersum"
|
||||||
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
|
||||||
time:
|
time:
|
||||||
unknown: "nieznany"
|
unknown: "nieznany"
|
||||||
@ -36,19 +36,19 @@ common:
|
|||||||
confused: "Zmieszany"
|
confused: "Zmieszany"
|
||||||
pudding: "Pudding"
|
pudding: "Pudding"
|
||||||
note-placeholders:
|
note-placeholders:
|
||||||
a: "今どうしてる?"
|
a: "Co robisz?"
|
||||||
b: "何かありましたか?"
|
b: "Co się wydarzyło?"
|
||||||
c: "何をお考えですか?"
|
c: "Co Ci chodzi po głowie?"
|
||||||
d: "言いたいことは?"
|
d: "Czy masz coś do powiedzenia?"
|
||||||
e: "ここに書いてください"
|
e: "Napisz coś tutaj!"
|
||||||
f: "あなたが書くのを待っています..."
|
f: "Czekamy, aż coś napiszesz."
|
||||||
delete: "Usuń"
|
delete: "Usuń"
|
||||||
loading: "Ładowanie"
|
loading: "Ładowanie"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
update-available: "Nowa wersja Misskey jest dostępna ({newer}, obecna to {current}). Odśwież stronę, aby zastosować aktualizację."
|
update-available: "Nowa wersja Misskey jest dostępna ({newer}, obecna to {current}). Odśwież stronę, aby zastosować aktualizację."
|
||||||
my-token-regenerated: "Twój token został wygenerowany. Zostaniesz wylogowany."
|
my-token-regenerated: "Twój token został wygenerowany. Zostaniesz wylogowany."
|
||||||
widgets:
|
widgets:
|
||||||
analog-clock: "アナログ時計"
|
analog-clock: "Zegar analogowy"
|
||||||
profile: "Profil"
|
profile: "Profil"
|
||||||
calendar: "Kalendarz"
|
calendar: "Kalendarz"
|
||||||
timemachine: "Kalendarz (wehikuł czasu)"
|
timemachine: "Kalendarz (wehikuł czasu)"
|
||||||
@ -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,8 +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: "Przestań śledzić"
|
following: "フォロー中"
|
||||||
follow: "Śledź"
|
follow: "Śledź"
|
||||||
|
request-pending: "Oczekiwanie na pozwolenie"
|
||||||
|
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:
|
||||||
@ -344,7 +357,7 @@ desktop/views/components/notifications.vue:
|
|||||||
desktop/views/components/post-form.vue:
|
desktop/views/components/post-form.vue:
|
||||||
reply-placeholder: "Odpowiedz na ten wpis…"
|
reply-placeholder: "Odpowiedz na ten wpis…"
|
||||||
quote-placeholder: "Zacytuj ten wpis…"
|
quote-placeholder: "Zacytuj ten wpis…"
|
||||||
submit: "投稿"
|
submit: "Wyślij"
|
||||||
reply: "Odpowiedz"
|
reply: "Odpowiedz"
|
||||||
renote: "Udostępnienie"
|
renote: "Udostępnienie"
|
||||||
posted: "Opublikowano!"
|
posted: "Opublikowano!"
|
||||||
@ -427,7 +440,7 @@ desktop/views/components/settings.vue:
|
|||||||
cache-warn: "Pamięć podręczna informacji o koncie/wpisów/odpowiedzi/wiadomości/ustawień przechowywanych w przeglądarce zostanie usunięta. Będziesz musiał odświeżyć stronę po wyczyszczeniu."
|
cache-warn: "Pamięć podręczna informacji o koncie/wpisów/odpowiedzi/wiadomości/ustawień przechowywanych w przeglądarce zostanie usunięta. Będziesz musiał odświeżyć stronę po wyczyszczeniu."
|
||||||
cache-cleared: "Wyczyszczono pamięć podręczną"
|
cache-cleared: "Wyczyszczono pamięć podręczną"
|
||||||
cache-cleared-desc: "Proszę odświeżyć stronę."
|
cache-cleared-desc: "Proszę odświeżyć stronę."
|
||||||
auto-watch: "投稿の自動ウォッチ"
|
auto-watch: "Automatycznie nasłuchuj"
|
||||||
auto-watch-desc: "Otrzymuj natychmiastowo informacje o wpisach/odpowiedziach/reakcjach."
|
auto-watch-desc: "Otrzymuj natychmiastowo informacje o wpisach/odpowiedziach/reakcjach."
|
||||||
about: "O Misskey"
|
about: "O Misskey"
|
||||||
operator: "Administrator instancji"
|
operator: "Administrator instancji"
|
||||||
@ -470,13 +483,13 @@ desktop/views/components/settings.2fa.vue:
|
|||||||
failed: "Nie udało się skonfigurować uwierzytelniania dwuetapowego, upewnij się że wprowadziłeś prawidłowy token."
|
failed: "Nie udało się skonfigurować uwierzytelniania dwuetapowego, upewnij się że wprowadziłeś prawidłowy token."
|
||||||
info: "Od teraz, wprowadzaj token wyświetlany na urządzeniu przy każdym logowaniu do Misskey."
|
info: "Od teraz, wprowadzaj token wyświetlany na urządzeniu przy każdym logowaniu do Misskey."
|
||||||
desktop/views/components/settings.api.vue:
|
desktop/views/components/settings.api.vue:
|
||||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
intro: "Aby uzyskać dostęp do API, ustaw ten token jako klucz 'i' parametrów żądań."
|
||||||
caution: "Nie pokazuj tego tokenu osobom trzecim (nie wprowadzaj go nigdzie indziej), aby konto nie trafiło w niepowołane ręce."
|
caution: "Nie pokazuj tego tokenu osobom trzecim (nie wprowadzaj go nigdzie indziej), aby konto nie trafiło w niepowołane ręce."
|
||||||
regeneration-of-token: "W przypadku wycieku tokenu, możesz wygenerować nowy."
|
regeneration-of-token: "W przypadku wycieku tokenu, możesz wygenerować nowy."
|
||||||
regenerate-token: "Wygeneruj nowy token"
|
regenerate-token: "Wygeneruj nowy token"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "Wprowadź hasło"
|
enter-password: "Wprowadź hasło"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "Brak zautoryzowanych aplikacji"
|
no-apps: "Brak zautoryzowanych aplikacji"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "Brak wyciszonych użytkowników"
|
no-users: "Brak wyciszonych użytkowników"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "Dysk"
|
drive: "Dysk"
|
||||||
favorites: "Ulubione"
|
favorites: "Ulubione"
|
||||||
lists: "Listy"
|
lists: "Listy"
|
||||||
|
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "Utwórz nowy wpis"
|
post: "Utwórz nowy wpis"
|
||||||
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:
|
||||||
|
title: "Poproś o śledzenie"
|
||||||
|
accept: "Zatwierdź"
|
||||||
|
reject: "Odmów"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
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"
|
||||||
@ -545,8 +565,8 @@ desktop/views/components/window.vue:
|
|||||||
popout: "Pop-out"
|
popout: "Pop-out"
|
||||||
close: "Zamknij"
|
close: "Zamknij"
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "O Misskey"
|
||||||
gotit: "わかった"
|
gotit: "Rozumiem!"
|
||||||
signin: "Zaloguj się"
|
signin: "Zaloguj się"
|
||||||
signup: "Zarejestruj się"
|
signup: "Zarejestruj się"
|
||||||
signin-button: "Zaloguj się"
|
signin-button: "Zaloguj się"
|
||||||
@ -653,8 +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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "Śledź"
|
follow: "Śledź"
|
||||||
unfollow: "Przestań śledzić"
|
request-pending: "Oczekiwanie na pozwolenie"
|
||||||
|
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."
|
||||||
@ -694,7 +716,7 @@ mobile/views/components/post-form.vue:
|
|||||||
submit: "Wyślij"
|
submit: "Wyślij"
|
||||||
reply: "Odpowiedz"
|
reply: "Odpowiedz"
|
||||||
renote: "Udostępnij"
|
renote: "Udostępnij"
|
||||||
quote-placeholder: "この投稿を引用... (オプション)"
|
quote-placeholder: "Zacytuj ten wpis… (nieobowiązkowe)"
|
||||||
reply-placeholder: "Odpowiedź na ten wpis…"
|
reply-placeholder: "Odpowiedź na ten wpis…"
|
||||||
cw-placeholder: "Treść ostrzeżenia (opcjonalnie)"
|
cw-placeholder: "Treść ostrzeżenia (opcjonalnie)"
|
||||||
location-alert: "Twoje urządzenie nie pozwala na przekazywanie informacji o lokalizacji"
|
location-alert: "Twoje urządzenie nie pozwala na przekazywanie informacji o lokalizacji"
|
||||||
@ -709,15 +731,17 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "Brak wpisów"
|
empty: "Brak wpisów"
|
||||||
load-more: "Więcej"
|
load-more: "Więcej"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "Strona główna"
|
timeline: "Oś czasu"
|
||||||
notifications: "Powiadomienia"
|
notifications: "Powiadomienia"
|
||||||
messaging: "Wiadomości"
|
messaging: "Wiadomości"
|
||||||
|
follow-requests: "Prośby o śledzenie"
|
||||||
search: "Szukaj"
|
search: "Szukaj"
|
||||||
drive: "Dysk"
|
drive: "Dysk"
|
||||||
favorites: "お気に入り"
|
favorites: "Ulubione"
|
||||||
widgets: "ウィジェット"
|
user-lists: "Listy"
|
||||||
game: "ゲーム"
|
widgets: "Widżety"
|
||||||
darkmode: "ダークモード"
|
game: "Gry"
|
||||||
|
darkmode: "Tryb ciemny"
|
||||||
settings: "Ustawienia"
|
settings: "Ustawienia"
|
||||||
about: "O Misskey"
|
about: "O Misskey"
|
||||||
mobile/views/components/user-timeline.vue:
|
mobile/views/components/user-timeline.vue:
|
||||||
@ -729,10 +753,13 @@ mobile/views/components/users-list.vue:
|
|||||||
known: "Znasz"
|
known: "Znasz"
|
||||||
load-more: "Więcej"
|
load-more: "Więcej"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "Ulubione"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "Listy"
|
||||||
|
enter-list-name: "Wprowadź nazwę listy"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "Dysk"
|
drive: "Dysk"
|
||||||
more: "もっと見る"
|
more: "Załaduj więcej"
|
||||||
mobile/views/pages/followers.vue:
|
mobile/views/pages/followers.vue:
|
||||||
followers-of: "Śledzący {}"
|
followers-of: "Śledzący {}"
|
||||||
mobile/views/pages/following.vue:
|
mobile/views/pages/following.vue:
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "Wiadomości"
|
messaging: "Wiadomości"
|
||||||
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:
|
||||||
|
title: "Prośby o śledzenie"
|
||||||
|
accept: "Zatwierdź"
|
||||||
|
reject: "Odmów"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "Wpis"
|
title: "Wpis"
|
||||||
prev: "Poprzedni wpis"
|
prev: "Poprzedni wpis"
|
||||||
@ -788,7 +819,7 @@ mobile/views/pages/settings.vue:
|
|||||||
show-renoted-my-notes: "Pokazuj udostępnienia moich wpisów"
|
show-renoted-my-notes: "Pokazuj udostępnienia moich wpisów"
|
||||||
post-style: "Styl wpisów"
|
post-style: "Styl wpisów"
|
||||||
post-style-standard: "Standardowy"
|
post-style-standard: "Standardowy"
|
||||||
post-style-smart: "スマート"
|
post-style-smart: "Inteligentny"
|
||||||
behavior: "Zachowanie"
|
behavior: "Zachowanie"
|
||||||
fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół"
|
fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół"
|
||||||
disable-via-mobile: "Nie oznaczaj wpisów jako „wysłane z telefonu”"
|
disable-via-mobile: "Nie oznaczaj wpisów jako „wysłane z telefonu”"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "新規投稿"
|
post: "新規投稿"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "新規投稿"
|
post: "新規投稿"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -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,8 +301,10 @@ 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: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
desktop/views/components/followers.vue:
|
desktop/views/components/followers.vue:
|
||||||
@ -476,7 +489,7 @@ desktop/views/components/settings.api.vue:
|
|||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンを再生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入力してください"
|
||||||
desktop/views/components/settings.app.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはありません"
|
no-apps: "連携しているアプリケーションはありません"
|
||||||
desktop/views/components/settings.mute.vue:
|
desktop/views/components/settings.mute.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
@ -514,12 +527,14 @@ desktop/views/components/ui.header.account.vue:
|
|||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
customize: "カスタマイズ"
|
customize: "カスタマイズ"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
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:
|
||||||
@ -528,7 +543,12 @@ desktop/views/components/ui.header.post.vue:
|
|||||||
post: "新規投稿"
|
post: "新規投稿"
|
||||||
desktop/views/components/ui.header.search.vue:
|
desktop/views/components/ui.header.search.vue:
|
||||||
placeholder: "検索"
|
placeholder: "検索"
|
||||||
|
desktop/views/components/received-follow-requests-window.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
desktop/views/components/user-lists-window.vue:
|
desktop/views/components/user-lists-window.vue:
|
||||||
|
title: "リスト"
|
||||||
create-list: "リストを作成"
|
create-list: "リストを作成"
|
||||||
desktop/views/components/user-preview.vue:
|
desktop/views/components/user-preview.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
@ -653,8 +673,10 @@ 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:
|
||||||
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
unfollow: "フォロー解除"
|
request-pending: "フォロー許可待ち"
|
||||||
|
follow-request: "フォロー申請"
|
||||||
mobile/views/components/friends-maker.vue:
|
mobile/views/components/friends-maker.vue:
|
||||||
title: "気になるユーザーをフォロー"
|
title: "気になるユーザーをフォロー"
|
||||||
empty: "おすすめのユーザーは見つかりませんでした。"
|
empty: "おすすめのユーザーは見つかりませんでした。"
|
||||||
@ -709,12 +731,14 @@ mobile/views/components/timeline.vue:
|
|||||||
empty: "投稿がありません"
|
empty: "投稿がありません"
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/components/ui.nav.vue:
|
mobile/views/components/ui.nav.vue:
|
||||||
home: "ホーム"
|
timeline: "タイムライン"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
follow-requests: "フォロー申請"
|
||||||
search: "検索"
|
search: "検索"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
|
user-lists: "リスト"
|
||||||
widgets: "ウィジェット"
|
widgets: "ウィジェット"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
darkmode: "ダークモード"
|
darkmode: "ダークモード"
|
||||||
@ -730,6 +754,9 @@ mobile/views/components/users-list.vue:
|
|||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
mobile/views/pages/favorites.vue:
|
mobile/views/pages/favorites.vue:
|
||||||
title: "お気に入り"
|
title: "お気に入り"
|
||||||
|
mobile/views/pages/user-lists.vue:
|
||||||
|
title: "リスト"
|
||||||
|
enter-list-name: "リスト名を入力してください"
|
||||||
mobile/views/pages/drive.vue:
|
mobile/views/pages/drive.vue:
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
@ -745,6 +772,10 @@ mobile/views/pages/messaging.vue:
|
|||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
mobile/views/pages/messaging-room.vue:
|
mobile/views/pages/messaging-room.vue:
|
||||||
messaging: "メッセージ"
|
messaging: "メッセージ"
|
||||||
|
mobile/views/pages/received-follow-requests.vue:
|
||||||
|
title: "フォロー申請"
|
||||||
|
accept: "承認"
|
||||||
|
reject: "拒否"
|
||||||
mobile/views/pages/note.vue:
|
mobile/views/pages/note.vue:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "2.24.0",
|
"version": "2.29.0",
|
||||||
"clientVersion": "1.0.6017",
|
"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 |
@ -3,6 +3,7 @@
|
|||||||
<main v-if="$store.getters.isSignedIn">
|
<main v-if="$store.getters.isSignedIn">
|
||||||
<p class="fetching" v-if="fetching">読み込み中<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching">読み込み中<mk-ellipsis/></p>
|
||||||
<x-form
|
<x-form
|
||||||
|
class="form"
|
||||||
ref="form"
|
ref="form"
|
||||||
v-if="state == 'waiting'"
|
v-if="state == 'waiting'"
|
||||||
:session="session"
|
:session="session"
|
||||||
@ -26,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>
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ export default Vue.extend({
|
|||||||
padding 32px
|
padding 32px
|
||||||
color #555
|
color #555
|
||||||
|
|
||||||
> div
|
> div:not(.form)
|
||||||
padding 64px
|
padding 64px
|
||||||
|
|
||||||
> h1
|
> h1
|
||||||
@ -143,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';
|
||||||
@ -20,7 +18,7 @@ export class HomeStream extends Stream {
|
|||||||
}, 1000 * 60);
|
}, 1000 * 60);
|
||||||
|
|
||||||
// 自分の情報が更新されたとき
|
// 自分の情報が更新されたとき
|
||||||
this.on('i_updated', i => {
|
this.on('meUpdated', i => {
|
||||||
if (os.debug) {
|
if (os.debug) {
|
||||||
console.log('I updated:', i);
|
console.log('I updated:', i);
|
||||||
}
|
}
|
||||||
@ -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 => {
|
||||||
|
os.store.commit('settings/setWidget', {
|
||||||
id: x.id,
|
id: x.id,
|
||||||
data: x.data
|
data: x.data
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// トークンが再生成されたとき
|
// トークンが再生成されたとき
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg class="mk-analog-clock" viewBox="0 0 10 10" preserveAspectRatio="none">
|
<svg class="mk-analog-clock" viewBox="0 0 10 10" preserveAspectRatio="none">
|
||||||
<line v-for="angle, i in graduations"
|
<circle v-for="angle, i in graduations"
|
||||||
:x1="5 + (Math.sin(angle) * (5 - graduationsPadding))"
|
:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))"
|
||||||
:y1="5 - (Math.cos(angle) * (5 - graduationsPadding))"
|
:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))"
|
||||||
:x2="5 + (Math.sin(angle) * ((5 - graduationsPadding) - (i % 5 == 0 ? longGraduationLength : shortGraduationLength)))"
|
:r="i % 5 == 0 ? 0.125 : 0.05"
|
||||||
:y2="5 - (Math.cos(angle) * ((5 - graduationsPadding) - (i % 5 == 0 ? longGraduationLength : shortGraduationLength)))"
|
:fill="i % 5 == 0 ? majorGraduationColor : minorGraduationColor"/>
|
||||||
:stroke="i % 5 == 0 ? longGraduationColor : shortGraduationColor"
|
|
||||||
stroke-width="0.05"/>
|
|
||||||
|
|
||||||
<line
|
<line
|
||||||
:x1="5 - (Math.sin(sAngle) * (sHandLengthRatio * handsTailLength))"
|
:x1="5 - (Math.sin(sAngle) * (sHandLengthRatio * handsTailLength))"
|
||||||
@ -43,14 +41,13 @@ export default Vue.extend({
|
|||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
now: new Date(),
|
now: new Date(),
|
||||||
clock: null,
|
clock: null,
|
||||||
|
|
||||||
graduationsPadding: 0.5,
|
graduationsPadding: 0.5,
|
||||||
longGraduationLength: 0.3,
|
|
||||||
shortGraduationLength: 0.15,
|
|
||||||
handsPadding: 1,
|
handsPadding: 1,
|
||||||
handsTailLength: 0.7,
|
handsTailLength: 0.7,
|
||||||
hHandLengthRatio: 0.75,
|
hHandLengthRatio: 0.75,
|
||||||
@ -58,11 +55,12 @@ export default Vue.extend({
|
|||||||
sHandLengthRatio: 1
|
sHandLengthRatio: 1
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
longGraduationColor(): string {
|
majorGraduationColor(): string {
|
||||||
return this.dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
return this.dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||||
},
|
},
|
||||||
shortGraduationColor(): string {
|
minorGraduationColor(): string {
|
||||||
return this.dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
return this.dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -106,12 +104,15 @@ export default Vue.extend({
|
|||||||
return angles;
|
return angles;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.clock = setInterval(this.tick, 1000);
|
this.clock = setInterval(this.tick, 1000);
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
clearInterval(this.clock);
|
clearInterval(this.clock);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
tick() {
|
tick() {
|
||||||
this.now = new Date();
|
this.now = new Date();
|
||||||
|
@ -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) {
|
||||||
|
items.push({
|
||||||
|
content: '%i18n:@remote%',
|
||||||
|
onClick: () => {
|
||||||
|
window.open(this.note.uri, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
anime({
|
|
||||||
targets: this.$refs.backdrop,
|
|
||||||
opacity: 1,
|
|
||||||
duration: 100,
|
|
||||||
easing: 'linear'
|
|
||||||
});
|
|
||||||
|
|
||||||
anime({
|
|
||||||
targets: this.$refs.popover,
|
|
||||||
opacity: 1,
|
|
||||||
scale: [0.5, 1],
|
|
||||||
duration: 500
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
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>
|
||||||
|
@ -73,6 +73,7 @@ root(isDark)
|
|||||||
background isDark ? #282c37 : #fff
|
background isDark ? #282c37 : #fff
|
||||||
border none
|
border none
|
||||||
border-bottom solid 1px isDark ? #1c2023 : #eee
|
border-bottom solid 1px isDark ? #1c2023 : #eee
|
||||||
|
border-radius 0
|
||||||
|
|
||||||
> button
|
> button
|
||||||
display block
|
display block
|
||||||
|
@ -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%
|
|
||||||
|
@ -62,7 +62,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
onContextmenu(e) {
|
onContextmenu(e) {
|
||||||
this.isContextmenuShowing = true;
|
this.isContextmenuShowing = true;
|
||||||
contextmenu(e, [{
|
contextmenu((this as any).os)(e, [{
|
||||||
type: 'item',
|
type: 'item',
|
||||||
text: '%i18n:@contextmenu.rename%',
|
text: '%i18n:@contextmenu.rename%',
|
||||||
icon: '%fa:i-cursor%',
|
icon: '%fa:i-cursor%',
|
||||||
|
@ -52,7 +52,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
onContextmenu(e) {
|
onContextmenu(e) {
|
||||||
this.isContextmenuShowing = true;
|
this.isContextmenuShowing = true;
|
||||||
contextmenu(e, [{
|
contextmenu((this as any).os)(e, [{
|
||||||
type: 'item',
|
type: 'item',
|
||||||
text: '%i18n:@contextmenu.move-to-this-folder%',
|
text: '%i18n:@contextmenu.move-to-this-folder%',
|
||||||
icon: '%fa:arrow-right%',
|
icon: '%fa:arrow-right%',
|
||||||
|
@ -136,7 +136,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onContextmenu(e) {
|
onContextmenu(e) {
|
||||||
contextmenu(e, [{
|
contextmenu((this as any).os)(e, [{
|
||||||
type: 'item',
|
type: 'item',
|
||||||
text: '%i18n:@contextmenu.create-folder%',
|
text: '%i18n:@contextmenu.create-folder%',
|
||||||
icon: '%fa:R folder%',
|
icon: '%fa:R folder%',
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<button class="mk-follow-button"
|
<button class="mk-follow-button"
|
||||||
:class="{ wait, follow: !user.isFollowing, unfollow: user.isFollowing, big: size == 'big' }"
|
:class="{ wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou, big: size == 'big' }"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
:disabled="wait"
|
:disabled="wait"
|
||||||
:title="user.isFollowing ? '%i18n:@unfollow%' : '%i18n:@follow%'"
|
|
||||||
>
|
>
|
||||||
<template v-if="!wait && user.isFollowing">
|
<template v-if="!wait">
|
||||||
<template v-if="size == 'compact'">%fa:minus%</template>
|
<template v-if="u.hasPendingFollowRequestFromYou">%fa:hourglass-half%<template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
|
||||||
<template v-if="size == 'big'">%fa:minus%%i18n:@unfollow%</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%</template></template>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!wait && !user.isFollowing">
|
<template v-else>%fa:spinner .pulse .fw%</template>
|
||||||
<template v-if="size == 'compact'">%fa:plus%</template>
|
|
||||||
<template v-if="size == 'big'">%fa:plus%%i18n:@follow%</template>
|
|
||||||
</template>
|
|
||||||
<template v-if="wait">%fa:spinner .pulse .fw%</template>
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -34,6 +31,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
u: this.user,
|
||||||
wait: false,
|
wait: false,
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null
|
connectionId: null
|
||||||
@ -56,39 +54,44 @@ export default Vue.extend({
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onFollow(user) {
|
onFollow(user) {
|
||||||
if (user.id == this.user.id) {
|
if (user.id == this.u.id) {
|
||||||
this.user.isFollowing = user.isFollowing;
|
this.user.isFollowing = user.isFollowing;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnfollow(user) {
|
onUnfollow(user) {
|
||||||
if (user.id == this.user.id) {
|
if (user.id == this.u.id) {
|
||||||
this.user.isFollowing = user.isFollowing;
|
this.user.isFollowing = user.isFollowing;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick() {
|
async onClick() {
|
||||||
this.wait = true;
|
this.wait = true;
|
||||||
if (this.user.isFollowing) {
|
|
||||||
(this as any).api('following/delete', {
|
try {
|
||||||
userId: this.user.id
|
if (this.u.isFollowing) {
|
||||||
}).then(() => {
|
this.u = await (this as any).api('following/delete', {
|
||||||
this.user.isFollowing = false;
|
userId: this.u.id
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
}).then(() => {
|
|
||||||
this.wait = false;
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
(this as any).api('following/create', {
|
if (this.u.isLocked && this.u.hasPendingFollowRequestFromYou) {
|
||||||
userId: this.user.id
|
this.u = await (this as any).api('following/requests/cancel', {
|
||||||
}).then(() => {
|
userId: this.u.id
|
||||||
this.user.isFollowing = true;
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
}).then(() => {
|
|
||||||
this.wait = false;
|
|
||||||
});
|
});
|
||||||
|
} else if (this.u.isLocked) {
|
||||||
|
this.u = await (this as any).api('following/create', {
|
||||||
|
userId: this.u.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.u = await (this as any).api('following/create', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
this.wait = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +127,7 @@ root(isDark)
|
|||||||
border 2px solid rgba($theme-color, 0.3)
|
border 2px solid rgba($theme-color, 0.3)
|
||||||
border-radius 8px
|
border-radius 8px
|
||||||
|
|
||||||
&.follow
|
&:not(.active)
|
||||||
color isDark ? #fff : #888
|
color isDark ? #fff : #888
|
||||||
background isDark ? linear-gradient(to bottom, #313543 0%, #282c37 100%) : linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
|
background isDark ? linear-gradient(to bottom, #313543 0%, #282c37 100%) : linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
|
||||||
border solid 1px isDark ? #1c2023 : #e2e2e2
|
border solid 1px isDark ? #1c2023 : #e2e2e2
|
||||||
@ -137,7 +140,7 @@ root(isDark)
|
|||||||
background isDark ? #22262f : #ececec
|
background isDark ? #22262f : #ececec
|
||||||
border-color isDark ? #151a1d : #dcdcdc
|
border-color isDark ? #151a1d : #dcdcdc
|
||||||
|
|
||||||
&.unfollow
|
&.active
|
||||||
color $theme-color-foreground
|
color $theme-color-foreground
|
||||||
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
|
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
|
||||||
border solid 1px lighten($theme-color, 15%)
|
border solid 1px lighten($theme-color, 15%)
|
||||||
@ -162,9 +165,6 @@ root(isDark)
|
|||||||
height 38px
|
height 38px
|
||||||
line-height 38px
|
line-height 38px
|
||||||
|
|
||||||
i
|
|
||||||
margin-right 8px
|
|
||||||
|
|
||||||
.mk-follow-button[data-darkmode]
|
.mk-follow-button[data-darkmode]
|
||||||
root(true)
|
root(true)
|
||||||
|
|
||||||
|
@ -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,12 +60,11 @@
|
|||||||
</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 v-if="$store.state.settings.showPostFormOnTopOfTl"/>
|
<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
|
||||||
<mk-timeline ref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
|
<mk-timeline class="tl" cref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
|
||||||
<mk-mentions @loaded="onTlLoaded" v-if="mode == 'mentions'"/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -77,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
|
||||||
@ -104,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');
|
||||||
@ -120,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();
|
||||||
@ -299,11 +352,18 @@ root(isDark)
|
|||||||
width calc(100% - 275px * 2)
|
width calc(100% - 275px * 2)
|
||||||
order 2
|
order 2
|
||||||
|
|
||||||
.mk-post-form
|
> .form
|
||||||
margin-bottom 16px
|
margin-bottom 16px
|
||||||
border solid 1px rgba(#000, 0.075)
|
border solid 1px rgba(#000, 0.075)
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
|
||||||
|
@media (max-width 700px)
|
||||||
|
padding 0
|
||||||
|
|
||||||
|
> .tl
|
||||||
|
border none
|
||||||
|
border-radius 0
|
||||||
|
|
||||||
> *:not(.main)
|
> *:not(.main)
|
||||||
width 275px
|
width 275px
|
||||||
padding 16px 0 16px 0
|
padding 16px 0 16px 0
|
||||||
|
@ -463,7 +463,7 @@ root(isDark)
|
|||||||
> .app
|
> .app
|
||||||
margin-right 8px
|
margin-right 8px
|
||||||
padding-right 8px
|
padding-right 8px
|
||||||
border-right solid 1px #eaeaea
|
border-right solid 1px isDark ? #1c2023 : #eaeaea
|
||||||
|
|
||||||
> .visibility
|
> .visibility
|
||||||
margin-left 8px
|
margin-left 8px
|
||||||
@ -552,7 +552,7 @@ root(isDark)
|
|||||||
padding 2px 8px 2px 16px
|
padding 2px 8px 2px 16px
|
||||||
font-size 90%
|
font-size 90%
|
||||||
color #8d969e
|
color #8d969e
|
||||||
background #edf0f3
|
background isDark ? #313543 : #edf0f3
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
|
||||||
&:before
|
&:before
|
||||||
@ -565,7 +565,7 @@ root(isDark)
|
|||||||
width 8px
|
width 8px
|
||||||
height 8px
|
height 8px
|
||||||
margin auto 0
|
margin auto 0
|
||||||
background #fff
|
background isDark ? #282c37 : #fff
|
||||||
border-radius 100%
|
border-radius 100%
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
|
@ -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() {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<template v-for="(notification, i) in _notifications">
|
<template v-for="(notification, i) in _notifications">
|
||||||
<div class="notification" :class="notification.type" :key="notification.id">
|
<div class="notification" :class="notification.type" :key="notification.id">
|
||||||
<mk-time :time="notification.createdAt"/>
|
<mk-time :time="notification.createdAt"/>
|
||||||
|
|
||||||
<template v-if="notification.type == 'reaction'">
|
<template v-if="notification.type == 'reaction'">
|
||||||
<mk-avatar class="avatar" :user="notification.user"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -17,6 +18,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'renote'">
|
<template v-if="notification.type == 'renote'">
|
||||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -28,6 +30,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'quote'">
|
<template v-if="notification.type == 'quote'">
|
||||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -37,6 +40,7 @@
|
|||||||
<router-link class="note-preview" :to="notification.note | notePage">{{ getNoteSummary(notification.note) }}</router-link>
|
<router-link class="note-preview" :to="notification.note | notePage">{{ getNoteSummary(notification.note) }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'follow'">
|
<template v-if="notification.type == 'follow'">
|
||||||
<mk-avatar class="avatar" :user="notification.user"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -45,6 +49,16 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="notification.type == 'receiveFollowRequest'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div class="text">
|
||||||
|
<p>%fa:user-clock%
|
||||||
|
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id">{{ notification.user | userName }}</router-link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'reply'">
|
<template v-if="notification.type == 'reply'">
|
||||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -54,6 +68,7 @@
|
|||||||
<router-link class="note-preview" :to="notification.note | notePage">{{ getNoteSummary(notification.note) }}</router-link>
|
<router-link class="note-preview" :to="notification.note | notePage">{{ getNoteSummary(notification.note) }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'mention'">
|
<template v-if="notification.type == 'mention'">
|
||||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -63,6 +78,7 @@
|
|||||||
<a class="note-preview" :href="notification.note | notePage">{{ getNoteSummary(notification.note) }}</a>
|
<a class="note-preview" :href="notification.note | notePage">{{ getNoteSummary(notification.note) }}</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'poll_vote'">
|
<template v-if="notification.type == 'poll_vote'">
|
||||||
<mk-avatar class="avatar" :user="notification.user"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
@ -73,6 +89,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="date" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date" :key="notification.id + '-time'">
|
<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-up%{{ notification._datetext }}</span>
|
||||||
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
|
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
|
||||||
@ -189,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
|
||||||
@ -251,6 +268,10 @@ root(isDark)
|
|||||||
.text p i
|
.text p i
|
||||||
color #53c7ce
|
color #53c7ce
|
||||||
|
|
||||||
|
&.receiveFollowRequest
|
||||||
|
.text p i
|
||||||
|
color #888
|
||||||
|
|
||||||
&.reply, &.mention
|
&.reply, &.mention
|
||||||
.text p i
|
.text p i
|
||||||
color #555
|
color #555
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<mk-window ref="window" is-modal @closed="$destroy">
|
<mk-window class="mk-post-form-window" ref="window" is-modal @closed="$destroy">
|
||||||
<span slot="header">
|
<span slot="header" class="mk-post-form-window--header">
|
||||||
<span :class="$style.icon" v-if="geo">%fa:map-marker-alt%</span>
|
<span class="icon" v-if="geo">%fa:map-marker-alt%</span>
|
||||||
<span v-if="!reply">%i18n:@note%</span>
|
<span v-if="!reply">%i18n:@note%</span>
|
||||||
<span v-if="reply">%i18n:@reply%</span>
|
<span v-if="reply">%i18n:@reply%</span>
|
||||||
<span :class="$style.count" v-if="media.length != 0">{{ '%i18n:@attaches%'.replace('{}', media.length) }}</span>
|
<span class="count" v-if="media.length != 0">{{ '%i18n:@attaches%'.replace('{}', media.length) }}</span>
|
||||||
<span :class="$style.count" v-if="uploadings.length != 0">{{ '%i18n:@uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
|
<span class="count" v-if="uploadings.length != 0">{{ '%i18n:@uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<mk-note-preview v-if="reply" :class="$style.notePreview" :note="reply"/>
|
<div class="mk-post-form-window--body">
|
||||||
|
<mk-note-preview v-if="reply" class="notePreview" :note="reply"/>
|
||||||
<mk-post-form ref="form"
|
<mk-post-form ref="form"
|
||||||
:reply="reply"
|
:reply="reply"
|
||||||
@posted="onPosted"
|
@posted="onPosted"
|
||||||
@ -16,6 +17,7 @@
|
|||||||
@change-attached-media="onChangeMedia"
|
@change-attached-media="onChangeMedia"
|
||||||
@geo-attached="onGeoAttached"
|
@geo-attached="onGeoAttached"
|
||||||
@geo-dettached="onGeoDettached"/>
|
@geo-dettached="onGeoDettached"/>
|
||||||
|
</div>
|
||||||
</mk-window>
|
</mk-window>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -56,11 +58,13 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" module>
|
<style lang="stylus" scoped>
|
||||||
.icon
|
root(isDark)
|
||||||
|
.mk-post-form-window--header
|
||||||
|
.icon
|
||||||
margin-right 8px
|
margin-right 8px
|
||||||
|
|
||||||
.count
|
.count
|
||||||
margin-left 8px
|
margin-left 8px
|
||||||
opacity 0.8
|
opacity 0.8
|
||||||
|
|
||||||
@ -70,7 +74,17 @@ export default Vue.extend({
|
|||||||
&:after
|
&:after
|
||||||
content ')'
|
content ')'
|
||||||
|
|
||||||
.notePreview
|
.mk-post-form-window--body
|
||||||
|
.notePreview
|
||||||
|
if isDark
|
||||||
margin 16px 22px 0 22px
|
margin 16px 22px 0 22px
|
||||||
|
else
|
||||||
|
margin 16px 22px
|
||||||
|
|
||||||
|
.mk-post-form-window[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.mk-post-form-window:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy">
|
||||||
|
<span slot="header">%fa:envelope R% %i18n:@title%</span>
|
||||||
|
|
||||||
|
<div class="slpqaxdoxhvglersgjukmvizkqbmbokc" :data-darkmode="$store.state.device.darkmode">
|
||||||
|
<div v-for="req in requests">
|
||||||
|
<router-link :key="req.id" :to="req.follower | userPage">{{ req.follower | userName }}</router-link>
|
||||||
|
<span>
|
||||||
|
<a @click="accept(req.follower)">%i18n:@accept%</a>|<a @click="reject(req.follower)">%i18n:@reject%</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mk-window>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
requests: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
(this as any).api('following/requests/list').then(requests => {
|
||||||
|
this.fetching = false;
|
||||||
|
this.requests = requests;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
accept(user) {
|
||||||
|
(this as any).api('following/requests/accept', { userId: user.id }).then(() => {
|
||||||
|
this.requests = this.requests.filter(r => r.follower.id != user.id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reject(user) {
|
||||||
|
(this as any).api('following/requests/reject', { userId: user.id }).then(() => {
|
||||||
|
this.requests = this.requests.filter(r => r.follower.id != user.id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
(this as any).$refs.window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
> button
|
||||||
|
margin-bottom 16px
|
||||||
|
|
||||||
|
> div
|
||||||
|
display flex
|
||||||
|
padding 16px
|
||||||
|
border solid 1px isDark ? #1c2023 : #eee
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
> span
|
||||||
|
margin 0 0 0 auto
|
||||||
|
|
||||||
|
.slpqaxdoxhvglersgjukmvizkqbmbokc[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.slpqaxdoxhvglersgjukmvizkqbmbokc:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
@ -23,7 +23,11 @@
|
|||||||
</label>
|
</label>
|
||||||
<button class="ui primary" @click="save">%i18n:@save%</button>
|
<button class="ui primary" @click="save">%i18n:@save%</button>
|
||||||
<section>
|
<section>
|
||||||
<h2>その他</h2>
|
<h2>%i18n:@locked-account%</h2>
|
||||||
|
<mk-switch v-model="$store.state.i.isLocked" @change="onChangeIsLocked" text="%i18n:@is-locked%"/>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>%i18n:@other%</h2>
|
||||||
<mk-switch v-model="$store.state.i.isBot" @change="onChangeIsBot" text="%i18n:@is-bot%"/>
|
<mk-switch v-model="$store.state.i.isBot" @change="onChangeIsBot" text="%i18n:@is-bot%"/>
|
||||||
<mk-switch v-model="$store.state.i.isCat" @change="onChangeIsCat" text="%i18n:@is-cat%"/>
|
<mk-switch v-model="$store.state.i.isCat" @change="onChangeIsCat" text="%i18n:@is-cat%"/>
|
||||||
</section>
|
</section>
|
||||||
@ -62,6 +66,11 @@ export default Vue.extend({
|
|||||||
(this as any).apis.notify('プロフィールを更新しました');
|
(this as any).apis.notify('プロフィールを更新しました');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onChangeIsLocked() {
|
||||||
|
(this as any).api('i/update', {
|
||||||
|
isLocked: this.$store.state.i.isLocked
|
||||||
|
});
|
||||||
|
},
|
||||||
onChangeIsBot() {
|
onChangeIsBot() {
|
||||||
(this as any).api('i/update', {
|
(this as any).api('i/update', {
|
||||||
isBot: this.$store.state.i.isBot
|
isBot: this.$store.state.i.isBot
|
||||||
|
@ -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',
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
<li @click="list">
|
<li @click="list">
|
||||||
<p>%fa:list%<span>%i18n:@lists%</span>%fa:angle-right%</p>
|
<p>%fa:list%<span>%i18n:@lists%</span>%fa:angle-right%</p>
|
||||||
</li>
|
</li>
|
||||||
|
<li @click="followRequests" v-if="$store.state.i.isLocked">
|
||||||
|
<p>%fa:envelope R%<span>%i18n:@follow-requests%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></span>%fa:angle-right%</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@ -46,6 +49,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import MkUserListsWindow from './user-lists-window.vue';
|
import MkUserListsWindow from './user-lists-window.vue';
|
||||||
|
import MkFollowRequestsWindow from './received-follow-requests-window.vue';
|
||||||
import MkSettingsWindow from './settings-window.vue';
|
import MkSettingsWindow from './settings-window.vue';
|
||||||
import MkDriveWindow from './drive-window.vue';
|
import MkDriveWindow from './drive-window.vue';
|
||||||
import contains from '../../../common/scripts/contains';
|
import contains from '../../../common/scripts/contains';
|
||||||
@ -91,6 +95,10 @@ export default Vue.extend({
|
|||||||
this.$router.push(`i/lists/${ list.id }`);
|
this.$router.push(`i/lists/${ list.id }`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
followRequests() {
|
||||||
|
this.close();
|
||||||
|
(this as any).os.new(MkFollowRequestsWindow);
|
||||||
|
},
|
||||||
settings() {
|
settings() {
|
||||||
this.close();
|
this.close();
|
||||||
(this as any).os.new(MkSettingsWindow);
|
(this as any).os.new(MkSettingsWindow);
|
||||||
@ -225,6 +233,16 @@ root(isDark)
|
|||||||
> span:first-child
|
> span:first-child
|
||||||
padding-left 22px
|
padding-left 22px
|
||||||
|
|
||||||
|
> span:nth-child(2)
|
||||||
|
> i
|
||||||
|
margin-left 4px
|
||||||
|
padding 2px 8px
|
||||||
|
font-size 90%
|
||||||
|
font-style normal
|
||||||
|
background $theme-color
|
||||||
|
color $theme-color-foreground
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
> [data-fa]:first-child
|
> [data-fa]:first-child
|
||||||
margin-right 6px
|
margin-right 6px
|
||||||
width 16px
|
width 16px
|
||||||
|
@ -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,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="mk-ui" :style="style">
|
||||||
<x-header/>
|
<x-header class="header"/>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
@ -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);
|
||||||
},
|
},
|
||||||
@ -35,3 +44,22 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.mk-ui
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
flex 1
|
||||||
|
background-size cover
|
||||||
|
background-position center
|
||||||
|
background-attachment fixed
|
||||||
|
|
||||||
|
> .header
|
||||||
|
@media (max-width 1000px)
|
||||||
|
display none
|
||||||
|
|
||||||
|
> .content
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
flex 1
|
||||||
|
overflow hidden
|
||||||
|
</style>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<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% リスト</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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,11 +32,14 @@ import MkNotifications from './views/pages/notifications.vue';
|
|||||||
import MkWidgets from './views/pages/widgets.vue';
|
import MkWidgets from './views/pages/widgets.vue';
|
||||||
import MkMessaging from './views/pages/messaging.vue';
|
import MkMessaging from './views/pages/messaging.vue';
|
||||||
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
||||||
|
import MkReceivedFollowRequests from './views/pages/received-follow-requests.vue';
|
||||||
import MkNote from './views/pages/note.vue';
|
import MkNote from './views/pages/note.vue';
|
||||||
import MkSearch from './views/pages/search.vue';
|
import MkSearch from './views/pages/search.vue';
|
||||||
import MkFollowers from './views/pages/followers.vue';
|
import MkFollowers from './views/pages/followers.vue';
|
||||||
import MkFollowing from './views/pages/following.vue';
|
import MkFollowing from './views/pages/following.vue';
|
||||||
import MkFavorites from './views/pages/favorites.vue';
|
import MkFavorites from './views/pages/favorites.vue';
|
||||||
|
import MkUserLists from './views/pages/user-lists.vue';
|
||||||
|
import MkUserList from './views/pages/user-list.vue';
|
||||||
import MkSettings from './views/pages/settings.vue';
|
import MkSettings from './views/pages/settings.vue';
|
||||||
import MkOthello from './views/pages/othello.vue';
|
import MkOthello from './views/pages/othello.vue';
|
||||||
|
|
||||||
@ -74,6 +77,9 @@ init((launch) => {
|
|||||||
{ path: '/i/settings', name: 'settings', component: MkSettings },
|
{ path: '/i/settings', name: 'settings', component: MkSettings },
|
||||||
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
|
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
|
||||||
{ path: '/i/favorites', name: 'favorites', component: MkFavorites },
|
{ path: '/i/favorites', name: 'favorites', component: MkFavorites },
|
||||||
|
{ path: '/i/lists', name: 'user-lists', component: MkUserLists },
|
||||||
|
{ path: '/i/lists/:list', name: 'user-list', component: MkUserList },
|
||||||
|
{ path: '/i/received-follow-requests', name: 'received-follow-requests', component: MkReceivedFollowRequests },
|
||||||
{ path: '/i/widgets', name: 'widgets', component: MkWidgets },
|
{ path: '/i/widgets', name: 'widgets', component: MkWidgets },
|
||||||
{ path: '/i/messaging', name: 'messaging', component: MkMessaging },
|
{ path: '/i/messaging', name: 'messaging', component: MkMessaging },
|
||||||
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<button class="mk-follow-button"
|
<button class="mk-follow-button"
|
||||||
:class="{ wait: wait, follow: !user.isFollowing, unfollow: user.isFollowing }"
|
:class="{ wait: wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou }"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
:disabled="wait"
|
:disabled="wait"
|
||||||
>
|
>
|
||||||
<template v-if="!wait && user.isFollowing">%fa:minus%</template>
|
<template v-if="!wait">
|
||||||
<template v-if="!wait && !user.isFollowing">%fa:plus%</template>
|
<template v-if="u.hasPendingFollowRequestFromYou">%fa:hourglass-half% %i18n:@request-pending%</template>
|
||||||
<template v-if="wait">%fa:spinner .pulse .fw%</template>
|
<template v-else-if="u.isFollowing">%fa:minus% %i18n:@following%</template>
|
||||||
{{ user.isFollowing ? '%i18n:@unfollow%' : '%i18n:@follow%' }}
|
<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>
|
||||||
|
<template v-else>%fa:spinner .pulse .fw%</template>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -22,6 +25,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
u: this.user,
|
||||||
wait: false,
|
wait: false,
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null
|
connectionId: null
|
||||||
@ -42,39 +46,44 @@ export default Vue.extend({
|
|||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
onFollow(user) {
|
onFollow(user) {
|
||||||
if (user.id == this.user.id) {
|
if (user.id == this.u.id) {
|
||||||
this.user.isFollowing = user.isFollowing;
|
this.u.isFollowing = user.isFollowing;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnfollow(user) {
|
onUnfollow(user) {
|
||||||
if (user.id == this.user.id) {
|
if (user.id == this.u.id) {
|
||||||
this.user.isFollowing = user.isFollowing;
|
this.u.isFollowing = user.isFollowing;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick() {
|
async onClick() {
|
||||||
this.wait = true;
|
this.wait = true;
|
||||||
if (this.user.isFollowing) {
|
|
||||||
(this as any).api('following/delete', {
|
try {
|
||||||
userId: this.user.id
|
if (this.u.isFollowing) {
|
||||||
}).then(() => {
|
this.u = await (this as any).api('following/delete', {
|
||||||
this.user.isFollowing = false;
|
userId: this.u.id
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
}).then(() => {
|
|
||||||
this.wait = false;
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
(this as any).api('following/create', {
|
if (this.u.isLocked && this.u.hasPendingFollowRequestFromYou) {
|
||||||
userId: this.user.id
|
this.u = await (this as any).api('following/requests/cancel', {
|
||||||
}).then(() => {
|
userId: this.u.id
|
||||||
this.user.isFollowing = true;
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
}).then(() => {
|
|
||||||
this.wait = false;
|
|
||||||
});
|
});
|
||||||
|
} else if (this.u.isLocked) {
|
||||||
|
this.u = await (this as any).api('following/create', {
|
||||||
|
userId: this.u.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.u = await (this as any).api('following/create', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
this.wait = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,18 +99,15 @@ export default Vue.extend({
|
|||||||
cursor pointer
|
cursor pointer
|
||||||
padding 0 16px
|
padding 0 16px
|
||||||
margin 0
|
margin 0
|
||||||
height inherit
|
min-width 150px
|
||||||
font-size 16px
|
line-height 36px
|
||||||
outline none
|
font-size 14px
|
||||||
border solid 1px $theme-color
|
font-weight bold
|
||||||
border-radius 4px
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
&.follow
|
|
||||||
color $theme-color
|
color $theme-color
|
||||||
background transparent
|
background transparent
|
||||||
|
outline none
|
||||||
|
border solid 1px $theme-color
|
||||||
|
border-radius 36px
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background rgba($theme-color, 0.1)
|
background rgba($theme-color, 0.1)
|
||||||
@ -109,15 +115,23 @@ export default Vue.extend({
|
|||||||
&:active
|
&:active
|
||||||
background rgba($theme-color, 0.2)
|
background rgba($theme-color, 0.2)
|
||||||
|
|
||||||
&.unfollow
|
&.active
|
||||||
color $theme-color-foreground
|
color $theme-color-foreground
|
||||||
background $theme-color
|
background $theme-color
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background lighten($theme-color, 10%)
|
||||||
|
border-color lighten($theme-color, 10%)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background darken($theme-color, 10%)
|
||||||
|
border-color darken($theme-color, 10%)
|
||||||
|
|
||||||
&.wait
|
&.wait
|
||||||
cursor wait !important
|
cursor wait !important
|
||||||
opacity 0.7
|
opacity 0.7
|
||||||
|
|
||||||
> [data-fa]
|
*
|
||||||
margin-right 4px
|
pointer-events none
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -502,7 +502,7 @@ root(isDark)
|
|||||||
padding 2px 8px 2px 16px
|
padding 2px 8px 2px 16px
|
||||||
font-size 90%
|
font-size 90%
|
||||||
color #8d969e
|
color #8d969e
|
||||||
background #edf0f3
|
background isDark ? #313543 : #edf0f3
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
|
||||||
&:before
|
&:before
|
||||||
@ -515,7 +515,7 @@ root(isDark)
|
|||||||
width 8px
|
width 8px
|
||||||
height 8px
|
height 8px
|
||||||
margin auto 0
|
margin auto 0
|
||||||
background #fff
|
background isDark ? #282c37 : #fff
|
||||||
border-radius 100%
|
border-radius 100%
|
||||||
|
|
||||||
> .media
|
> .media
|
||||||
|
@ -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() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-notification-preview" :class="notification.type">
|
<div class="mk-notification-preview" :class="notification.type">
|
||||||
<template v-if="notification.type == 'reaction'">
|
<template v-if="notification.type == 'reaction'">
|
||||||
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p><mk-reaction-icon :reaction="notification.reaction"/>{{ notification.user | userName }}</p>
|
<p><mk-reaction-icon :reaction="notification.reaction"/>{{ notification.user | userName }}</p>
|
||||||
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
|
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
|
||||||
@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'renote'">
|
<template v-if="notification.type == 'renote'">
|
||||||
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:retweet%{{ notification.note.user | userName }}</p>
|
<p>%fa:retweet%{{ notification.note.user | userName }}</p>
|
||||||
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%</p>
|
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%</p>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'quote'">
|
<template v-if="notification.type == 'quote'">
|
||||||
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:quote-left%{{ notification.note.user | userName }}</p>
|
<p>%fa:quote-left%{{ notification.note.user | userName }}</p>
|
||||||
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
||||||
@ -25,14 +25,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'follow'">
|
<template v-if="notification.type == 'follow'">
|
||||||
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:user-plus%{{ notification.user | userName }}</p>
|
<p>%fa:user-plus%{{ notification.user | userName }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="notification.type == 'receiveFollowRequest'">
|
||||||
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
|
<div class="text">
|
||||||
|
<p>%fa:user-clock%{{ notification.user | userName }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'reply'">
|
<template v-if="notification.type == 'reply'">
|
||||||
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:reply%{{ notification.note.user | userName }}</p>
|
<p>%fa:reply%{{ notification.note.user | userName }}</p>
|
||||||
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
||||||
@ -40,7 +47,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'mention'">
|
<template v-if="notification.type == 'mention'">
|
||||||
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:at%{{ notification.note.user | userName }}</p>
|
<p>%fa:at%{{ notification.note.user | userName }}</p>
|
||||||
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
||||||
@ -48,7 +55,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'poll_vote'">
|
<template v-if="notification.type == 'poll_vote'">
|
||||||
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:chart-pie%{{ notification.user | userName }}</p>
|
<p>%fa:chart-pie%{{ notification.user | userName }}</p>
|
||||||
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
|
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
|
||||||
@ -83,16 +90,14 @@ export default Vue.extend({
|
|||||||
display block
|
display block
|
||||||
clear both
|
clear both
|
||||||
|
|
||||||
img
|
> .avatar
|
||||||
display block
|
display block
|
||||||
float left
|
float left
|
||||||
min-width 36px
|
width 36px
|
||||||
min-height 36px
|
height 36px
|
||||||
max-width 36px
|
|
||||||
max-height 36px
|
|
||||||
border-radius 6px
|
border-radius 6px
|
||||||
|
|
||||||
.text
|
> .text
|
||||||
float right
|
float right
|
||||||
width calc(100% - 36px)
|
width calc(100% - 36px)
|
||||||
padding-left 8px
|
padding-left 8px
|
||||||
@ -120,6 +125,10 @@ export default Vue.extend({
|
|||||||
.text p i
|
.text p i
|
||||||
color #53c7ce
|
color #53c7ce
|
||||||
|
|
||||||
|
&.receiveFollowRequest
|
||||||
|
.text p i
|
||||||
|
color #888
|
||||||
|
|
||||||
&.reply, &.mention
|
&.reply, &.mention
|
||||||
.text p i
|
.text p i
|
||||||
color #fff
|
color #fff
|
||||||
|
@ -40,6 +40,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</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'">
|
<div class="notification poll_vote" v-if="notification.type == 'poll_vote'">
|
||||||
<mk-avatar class="avatar" :user="notification.user"/>
|
<mk-avatar class="avatar" :user="notification.user"/>
|
||||||
<div>
|
<div>
|
||||||
@ -55,15 +66,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="notification.type == 'quote'">
|
<template v-if="notification.type == 'quote'">
|
||||||
<mk-note :note="notification.note"/>
|
<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'reply'">
|
<template v-if="notification.type == 'reply'">
|
||||||
<mk-note :note="notification.note"/>
|
<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'mention'">
|
<template v-if="notification.type == 'mention'">
|
||||||
<mk-note :note="notification.note"/>
|
<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -78,6 +89,17 @@ export default Vue.extend({
|
|||||||
return {
|
return {
|
||||||
getNoteSummary
|
getNoteSummary
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNoteUpdated(note) {
|
||||||
|
switch (this.notification.type) {
|
||||||
|
case 'quote':
|
||||||
|
case 'reply':
|
||||||
|
case 'mention':
|
||||||
|
Vue.set(this.notification, 'note', note);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -156,6 +178,10 @@ root(isDark)
|
|||||||
> div > header i
|
> div > header i
|
||||||
color #53c7ce
|
color #53c7ce
|
||||||
|
|
||||||
|
&.receiveFollowRequest
|
||||||
|
> div > header i
|
||||||
|
color #888
|
||||||
|
|
||||||
.mk-notification[data-darkmode]
|
.mk-notification[data-darkmode]
|
||||||
root(true)
|
root(true)
|
||||||
|
|
||||||
|
@ -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">
|
||||||
|
@ -15,14 +15,16 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<ul>
|
<ul>
|
||||||
<li><router-link to="/" :data-active="$route.name == 'index'">%fa:home%%i18n:@home%%fa:angle-right%</router-link></li>
|
<li><router-link to="/" :data-active="$route.name == 'index'">%fa:home%%i18n:@timeline%%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/i/notifications" :data-active="$route.name == 'notifications'">%fa:R bell%%i18n:@notifications%<template v-if="hasUnreadNotification">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/notifications" :data-active="$route.name == 'notifications'">%fa:R bell%%i18n:@notifications%<template v-if="hasUnreadNotification">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'">%fa:R comments%%i18n:@messaging%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'">%fa:R comments%%i18n:@messaging%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
|
<li v-if="$store.getters.isSignedIn && $store.state.i.isLocked"><router-link to="/i/received-follow-requests" :data-active="$route.name == 'received-follow-requests'">%fa:R envelope%%i18n:@follow-requests%<template v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/othello" :data-active="$route.name == 'othello'">%fa:gamepad%%i18n:@game%<template v-if="hasGameInvitation">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/othello" :data-active="$route.name == 'othello'">%fa:gamepad%%i18n:@game%<template v-if="hasGameInvitation">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><router-link to="/i/widgets" :data-active="$route.name == 'widgets'">%fa:quidditch%%i18n:@widgets%%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/widgets" :data-active="$route.name == 'widgets'">%fa:R calendar-alt%%i18n:@widgets%%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/i/favorites" :data-active="$route.name == 'favorites'">%fa:star%%i18n:@favorites%%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/favorites" :data-active="$route.name == 'favorites'">%fa:star%%i18n:@favorites%%fa:angle-right%</router-link></li>
|
||||||
|
<li><router-link to="/i/lists" :data-active="$route.name == 'user-lists'">%fa:list%%i18n:@user-lists%%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/i/drive" :data-active="$route.name == 'drive'">%fa:cloud%%i18n:@drive%%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/drive" :data-active="$route.name == 'drive'">%fa:cloud%%i18n:@drive%%fa:angle-right%</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
@ -157,7 +159,10 @@ root(isDark)
|
|||||||
&:first-child
|
&:first-child
|
||||||
margin-top 0
|
margin-top 0
|
||||||
|
|
||||||
li
|
&:last-child
|
||||||
|
margin-bottom 0
|
||||||
|
|
||||||
|
> li
|
||||||
display block
|
display block
|
||||||
font-size 1em
|
font-size 1em
|
||||||
line-height 1em
|
line-height 1em
|
||||||
@ -199,7 +204,7 @@ root(isDark)
|
|||||||
opacity 0.5
|
opacity 0.5
|
||||||
|
|
||||||
.about
|
.about
|
||||||
margin 0
|
margin 0 0 8px 0
|
||||||
padding 1em 0
|
padding 1em 0
|
||||||
text-align center
|
text-align center
|
||||||
font-size 0.8em
|
font-size 0.8em
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<mk-ui>
|
||||||
|
<span slot="header">%fa:envelope R%%i18n:@title%</span>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div v-for="req in requests">
|
||||||
|
<router-link :key="req.id" :to="req.follower | userPage">{{ req.follower | userName }}</router-link>
|
||||||
|
<span>
|
||||||
|
<a @click="accept(req.follower)">%i18n:@accept%</a>|<a @click="reject(req.follower)">%i18n:@reject%</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</mk-ui>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Progress from '../../../common/scripts/loading';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
requests: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.title = 'Misskey | %i18n:@title%';
|
||||||
|
|
||||||
|
Progress.start();
|
||||||
|
|
||||||
|
(this as any).api('following/requests/list').then(requests => {
|
||||||
|
this.fetching = false;
|
||||||
|
this.requests = requests;
|
||||||
|
|
||||||
|
Progress.done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
accept(user) {
|
||||||
|
(this as any).api('following/requests/accept', { userId: user.id }).then(() => {
|
||||||
|
this.requests = this.requests.filter(r => r.follower.id != user.id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reject(user) {
|
||||||
|
(this as any).api('following/requests/reject', { userId: user.id }).then(() => {
|
||||||
|
this.requests = this.requests.filter(r => r.follower.id != user.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
main
|
||||||
|
width 100%
|
||||||
|
max-width 680px
|
||||||
|
margin 0 auto
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
@media (min-width 600px)
|
||||||
|
padding 32px
|
||||||
|
|
||||||
|
> div
|
||||||
|
display flex
|
||||||
|
padding 16px
|
||||||
|
border solid 1px isDark ? #1c2023 : #eee
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
> span
|
||||||
|
margin 0 0 0 auto
|
||||||
|
|
||||||
|
</style>
|
70
src/client/app/mobile/views/pages/user-list.vue
Normal file
70
src/client/app/mobile/views/pages/user-list.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<mk-ui>
|
||||||
|
<span slot="header" v-if="!fetching">%fa:list%{{ list.title }}</span>
|
||||||
|
|
||||||
|
<main v-if="!fetching">
|
||||||
|
<ul>
|
||||||
|
<li v-for="user in users" :key="user.id"><router-link :to="user | userPage">{{ user | userName }}</router-link></li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</mk-ui>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Progress from '../../../common/scripts/loading';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
list: null,
|
||||||
|
users: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: 'fetch'
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
Progress.start();
|
||||||
|
this.fetching = true;
|
||||||
|
|
||||||
|
(this as any).api('users/lists/show', {
|
||||||
|
listId: this.$route.params.list
|
||||||
|
}).then(list => {
|
||||||
|
this.list = list;
|
||||||
|
this.fetching = false;
|
||||||
|
|
||||||
|
Progress.done();
|
||||||
|
|
||||||
|
(this as any).api('users/show', {
|
||||||
|
userIds: this.list.userIds
|
||||||
|
}).then(users => {
|
||||||
|
this.users = users;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
main
|
||||||
|
width 100%
|
||||||
|
max-width 680px
|
||||||
|
margin 0 auto
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
@media (min-width 600px)
|
||||||
|
padding 32px
|
||||||
|
|
||||||
|
</style>
|
68
src/client/app/mobile/views/pages/user-lists.vue
Normal file
68
src/client/app/mobile/views/pages/user-lists.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<mk-ui>
|
||||||
|
<span slot="header">%fa:list%%i18n:@title%</span>
|
||||||
|
<template slot="func"><button @click="fn">%fa:plus%</button></template>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<ul>
|
||||||
|
<li v-for="list in lists" :key="list.id"><router-link :to="`/i/lists/${list.id}`">{{ list.title }}</router-link></li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</mk-ui>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Progress from '../../../common/scripts/loading';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
lists: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.title = 'Misskey | %i18n:@title%';
|
||||||
|
|
||||||
|
Progress.start();
|
||||||
|
|
||||||
|
(this as any).api('users/lists/list').then(lists => {
|
||||||
|
this.fetching = false;
|
||||||
|
this.lists = lists;
|
||||||
|
|
||||||
|
Progress.done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fn() {
|
||||||
|
(this as any).apis.input({
|
||||||
|
title: '%i18n:@enter-list-name%',
|
||||||
|
}).then(async title => {
|
||||||
|
const list = await (this as any).api('users/lists/create', {
|
||||||
|
title
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$router.push('/i/lists/' + list.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
main
|
||||||
|
width 100%
|
||||||
|
max-width 680px
|
||||||
|
margin 0 auto
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
@media (min-width 600px)
|
||||||
|
padding 32px
|
||||||
|
|
||||||
|
</style>
|
@ -184,7 +184,6 @@ root(isDark)
|
|||||||
|
|
||||||
> .mk-follow-button
|
> .mk-follow-button
|
||||||
float right
|
float right
|
||||||
height 40px
|
|
||||||
|
|
||||||
> .title
|
> .title
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
//#region Decktop home
|
||||||
|
if (state.home) {
|
||||||
|
w = state.home.find(w => w.id == x.id);
|
||||||
if (w) {
|
if (w) {
|
||||||
w.data = x.data;
|
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);
|
||||||
|
|
||||||
|
87
src/models/follow-request.ts
Normal file
87
src/models/follow-request.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import * as deepcopy from 'deepcopy';
|
||||||
|
import db from '../db/mongodb';
|
||||||
|
import { pack as packUser } from './user';
|
||||||
|
|
||||||
|
const FollowRequest = db.get<IFollowRequest>('followRequests');
|
||||||
|
FollowRequest.createIndex(['followerId', 'followeeId'], { unique: true });
|
||||||
|
export default FollowRequest;
|
||||||
|
|
||||||
|
export type IFollowRequest = {
|
||||||
|
_id: mongo.ObjectID;
|
||||||
|
createdAt: Date;
|
||||||
|
followeeId: mongo.ObjectID;
|
||||||
|
followerId: mongo.ObjectID;
|
||||||
|
|
||||||
|
// 非正規化
|
||||||
|
_followee: {
|
||||||
|
host: string;
|
||||||
|
inbox?: string;
|
||||||
|
},
|
||||||
|
_follower: {
|
||||||
|
host: string;
|
||||||
|
inbox?: string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FollowRequestを物理削除します
|
||||||
|
*/
|
||||||
|
export async function deleteFollowRequest(followRequest: string | mongo.ObjectID | IFollowRequest) {
|
||||||
|
let f: IFollowRequest;
|
||||||
|
|
||||||
|
// Populate
|
||||||
|
if (mongo.ObjectID.prototype.isPrototypeOf(followRequest)) {
|
||||||
|
f = await FollowRequest.findOne({
|
||||||
|
_id: followRequest
|
||||||
|
});
|
||||||
|
} else if (typeof followRequest === 'string') {
|
||||||
|
f = await FollowRequest.findOne({
|
||||||
|
_id: new mongo.ObjectID(followRequest)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
f = followRequest as IFollowRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f == null) return;
|
||||||
|
|
||||||
|
// このFollowingを削除
|
||||||
|
await FollowRequest.remove({
|
||||||
|
_id: f._id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pack a request for API response
|
||||||
|
*/
|
||||||
|
export const pack = (
|
||||||
|
request: any,
|
||||||
|
me?: any
|
||||||
|
) => new Promise<any>(async (resolve, reject) => {
|
||||||
|
let _request: any;
|
||||||
|
|
||||||
|
// Populate the request if 'request' is ID
|
||||||
|
if (mongo.ObjectID.prototype.isPrototypeOf(request)) {
|
||||||
|
_request = await FollowRequest.findOne({
|
||||||
|
_id: request
|
||||||
|
});
|
||||||
|
} else if (typeof request === 'string') {
|
||||||
|
_request = await FollowRequest.findOne({
|
||||||
|
_id: new mongo.ObjectID(request)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_request = deepcopy(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename _id to id
|
||||||
|
_request.id = _request._id;
|
||||||
|
delete _request._id;
|
||||||
|
|
||||||
|
// Populate follower
|
||||||
|
_request.follower = await packUser(_request.followerId, me);
|
||||||
|
|
||||||
|
// Populate followee
|
||||||
|
_request.followee = await packUser(_request.followeeId, me);
|
||||||
|
|
||||||
|
resolve(_request);
|
||||||
|
});
|
@ -111,6 +111,7 @@ export const pack = (notification: any) => new Promise<any>(async (resolve, reje
|
|||||||
|
|
||||||
switch (_notification.type) {
|
switch (_notification.type) {
|
||||||
case 'follow':
|
case 'follow':
|
||||||
|
case 'receiveFollowRequest':
|
||||||
// nope
|
// nope
|
||||||
break;
|
break;
|
||||||
case 'mention':
|
case 'mention':
|
||||||
|
@ -22,6 +22,7 @@ import FollowedLog, { deleteFollowedLog } from './followed-log';
|
|||||||
import SwSubscription, { deleteSwSubscription } from './sw-subscription';
|
import SwSubscription, { deleteSwSubscription } from './sw-subscription';
|
||||||
import Notification, { deleteNotification } from './notification';
|
import Notification, { deleteNotification } from './notification';
|
||||||
import UserList, { deleteUserList } from './user-list';
|
import UserList, { deleteUserList } from './user-list';
|
||||||
|
import FollowRequest, { deleteFollowRequest } from './follow-request';
|
||||||
|
|
||||||
const User = db.get<IUser>('users');
|
const User = db.get<IUser>('users');
|
||||||
|
|
||||||
@ -47,10 +48,26 @@ 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凍結されているか否か
|
||||||
|
*/
|
||||||
isSuspended: boolean;
|
isSuspended: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鍵アカウントか否か
|
||||||
|
*/
|
||||||
|
isLocked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このアカウントに届いているフォローリクエストの数
|
||||||
|
*/
|
||||||
|
pendingReceivedFollowRequestsCount: number;
|
||||||
|
|
||||||
host: string;
|
host: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -240,6 +257,16 @@ export async function deleteUser(user: string | mongo.ObjectID | IUser) {
|
|||||||
await Following.find({ followeeId: u._id })
|
await Following.find({ followeeId: u._id })
|
||||||
).map(x => deleteFollowing(x)));
|
).map(x => deleteFollowing(x)));
|
||||||
|
|
||||||
|
// このユーザーのFollowRequestをすべて削除
|
||||||
|
await Promise.all((
|
||||||
|
await FollowRequest.find({ followerId: u._id })
|
||||||
|
).map(x => deleteFollowRequest(x)));
|
||||||
|
|
||||||
|
// このユーザーへのFollowRequestをすべて削除
|
||||||
|
await Promise.all((
|
||||||
|
await FollowRequest.find({ followeeId: u._id })
|
||||||
|
).map(x => deleteFollowRequest(x)));
|
||||||
|
|
||||||
// このユーザーのFollowingLogをすべて削除
|
// このユーザーのFollowingLogをすべて削除
|
||||||
await Promise.all((
|
await Promise.all((
|
||||||
await FollowingLog.find({ userId: u._id })
|
await FollowingLog.find({ userId: u._id })
|
||||||
@ -386,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;
|
||||||
@ -395,7 +426,7 @@ export const pack = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (meId && !meId.equals(_user.id)) {
|
if (meId && !meId.equals(_user.id)) {
|
||||||
const [following1, following2, mute] = await Promise.all([
|
const [following1, following2, followReq1, followReq2, mute] = await Promise.all([
|
||||||
Following.findOne({
|
Following.findOne({
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
followeeId: _user.id
|
followeeId: _user.id
|
||||||
@ -404,6 +435,14 @@ export const pack = (
|
|||||||
followerId: _user.id,
|
followerId: _user.id,
|
||||||
followeeId: meId
|
followeeId: meId
|
||||||
}),
|
}),
|
||||||
|
_user.isLocked ? FollowRequest.findOne({
|
||||||
|
followerId: meId,
|
||||||
|
followeeId: _user.id
|
||||||
|
}) : Promise.resolve(null),
|
||||||
|
FollowRequest.findOne({
|
||||||
|
followerId: _user.id,
|
||||||
|
followeeId: meId
|
||||||
|
}),
|
||||||
Mute.findOne({
|
Mute.findOne({
|
||||||
muterId: meId,
|
muterId: meId,
|
||||||
muteeId: _user.id
|
muteeId: _user.id
|
||||||
@ -414,6 +453,9 @@ export const pack = (
|
|||||||
_user.isFollowing = following1 !== null;
|
_user.isFollowing = following1 !== null;
|
||||||
_user.isStalking = following1 && following1.stalk;
|
_user.isStalking = following1 && following1.stalk;
|
||||||
|
|
||||||
|
_user.hasPendingFollowRequestFromYou = followReq1 !== null;
|
||||||
|
_user.hasPendingFollowRequestToYou = followReq2 !== null;
|
||||||
|
|
||||||
// Whether the user is followed
|
// Whether the user is followed
|
||||||
_user.isFollowed = following2 !== null;
|
_user.isFollowed = following2 !== null;
|
||||||
|
|
||||||
|
27
src/remote/activitypub/kernel/accept/follow.ts
Normal file
27
src/remote/activitypub/kernel/accept/follow.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import User, { IRemoteUser } from '../../../../models/user';
|
||||||
|
import config from '../../../../config';
|
||||||
|
import accept from '../../../../services/following/requests/accept';
|
||||||
|
import { IFollow } from '../../type';
|
||||||
|
|
||||||
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
|
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||||
|
|
||||||
|
if (!id.startsWith(config.url + '/')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const follower = await User.findOne({
|
||||||
|
_id: new mongo.ObjectID(id.split('/').pop())
|
||||||
|
});
|
||||||
|
|
||||||
|
if (follower === null) {
|
||||||
|
throw new Error('follower not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (follower.host != null) {
|
||||||
|
throw new Error('フォローリクエストしたユーザーはローカルユーザーではありません');
|
||||||
|
}
|
||||||
|
|
||||||
|
await accept(actor, follower);
|
||||||
|
};
|
35
src/remote/activitypub/kernel/accept/index.ts
Normal file
35
src/remote/activitypub/kernel/accept/index.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import * as debug from 'debug';
|
||||||
|
|
||||||
|
import Resolver from '../../resolver';
|
||||||
|
import { IRemoteUser } from '../../../../models/user';
|
||||||
|
import acceptFollow from './follow';
|
||||||
|
import { IAccept } from '../../type';
|
||||||
|
|
||||||
|
const log = debug('misskey:activitypub');
|
||||||
|
|
||||||
|
export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => {
|
||||||
|
const uri = activity.id || activity;
|
||||||
|
|
||||||
|
log(`Accept: ${uri}`);
|
||||||
|
|
||||||
|
const resolver = new Resolver();
|
||||||
|
|
||||||
|
let object;
|
||||||
|
|
||||||
|
try {
|
||||||
|
object = await resolver.resolve(activity.object);
|
||||||
|
} catch (e) {
|
||||||
|
log(`Resolution failed: ${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (object.type) {
|
||||||
|
case 'Follow':
|
||||||
|
acceptFollow(actor, object);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`Unknown accept type: ${object.type}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
@ -23,5 +23,5 @@ export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
|||||||
throw new Error('フォローしようとしているユーザーはローカルユーザーではありません');
|
throw new Error('フォローしようとしているユーザーはローカルユーザーではありません');
|
||||||
}
|
}
|
||||||
|
|
||||||
await follow(actor, followee, activity);
|
await follow(actor, followee);
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,8 @@ import follow from './follow';
|
|||||||
import undo from './undo';
|
import undo from './undo';
|
||||||
import like from './like';
|
import like from './like';
|
||||||
import announce from './announce';
|
import announce from './announce';
|
||||||
|
import accept from './accept';
|
||||||
|
import reject from './reject';
|
||||||
|
|
||||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||||
switch (activity.type) {
|
switch (activity.type) {
|
||||||
@ -22,7 +24,11 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Accept':
|
case 'Accept':
|
||||||
// noop
|
await accept(actor, activity);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Reject':
|
||||||
|
await reject(actor, activity);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Announce':
|
case 'Announce':
|
||||||
|
27
src/remote/activitypub/kernel/reject/follow.ts
Normal file
27
src/remote/activitypub/kernel/reject/follow.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import User, { IRemoteUser } from '../../../../models/user';
|
||||||
|
import config from '../../../../config';
|
||||||
|
import reject from '../../../../services/following/requests/reject';
|
||||||
|
import { IFollow } from '../../type';
|
||||||
|
|
||||||
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
|
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||||
|
|
||||||
|
if (!id.startsWith(config.url + '/')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const follower = await User.findOne({
|
||||||
|
_id: new mongo.ObjectID(id.split('/').pop())
|
||||||
|
});
|
||||||
|
|
||||||
|
if (follower === null) {
|
||||||
|
throw new Error('follower not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (follower.host != null) {
|
||||||
|
throw new Error('フォローリクエストしたユーザーはローカルユーザーではありません');
|
||||||
|
}
|
||||||
|
|
||||||
|
await reject(actor, follower);
|
||||||
|
};
|
35
src/remote/activitypub/kernel/reject/index.ts
Normal file
35
src/remote/activitypub/kernel/reject/index.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import * as debug from 'debug';
|
||||||
|
|
||||||
|
import Resolver from '../../resolver';
|
||||||
|
import { IRemoteUser } from '../../../../models/user';
|
||||||
|
import rejectFollow from './follow';
|
||||||
|
import { IReject } from '../../type';
|
||||||
|
|
||||||
|
const log = debug('misskey:activitypub');
|
||||||
|
|
||||||
|
export default async (actor: IRemoteUser, activity: IReject): Promise<void> => {
|
||||||
|
const uri = activity.id || activity;
|
||||||
|
|
||||||
|
log(`Reject: ${uri}`);
|
||||||
|
|
||||||
|
const resolver = new Resolver();
|
||||||
|
|
||||||
|
let object;
|
||||||
|
|
||||||
|
try {
|
||||||
|
object = await resolver.resolve(activity.object);
|
||||||
|
} catch (e) {
|
||||||
|
log(`Resolution failed: ${e}`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (object.type) {
|
||||||
|
case 'Follow':
|
||||||
|
rejectFollow(actor, object);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`Unknown reject type: ${object.type}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
@ -2,7 +2,9 @@ import * as mongo from 'mongodb';
|
|||||||
import User, { IRemoteUser } from '../../../../models/user';
|
import User, { IRemoteUser } from '../../../../models/user';
|
||||||
import config from '../../../../config';
|
import config from '../../../../config';
|
||||||
import unfollow from '../../../../services/following/delete';
|
import unfollow from '../../../../services/following/delete';
|
||||||
|
import cancelRequest from '../../../../services/following/requests/cancel';
|
||||||
import { IFollow } from '../../type';
|
import { IFollow } from '../../type';
|
||||||
|
import FollowRequest from '../../../../models/follow-request';
|
||||||
|
|
||||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||||
@ -23,5 +25,14 @@ export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
|||||||
throw new Error('フォロー解除しようとしているユーザーはローカルユーザーではありません');
|
throw new Error('フォロー解除しようとしているユーザーはローカルユーザーではありません');
|
||||||
}
|
}
|
||||||
|
|
||||||
await unfollow(actor, followee, activity);
|
const req = await FollowRequest.findOne({
|
||||||
|
followerId: actor._id,
|
||||||
|
followeeId: followee._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (req) {
|
||||||
|
await cancelRequest(actor, followee);
|
||||||
|
} else {
|
||||||
|
await unfollow(actor, followee);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -93,6 +93,7 @@ export async function createPerson(value: any, resolver?: Resolver): Promise<IUs
|
|||||||
notesCount,
|
notesCount,
|
||||||
name: person.name,
|
name: person.name,
|
||||||
driveCapacity: 1024 * 1024 * 8, // 8MiB
|
driveCapacity: 1024 * 1024 * 8, // 8MiB
|
||||||
|
isLocked: person.manuallyApprovesFollowers,
|
||||||
username: person.preferredUsername,
|
username: person.preferredUsername,
|
||||||
usernameLower: person.preferredUsername.toLowerCase(),
|
usernameLower: person.preferredUsername.toLowerCase(),
|
||||||
host,
|
host,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
import { IRemoteUser, ILocalUser } from '../../../models/user';
|
import { IUser, isLocalUser } from '../../../models/user';
|
||||||
|
|
||||||
export default (follower: ILocalUser, followee: IRemoteUser) => ({
|
export default (follower: IUser, followee: IUser) => ({
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
actor: `${config.url}/users/${follower._id}`,
|
actor: isLocalUser(follower) ? `${config.url}/users/${follower._id}` : follower.uri,
|
||||||
object: followee.uri
|
object: isLocalUser(followee) ? `${config.url}/users/${followee._id}` : followee.uri
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import renderImage from './image';
|
import renderImage from './image';
|
||||||
import renderKey from './key';
|
import renderKey from './key';
|
||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
|
import { ILocalUser } from '../../../models/user';
|
||||||
|
|
||||||
export default user => {
|
export default (user: ILocalUser) => {
|
||||||
const id = `${config.url}/users/${user._id}`;
|
const id = `${config.url}/users/${user._id}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -17,6 +18,7 @@ export default user => {
|
|||||||
summary: user.description,
|
summary: user.description,
|
||||||
icon: user.avatarId && renderImage({ _id: user.avatarId }),
|
icon: user.avatarId && renderImage({ _id: user.avatarId }),
|
||||||
image: user.bannerId && renderImage({ _id: user.bannerId }),
|
image: user.bannerId && renderImage({ _id: user.bannerId }),
|
||||||
|
manuallyApprovesFollowers: user.isLocked,
|
||||||
publicKey: renderKey(user)
|
publicKey: renderKey(user)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
4
src/remote/activitypub/renderer/reject.ts
Normal file
4
src/remote/activitypub/renderer/reject.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default object => ({
|
||||||
|
type: 'Reject',
|
||||||
|
object
|
||||||
|
});
|
@ -45,6 +45,7 @@ export interface IPerson extends IObject {
|
|||||||
type: 'Person';
|
type: 'Person';
|
||||||
name: string;
|
name: string;
|
||||||
preferredUsername: string;
|
preferredUsername: string;
|
||||||
|
manuallyApprovesFollowers: boolean;
|
||||||
inbox: string;
|
inbox: string;
|
||||||
publicKey: any;
|
publicKey: any;
|
||||||
followers: any;
|
followers: any;
|
||||||
@ -82,6 +83,10 @@ export interface IAccept extends IActivity {
|
|||||||
type: 'Accept';
|
type: 'Accept';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IReject extends IActivity {
|
||||||
|
type: 'Reject';
|
||||||
|
}
|
||||||
|
|
||||||
export interface ILike extends IActivity {
|
export interface ILike extends IActivity {
|
||||||
type: 'Like';
|
type: 'Like';
|
||||||
_misskey_reaction: string;
|
_misskey_reaction: string;
|
||||||
@ -99,5 +104,6 @@ export type Object =
|
|||||||
IUndo |
|
IUndo |
|
||||||
IFollow |
|
IFollow |
|
||||||
IAccept |
|
IAccept |
|
||||||
|
IReject |
|
||||||
ILike |
|
ILike |
|
||||||
IAnnounce;
|
IAnnounce;
|
||||||
|
@ -7,7 +7,7 @@ const httpSignature = require('http-signature');
|
|||||||
import { createHttp } from '../queue';
|
import { createHttp } from '../queue';
|
||||||
import pack from '../remote/activitypub/renderer';
|
import pack from '../remote/activitypub/renderer';
|
||||||
import Note from '../models/note';
|
import Note from '../models/note';
|
||||||
import User, { isLocalUser } from '../models/user';
|
import User, { isLocalUser, ILocalUser } from '../models/user';
|
||||||
import renderNote from '../remote/activitypub/renderer/note';
|
import renderNote from '../remote/activitypub/renderer/note';
|
||||||
import renderKey from '../remote/activitypub/renderer/key';
|
import renderKey from '../remote/activitypub/renderer/key';
|
||||||
import renderPerson from '../remote/activitypub/renderer/person';
|
import renderPerson from '../remote/activitypub/renderer/person';
|
||||||
@ -69,7 +69,10 @@ router.get('/notes/:note', async (ctx, next) => {
|
|||||||
router.get('/users/:user/outbox', async ctx => {
|
router.get('/users/:user/outbox', async ctx => {
|
||||||
const userId = new mongo.ObjectID(ctx.params.user);
|
const userId = new mongo.ObjectID(ctx.params.user);
|
||||||
|
|
||||||
const user = await User.findOne({ _id: userId });
|
const user = await User.findOne({
|
||||||
|
_id: userId,
|
||||||
|
host: null
|
||||||
|
});
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
@ -91,7 +94,10 @@ router.get('/users/:user/outbox', async ctx => {
|
|||||||
router.get('/users/:user/publickey', async ctx => {
|
router.get('/users/:user/publickey', async ctx => {
|
||||||
const userId = new mongo.ObjectID(ctx.params.user);
|
const userId = new mongo.ObjectID(ctx.params.user);
|
||||||
|
|
||||||
const user = await User.findOne({ _id: userId });
|
const user = await User.findOne({
|
||||||
|
_id: userId,
|
||||||
|
host: null
|
||||||
|
});
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
@ -109,14 +115,17 @@ router.get('/users/:user/publickey', async ctx => {
|
|||||||
router.get('/users/:user', async ctx => {
|
router.get('/users/:user', async ctx => {
|
||||||
const userId = new mongo.ObjectID(ctx.params.user);
|
const userId = new mongo.ObjectID(ctx.params.user);
|
||||||
|
|
||||||
const user = await User.findOne({ _id: userId });
|
const user = await User.findOne({
|
||||||
|
_id: userId,
|
||||||
|
host: null
|
||||||
|
});
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = pack(renderPerson(user));
|
ctx.body = pack(renderPerson(user as ILocalUser));
|
||||||
});
|
});
|
||||||
|
|
||||||
// follow form
|
// follow form
|
||||||
|
@ -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,
|
||||||
@ -448,6 +453,26 @@ const endpoints: Endpoint[] = [
|
|||||||
},
|
},
|
||||||
kind: 'following-write'
|
kind: 'following-write'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'following/requests/accept',
|
||||||
|
withCredential: true,
|
||||||
|
kind: 'following-write'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'following/requests/reject',
|
||||||
|
withCredential: true,
|
||||||
|
kind: 'following-write'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'following/requests/cancel',
|
||||||
|
withCredential: true,
|
||||||
|
kind: 'following-write'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'following/requests/list',
|
||||||
|
withCredential: true,
|
||||||
|
kind: 'following-read'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'following/stalk',
|
name: 'following/stalk',
|
||||||
withCredential: true,
|
withCredential: true,
|
||||||
@ -555,7 +580,7 @@ const endpoints: Endpoint[] = [
|
|||||||
withCredential: true,
|
withCredential: true,
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
max: 100
|
max: 300
|
||||||
},
|
},
|
||||||
kind: 'reaction-write'
|
kind: 'reaction-write'
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Module dependencies
|
* Module dependencies
|
||||||
*/
|
*/
|
||||||
import $ from 'cafy'; import ID from '../../../../cafy-id';
|
import $ from 'cafy'; import ID from '../../../../cafy-id';
|
||||||
import User from '../../../../models/user';
|
import User, { pack } from '../../../../models/user';
|
||||||
import Following from '../../../../models/following';
|
import Following from '../../../../models/following';
|
||||||
import create from '../../../../services/following/create';
|
import create from '../../../../services/following/create';
|
||||||
|
|
||||||
@ -49,5 +49,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
|
|||||||
create(follower, followee);
|
create(follower, followee);
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res();
|
res(await pack(followee._id, user));
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Module dependencies
|
* Module dependencies
|
||||||
*/
|
*/
|
||||||
import $ from 'cafy'; import ID from '../../../../cafy-id';
|
import $ from 'cafy'; import ID from '../../../../cafy-id';
|
||||||
import User from '../../../../models/user';
|
import User, { pack } from '../../../../models/user';
|
||||||
import Following from '../../../../models/following';
|
import Following from '../../../../models/following';
|
||||||
import deleteFollowing from '../../../../services/following/delete';
|
import deleteFollowing from '../../../../services/following/delete';
|
||||||
|
|
||||||
@ -49,5 +49,5 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
|
|||||||
deleteFollowing(follower, followee);
|
deleteFollowing(follower, followee);
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res();
|
res(await pack(followee._id, user));
|
||||||
});
|
});
|
||||||
|
26
src/server/api/endpoints/following/requests/accept.ts
Normal file
26
src/server/api/endpoints/following/requests/accept.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import $ from 'cafy'; import ID from '../../../../../cafy-id';
|
||||||
|
import acceptFollowRequest from '../../../../../services/following/requests/accept';
|
||||||
|
import User from '../../../../../models/user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a follow request
|
||||||
|
*/
|
||||||
|
module.exports = (params, user) => new Promise(async (res, rej) => {
|
||||||
|
// Get 'userId' parameter
|
||||||
|
const [followerId, followerIdErr] = $.type(ID).get(params.userId);
|
||||||
|
if (followerIdErr) return rej('invalid userId param');
|
||||||
|
|
||||||
|
// Fetch follower
|
||||||
|
const follower = await User.findOne({
|
||||||
|
_id: followerId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (follower === null) {
|
||||||
|
return rej('follower not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
await acceptFollowRequest(user, follower);
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res();
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user