Compare commits

...

16 Commits

Author SHA1 Message Date
ade1e40395 12.6.0 2020-02-10 07:25:49 +09:00
c93b8677e4 New Crowdin translations (#5899)
* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-02-10 07:25:32 +09:00
62683d8878 なんかもうめっちゃ変えた
Resolve #5900
2020-02-10 07:23:43 +09:00
3d1239c1b4 Better initial widgets 2020-02-10 05:44:47 +09:00
5268bade66 Better widgets 2020-02-10 05:42:03 +09:00
514eb39a14 ユーザーページからグループに招待できるように 2020-02-10 05:03:01 +09:00
18628b821e Better title adjust logic 2020-02-10 04:04:10 +09:00
344fbe6bcd Note page title 2020-02-10 03:55:33 +09:00
afb8cd2dc1 Update CHANGELOG.md 2020-02-10 03:51:58 +09:00
1a5f385eb5 Improve mfm link 2020-02-10 03:48:45 +09:00
8df7864064 Clean up 2020-02-10 03:16:34 +09:00
9ca60bad7f Update url-preview-popup.vue 2020-02-10 03:13:24 +09:00
bd828bb072 Better resize observe 2020-02-10 03:13:22 +09:00
892cb44d84 Resolve #3644 2020-02-10 02:59:00 +09:00
517ea6a119 Refactor 2020-02-10 02:42:06 +09:00
ba8ffda32a New Crowdin translations (#5884)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)
2020-02-09 22:47:54 +09:00
23 changed files with 749 additions and 189 deletions

View File

@ -1,6 +1,16 @@
ChangeLog
=========
12.6.0 (2020/02/10)
--------------------
### ✨Improvements
* リンクにホバーするとURLプレビューを表示するように
* ユーザーページからグループに招待できるように
* ウィジェットはブラウザごとに記憶するように
### 🐛Fixes
* 要素の幅を判定する処理が上手くいかないことがある問題を修正
12.5.0 (2020/02/09)
--------------------
### ✨Improvements

View File

@ -214,12 +214,10 @@ remove: "Delete"
removed: "Successfully deleted"
removeAreYouSure: "Are you sure that you want to delete \"{x}\"?"
saved: "Saved"
messaging: "Talk"
messaging: "Messaging"
upload: "Upload"
fromDrive: "From Drive"
fromUrl: "From URL"
editWidgets: "Edit widgets"
exitEdit: "Finish editing"
explore: "Explore"
games: "Misskey Games"
messageRead: "Read"
@ -348,6 +346,7 @@ post: "Notes"
posted: "Posted!"
autoReloadWhenDisconnected: "Auto reload when disconnected with server"
autoNoteWatch: "Watch note automatically"
autoNoteWatchDescription: "Get notified about the notes which you reactioned or replied."
reduceUiAnimation: "Reduce animations of User Interface"
share: "Share"
notFound: "Not found"
@ -356,7 +355,7 @@ uploadFolder: "Default Upload location"
cacheClear: "Clear cache"
markAsReadAllNotifications: "Mark all notifications as read"
markAsReadAllUnreadNotes: "Mark all notes as read"
markAsReadAllTalkMessages: "Mark all conversations as read"
markAsReadAllTalkMessages: "Mark all messages as read"
help: "Help"
inputMessageHere: "Enter message here"
close: "Close"
@ -369,6 +368,36 @@ invites: "Invite"
groupName: "Group name"
members: "Members"
transfer: "Transfer"
messagingWithUser: "Messaging with other user"
messagingWithGroup: "Messaging within group"
enable: "Enable"
next: "Next"
retype: "Enter again"
noteOf: "{user}'s notes"
inviteToGroup: "Invite to group"
_tutorial:
title: "How to use Misskey"
step1_1: "Welcome!"
step1_2: "This page is called \"timeline\". It shows chronologically ordered \"notes\" of people who you \"follow\"."
step1_3: "Your timeline is currently empty, since you have not posed any notes or followed anyone yet."
step2_1: "Let's finish setting up your profile before writing a note or following anyone."
step2_2: "Providing some information about who you are will make it easier for others to follow you back."
step3_1: "Finished setting up your profile?"
step3_2: "The next step is to post a note. You can do this by pressing a pencil icon on the screen."
step3_3: "Fill in the modal and press the button on the right top to post."
step3_4: "Have nothing to say? Try \"I just started Misskey!\""
step4_1: "Finished posting your first note?"
step4_2: "Hurray! Now your first note is displayed on your timeline."
step5_1: "Now, let's try making your timeline more lively by following other people."
step5_2: "{featured} will show you trending notes in this instance. {explore} will let you find trending users. Try following people you like!"
step5_3: "To follow other users, click on their icon and press \"follow\" button on their profile."
step5_4: "If the other user has a lock icon next to their name, that user will have to manually approve your follow request."
step6_1: "Now you will be able to see other users' notes on your timeline."
step6_2: "You can also put \"reactions\" on other people's notes to quickly respond."
step6_3: "To attach a \"reaction\", press \"+\" mark on other user's note and choose an emoji you'd like to react with."
step7_1: "Congratulations! You have now finished Misskey's basic tutorial."
step7_2: "If you would like to learn more about Misskey, try the {help} section."
step7_3: "Good luck and have fun! 🚀"
_2fa:
alreadyRegistered: "You have already registered 2-factor authentication device."
registerDevice: "Register a new device"
@ -389,11 +418,11 @@ _permissions:
"write:favorites": "Edit your favorites list"
"read:following": "View your following information"
"write:following": "Follow or unfollow other accounts"
"read:messaging": "View your talks"
"write:messaging": "Start or delete your talks"
"read:messaging": "View your messages"
"write:messaging": "Compose or Delete messages"
"read:mutes": "View the list of people you muted"
"write:mutes": "Edit the list of people you muted"
"write:notes": "Compose and delete notes"
"write:notes": "Compose or Delete notes"
"read:notifications": "View notifications"
"write:notifications": "Work with notifications"
"read:reactions": "View reactions"
@ -466,6 +495,7 @@ _visibility:
followersDescription: "Post to followers only"
specified: "Direct"
specifiedDescription: "Post to specified users only"
localOnly: "Local only"
_postForm:
replyPlaceholder: "Reply to this note..."
quotePlaceholder: "Quote this note..."

View File

@ -214,18 +214,16 @@ remove: "Borrar"
removed: "Borrado"
removeAreYouSure: "¿Desea borrar \"{x}\"?"
saved: "Guardado"
messaging: "Conversación"
messaging: "Chat"
upload: "Subir"
fromDrive: "Desde el drive"
fromUrl: "Desde la URL"
editWidgets: "Editar widgets"
exitEdit: "Terminar edición"
explore: "Explorar"
games: "Misskey Games"
messageRead: "Ya leído"
recentUsedEmojis: "Emojis usados recientemente"
noMoreHistory: "El historial se ha acabado"
startMessaging: "Iniciar conversación"
startMessaging: "Iniciar chat"
nUsersRead: "Leído por {n} personas"
agreeTo: "De acuerdo con {0}"
tos: "Términos de uso"
@ -348,12 +346,39 @@ post: "Nota"
posted: "Posteado"
autoReloadWhenDisconnected: "Recargar automáticamente cuando el servidor está desconectado"
autoNoteWatch: "Ver nota automáticamente"
autoNoteWatchDescription: "Recibe notificaciones sobre las notas de otros usuarios que a los que respondiste y reaccionaste"
reduceUiAnimation: "Reducir la animación de la UI"
share: "Compartir"
notFound: "No se encuentra"
notFoundDescription: "No se encontró la página correspondiente a la URL elegida"
uploadFolder: "Carpeta de subidas por defecto"
cacheClear: "Borrar caché"
markAsReadAllNotifications: "Marcar todas las notificaciones como leídas"
markAsReadAllUnreadNotes: "Marcar todas las notas como leídas"
markAsReadAllTalkMessages: "Marcar todos los chats como leídos"
help: "Ayuda"
inputMessageHere: "Escribe el mensaje aquí"
close: "Cerrar"
group: "Grupo"
groups: "Grupos"
createGroup: "Crear grupo"
ownedGroups: "Tus"
joinedGroups: "Grupos a los que me uní"
invites: "Invitar"
groupName: "Nombre del grupo"
members: "Miembros"
transfer: "Transferir"
messagingWithUser: "Chatear con usuario"
messagingWithGroup: "Chatear en grupo"
enable: "Activar"
next: "Siguiente"
retype: "Intentar de nuevo"
_tutorial:
title: "Cómo usar Misskey"
step1_1: "Bienvenido"
step1_2: "Esta imagen se llama \"Linea de tiempo\" y muestra en orden cronológico las \"notas\" tuyas y de la gente que \"sigues\""
step1_3: "Si no estás escribiendo ninguna nota y no estás siguiendo a nadie, es esperable que no se muestre nada en la linea de tiempo"
step2_1: "Antes de crear notas y seguir a alguien, primero vamos a crear tu perfil"
_2fa:
registerDevice: "Registrar dispositivo"
step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
@ -371,8 +396,6 @@ _permissions:
"write:favorites": "Addministrar favoritos"
"read:following": "Ver información de seguidor"
"write:following": "Seguir o dejar de seguir"
"read:messaging": "Ver conversación"
"write:messaging": "Administrar coversación"
"read:mutes": "Ver usuarios silenciados"
"write:mutes": "Administrar usuarios silenciados"
"write:notes": "Crear/borrar notas"

View File

@ -219,8 +219,6 @@ messaging: "チャット"
upload: "アップロード"
fromDrive: "ドライブから"
fromUrl: "URLから"
editWidgets: "ウィジェットを編集"
exitEdit: "編集を終了"
explore: "みつける"
games: "Misskey Games"
messageRead: "既読"
@ -376,6 +374,8 @@ messagingWithGroup: "グループでチャット"
enable: "有効にする"
next: "次"
retype: "再入力"
noteOf: "{user}のノート"
inviteToGroup: "グループに招待"
_tutorial:
title: "Misskeyの使い方"

View File

@ -1 +1,169 @@
---
_ago:
unknown: "謎"
future: "未来"
justNow: "たった今"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分前"
hoursAgo: "{n}時間前"
daysAgo: "{n}日前"
weeksAgo: "{n}週間前"
monthsAgo: "{n}ヶ月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分"
hour: "時間"
day: "日"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ート」を作成しぃ、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素はよ反応を追加することもできます✌\n新しい世界を探検しよう🚀"
monthAndDay: "{month}月 {day}日"
search: "探す"
notifications: "通知"
username: "ユーザー名"
password: "パスワード"
fetchingAsApObject: "連合に照会中"
ok: "おっけー"
gotIt: "ほい"
cancel: "やめとくわ"
enterUsername: "ユーザー名を入れてや"
renotedBy: "{user}がRenote"
noNotes: "ノートはあらへん"
noNotifications: "通知はあらへん"
instance: "インスタンス"
settings: "設定"
profile: "プロフィール"
timeline: "タイムライン"
noAccountDescription: "自己紹介はあらへん"
login: "ログイン"
loggingIn: "ログインしとります"
logout: "ログアウト"
signup: "新規登録"
uploading: "アップロードしとります"
save: "保存"
users: "ユーザー"
addUser: "ユーザー増やす"
favorite: "お気に入り"
favorites: "お気に入り"
unfavorite: "お気に入りやめる"
pin: "ピン留め"
unpin: "ピン留めやめる"
copyContent: "内容をコピー"
copyLink: "リンクをコピー"
delete: "ほかす"
addToList: "リストに入れたる"
reply: "返す"
loadMore: "もっとあるやろ!"
mentions: "あんた宛て"
directNotes: "ダイレクト投稿"
import: "インポート"
export: "エクスポート"
files: "ファイル"
download: "ダウンロード"
lists: "リスト"
noLists: "リストはあらへん"
followsYou: "フォローされとるで"
error: "問題が発生してん"
enterListName: "リスト名を入れてや"
privacy: "プライバシーってなんや?オカンの年齢か?"
makeFollowManuallyApprove: "他人のフォローは許可してからや!"
defaultNoteVisibility: "もとからの公開範囲"
follow: "フォロー"
followRequest: "フォロー許してくれや!言うてみる"
followRequests: "フォロー許してくれや!"
followRequestPending: "フォロー許してくれるん待っとる"
enterEmoji: "絵文字を入れてや"
you: "あんた"
clickToShow: "押してみ、見せたるわ"
sensitive: "見たらあかんで"
add: "増やす"
reaction: "リアクション"
renameFile: "ファイル名をいらう"
attachCancel: "くっつけるのやめよか"
markAsSensitive: "ちょっと見せられへんわ"
unmarkAsSensitive: "別にええんじゃね?"
enterFileName: "ファイル名を入れてや"
mute: "ミュート"
unmute: "ミュートやめたる"
block: "ブロック"
unblock: "ブロックやめたる"
suspend: "凍結"
unsuspend: "溶かす"
customEmojis: "カスタム絵文字"
cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定をチャラにすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されへんので通信量が増加します。"
loginFailed: "ログインに失敗してん"
wallpaper: "壁紙"
removeWallpaper: "壁紙ほかす"
youHaveNoLists: "リストはあらへん"
proxyAccountDescription: "プロキシアカウントは、代わりにフォローしてくれるアカウントや。例えば、551に豚まんが無いときやったり、ユーザーがリモートユーザーをアカウントに入れたとき、リストに入れられたユーザーが誰からもフォローされてないと寂しいやん。寂しいし、アクティビティも配達されへんから、プロキシアカウントがフォローしてくれるで。ええやつやん…"
host: "ホスト"
federation: "連合"
instances: "インスタンス"
charts: "チャート"
perHour: "1時間ごと"
perDay: "1日ごと"
operations: "操作"
version: "バージョン"
network: "ネットワーク"
statistics: "統計"
clearQueueConfirmText: "未配達の投稿は配送されなくなるで。通常この操作を行う必要はあらへんや。"
muteAndBlock: "ミュートとブロック"
noUsers: "ユーザーはおらへん"
pinLimitExceeded: "これ以上ピン留めできひん"
intro: "Misskeyのインストールが完了してん管理者アカウントを作ってや。"
noCustomEmojis: "絵文字はあらへん"
noJobs: "ジョブはあらへん"
all: "みな"
retypedNotMatch: "そやないねん。"
remove: "ほかす"
noMoreHistory: "これより過去の履歴はあらへんで"
nsfw: "見たらあかんで"
userList: "リスト"
about: "情報"
aboutMisskey: "Misskeyってなんや"
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
close: "さいなら"
joinedGroups: "参加しとるグループ"
_2fa:
alreadyRegistered: "もう設定終わっとるわ"
_auth:
permissionAsk: "このアプリは次の権限を要求しとるで"
_antennaSources:
all: "みなのノート"
homeTimeline: "フォローしとるユーザーのノート"
_widgets:
notifications: "通知"
timeline: "タイムライン"
_cw:
show: "もっとあるやろ!"
_poll:
noMore: "これ以上追加でけへん"
deadlineTime: "時間"
_visibility:
publicDescription: "みなのユーザーに公開"
_profile:
username: "ユーザー名"
_exportOrImport:
allNotes: "全てのノート"
muteList: "ミュート"
blockingList: "ブロック"
userLists: "リスト"
_pages:
script:
categories:
list: "リスト"
blocks:
_join:
arg1: "リスト"
_randomPick:
arg1: "リスト"
_dailyRandomPick:
arg1: "リスト"
_seedRandomPick:
arg2: "リスト"
_pick:
arg1: "リスト"
_listLen:
arg1: "リスト"
types:
array: "リスト"

View File

@ -218,14 +218,12 @@ messaging: "대화"
upload: "업로드"
fromDrive: "드라이브에서"
fromUrl: "URL로부터"
editWidgets: "위젯 편집"
exitEdit: "편집 종료"
explore: "발견하기"
games: "Misskey Games"
messageRead: "읽음"
recentUsedEmojis: "최근에 사용한 이모지"
noMoreHistory: "이것보다 과거의 기록이 없습니다"
startMessaging: "대화 시작"
startMessaging: "대화 시작하기"
nUsersRead: "{n}명이 읽음"
agreeTo: "{0}에 동의"
tos: "이용 약관"
@ -348,6 +346,7 @@ post: "작성"
posted: "게시하였습니다"
autoReloadWhenDisconnected: "서버와의 연결이 끊기면 자동 새로고침"
autoNoteWatch: "노트를 자동으로 지켜보기"
autoNoteWatchDescription: "리액션하거나 답글을 남긴 다른 유저의 노트에 대한 알림을 받습니다."
reduceUiAnimation: "UI의 애니메이션을 줄이기"
share: "공유"
notFound: "찾을 수 없습니다"
@ -369,6 +368,36 @@ invites: "초대"
groupName: "그룹명"
members: "멤버"
transfer: "양도"
messagingWithUser: "유저와 대화하기"
messagingWithGroup: "그룹끼리 대화하기"
enable: "사용"
next: "다음"
retype: "다시 입력"
noteOf: "{user}의 노트"
inviteToGroup: "그룹에 초대하기"
_tutorial:
title: "Misskey의 사용 방법"
step1_1: "환영합니다!"
step1_2: "이 페이지는 \"타임라인\"이라고 불립니다. 당신이 \"팔로우\"하고 있는 사람들의 \"노트\"가 시간순으로 나타납니다."
step1_3: "아직 아무 유저도 팔로우하고 있지 않기에 타임라인은 비어 있을 것입니다."
step2_1: "새 노트를 작성하거나 다른 사람을 팔로우하기 전에, 먼저 프로필을 완성해보도록 합시다."
step2_2: "당신이 어떤 사람인지를 알린다면, 다른 사람들이 당신을 팔로우할 확률이 올라갈 것입니다."
step3_1: "프로필 설정은 잘 끝내셨나요?"
step3_2: "그럼 시험삼아 노트를 작성해 보세요. 화면에 있는 연필 버튼을 누르면 작성 폼이 열립니다."
step3_3: "내용을 작성한 후, 폼 오른쪽 상단의 버튼을 눌러 노트를 올릴 수 있습니다."
step3_4: "쓸 말이 없나요? \"Misskey 시작했어요!\" 같은 건 어떨까요? :>"
step4_1: "노트 작성을 끝내셨나요?"
step4_2: "당신의 노트가 타임라인에 표시되어 있다면 성공입니다."
step5_1: "이제, 다른 사람을 팔로우하여 타임라인을 활기차게 만들어보도록 합시다."
step5_2: "{featured}에서 이 인스턴스의 인기 노트를 보실 수 있습니다. {explore}에서는 인기 사용자를 찾을 수 있구요. 마음에 드는 사람을 골라 팔로우해 보세요!"
step5_3: "다른 유저를 팔로우하려면 해당 유저의 아이콘을 클릭하여 프로필 페이지를 띄운 후, 팔로우 버튼을 눌러 주세요."
step5_4: "사용자에 따라 팔로우가 승인될 때까지 시간이 걸릴 수 있습니다."
step6_1: "타임라인에 다른 사용자의 노트가 나타난다면 성공입니다."
step6_2: "다른 유저의 노트에 \"리액션\"을 붙여 간단하게 당신의 반응을 전달할 수도 있습니다."
step6_3: "리액션을 붙이려면, 노트의 \"+\" 버튼을 클릭하고 원하는 이모지를 선택합니다."
step7_1: "이것으로 Misskey의 기본 튜토리얼을 마치겠습니다. 수고하셨습니다!"
step7_2: "Misskey에 대해 더 알고 싶으시다면 {help}를 참고해 주세요."
step7_3: "그럼 Misskey를 즐기세요! 🚀"
_2fa:
alreadyRegistered: "이미 설정이 완료되었습니다."
registerDevice: "디바이스 등록"
@ -379,32 +408,32 @@ _2fa:
step4: "다음 로그인부터는 토큰을 입력해야 합니다."
securityKeyInfo: "FIDO2를 지원하는 하드웨어 시큐리티 키 혹은 휴대전화의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
_permissions:
"read:account": "계정 정보 보기"
"write:account": "계정 정보 변경"
"read:blocks": "차단 보기"
"write:blocks": "차단 수정"
"read:drive": "드라이브 보기"
"write:drive": "드라이브 수정"
"read:favorites": "즐겨찾기 보기"
"write:favorites": "즐겨찾기 수정"
"read:following": "팔로우 정보 보기"
"write:following": "팔로잉 및 팔로우 수정"
"read:messaging": "대화 보기"
"write:messaging": "대화 수정"
"read:mutes": "뮤트 보기"
"write:mutes": "뮤트 수정"
"write:notes": "노트를 작성하거나 삭제"
"read:notifications": "알림 보기"
"write:notifications": "알림 수정"
"read:reactions": "리액션 보기"
"write:reactions": "리액션 수정"
"write:votes": "투표하기"
"read:pages": "페이지 보기"
"write:pages": "페이지 수정"
"read:page-likes": "페이지의 좋아요 보기"
"write:page-likes": "페이지의 좋아요 수정"
"read:user-groups": "유저 그룹 조회"
"write:user-groups": "유저 그룹 변경"
"read:account": "계정 정보를 봅니다"
"write:account": "계정 정보 변경합니다"
"read:blocks": "차단 여부를 확인합니다"
"write:blocks": "차단을 하거나 해제합니다"
"read:drive": "드라이브를 조회합니다"
"write:drive": "드라이브에 파일을 올리거나, 이름을 변경하거나, 삭제합니다"
"read:favorites": "즐겨찾기를 조회합니다"
"write:favorites": "즐겨찾기에 추가하거나 삭제합니다"
"read:following": "팔로우 상태를 봅니다"
"write:following": "팔로우하거나 팔로우를 해제합니다"
"read:messaging": "대화를 읽습니다"
"write:messaging": "대화를 시작하거나 메시지를 보냅니다"
"read:mutes": "뮤트 여부를 확인합니다"
"write:mutes": "뮤트를 하거나 해제합니다"
"write:notes": "노트를 작성하거나 삭제합니다"
"read:notifications": "알림을 확인합니다"
"write:notifications": "알림을 모두 읽음 처리합니다"
"read:reactions": "리액션을 확인합니다"
"write:reactions": "리액션을 추가하거나 취소합니다"
"write:votes": "투표를 합니다"
"read:pages": "페이지를 봅니다"
"write:pages": "페이지 수정합니다"
"read:page-likes": "페이지의 좋아요를 확인합니다"
"write:page-likes": "페이지의 좋아요를 추가하거나 삭제합니다"
"read:user-groups": "유저 그룹 조회합니다"
"write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다"
_auth:
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
permissionAsk: "이 앱은 다음의 권한을 요청합니다"
@ -466,6 +495,7 @@ _visibility:
followersDescription: "팔로워에게만 공개"
specified: "다이렉트"
specifiedDescription: "지정한 유저에게만 공개"
localOnly: "로컬에만"
_postForm:
replyPlaceholder: "이 노트에 답글..."
quotePlaceholder: "이 노트를 인용..."

View File

@ -15,7 +15,7 @@ _time:
minute: "分"
hour: "小时"
day: "日"
introMisskey: "欢迎Misskey是一个开源的分散型SNS服务。\n通过「Note」来分享现在发生的事情吧!📡\n「反应」工能也可以让你快速的对大家的「Note」来表达感情👍\n一起来探索新的世界吧🚀"
introMisskey: "欢迎Misskey是一个开源的分散型SNS服务。\n通过「帖子」来分享现在发生的事情吧!📡\n「反应」功能,可以让你快速的对大家的「帖子」来表达感情👍\n一起来探索新的世界吧🚀"
monthAndDay: "{month}月 {day}日"
search: "搜索"
notifications: "通知"
@ -27,6 +27,7 @@ gotIt: "我明白了"
cancel: "取消"
enterUsername: "输入用户名"
renotedBy: "由 {user} 转推"
noNotes: "没有投稿"
noNotifications: "无通知"
instance: "实例"
settings: "设置"
@ -54,6 +55,11 @@ sendMessage: "发送"
copyUsername: "复制用户名"
reply: "回复"
loadMore: "查看更多"
youGotNewFollower: "你有新的关注者"
receiveFollowRequest: "收到关注请求"
followRequestAccepted: "同意关注请求"
mentions: "提及"
directNotes: "指定用户可见"
importAndExport: "导入和导出"
import: "导入"
export: "导出"
@ -169,11 +175,8 @@ remove: "删除"
removed: "已删除"
removeAreYouSure: "要删掉「{x}」吗?"
saved: "已保存"
messaging: "聊天"
upload: "上传"
fromUrl: "从 URL"
editWidgets: "编辑部件"
exitEdit: "停止编辑"
explore: "发现"
games: "Misskey游戏"
messageRead: "已读"
@ -287,7 +290,6 @@ uploadFolder: "默认上传文件夹"
cacheClear: "清空缓存"
markAsReadAllNotifications: "将所有通知标为已读"
markAsReadAllUnreadNotes: "将所有帖子标记为已读"
markAsReadAllTalkMessages: "将所有对话标为已读"
help: "帮助"
inputMessageHere: "在此键入信息"
close: "关闭"
@ -315,8 +317,6 @@ _permissions:
"write:favorites": "编辑收藏夹"
"read:following": "查看关注信息"
"write:following": "关注/取消关注"
"read:messaging": "查看对话"
"write:messaging": "对话操作"
"read:mutes": "查看屏蔽列表"
"write:mutes": "编辑屏蔽列表"
"read:notifications": "查看通知"

View File

@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.5.0",
"version": "12.6.0",
"codename": "indigo",
"repository": {
"type": "git",
@ -36,6 +36,7 @@
"@fortawesome/free-regular-svg-icons": "5.12.0",
"@fortawesome/free-solid-svg-icons": "5.12.0",
"@fortawesome/vue-fontawesome": "0.1.9",
"@juggle/resize-observer": "3.0.2",
"@koa/cors": "3.0.0",
"@koa/multer": "2.0.2",
"@koa/router": "8.0.6",

View File

@ -18,8 +18,12 @@
</transition>
</div>
<div class="sub">
<fa :icon="faSearch"/>
<input type="search" class="search" :placeholder="$t('search')" v-model="searchQuery" v-autocomplete="{ model: 'searchQuery' }" :disabled="searchWait" @keypress="searchKeypress"/>
<button v-if="widgetsEditMode" class="_button edit active" @click="widgetsEditMode = false"><fa :icon="faGripVertical"/></button>
<button v-else class="_button edit" @click="widgetsEditMode = true"><fa :icon="faGripVertical"/></button>
<div class="search">
<fa :icon="faSearch"/>
<input type="search" :placeholder="$t('search')" v-model="searchQuery" v-autocomplete="{ model: 'searchQuery' }" :disabled="searchWait" @keypress="searchKeypress"/>
</div>
<button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
</div>
</header>
@ -86,7 +90,7 @@
</nav>
</transition>
<div class="contents">
<div class="contents" ref="contents">
<main ref="main">
<div class="content">
<transition :name="$store.state.device.animation ? 'page' : ''" mode="out-in" @enter="onTransition">
@ -126,8 +130,6 @@
<template v-else>
<component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget"/>
</template>
<button ref="widgetsEditButton" v-if="widgetsEditMode" class="_button edit" @click="widgetsEditMode = false">{{ $t('exitEdit') }}</button>
<button ref="widgetsEditButton" v-else class="_button edit" @click="widgetsEditMode = true">{{ $t('editWidgets') }}</button>
</template>
</div>
</div>
@ -150,8 +152,9 @@
<script lang="ts">
import Vue from 'vue';
import { faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { ResizeObserver } from '@juggle/resize-observer';
import { v4 as uuid } from 'uuid';
import i18n from './i18n';
import { host } from './config';
@ -184,7 +187,7 @@ export default Vue.extend({
enableWidgets: window.innerWidth >= 1100,
canBack: false,
disconnectedDialog: null as Promise<void> | null,
faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer
};
},
@ -199,7 +202,7 @@ export default Vue.extend({
},
widgets(): any[] {
return this.$store.state.settings.widgets;
return this.$store.state.deviceUser.widgets;
}
},
@ -230,9 +233,15 @@ export default Vue.extend({
this.connection.on('notification', this.onNotification);
if (this.widgets.length === 0) {
this.$store.dispatch('settings/setWidgets', [{
name: 'notifications',
this.$store.commit('deviceUser/setWidgets', [{
name: 'calendar',
id: 'a', data: {}
}, {
name: 'notifications',
id: 'b', data: {}
}, {
name: 'trends',
id: 'c', data: {}
}]);
}
}
@ -256,21 +265,34 @@ export default Vue.extend({
});
}
});
},
setInterval(() => {
if (this.showNav) return; // TODO: トランジション中も false になるので、これだけでは不十分
this.$refs.title.style.left = (this.$refs.main.getBoundingClientRect().left - this.$refs.nav.offsetWidth) + 'px';
}, 1000);
mounted() {
// https://stackoverflow.com/questions/33891709/when-flexbox-items-wrap-in-column-mode-container-does-not-grow-its-width
if (this.enableWidgets) {
setInterval(() => {
if (!this.$refs.widgetsEditButton) return;
const adjustWidgetsWidth = () => {
const lastChild = this.$refs.widgets.children[this.$refs.widgets.children.length - 1];
if (lastChild == null) return;
const width = this.$refs.widgetsEditButton.offsetLeft + 300;
const width = lastChild.offsetLeft + 300;
this.$refs.widgets.style.width = width + 'px';
}, 1000);
};
setInterval(adjustWidgetsWidth, 1000);
}
const adjustTitlePosition = () => {
this.$refs.title.style.left = (this.$refs.main.getBoundingClientRect().left - this.$refs.nav.offsetWidth) + 'px';
};
adjustTitlePosition();
const ro = new ResizeObserver((entries, observer) => {
adjustTitlePosition();
});
ro.observe(this.$refs.contents);
window.addEventListener('resize', adjustTitlePosition);
},
methods: {
@ -482,8 +504,9 @@ export default Vue.extend({
this.$store.dispatch('switchAccount', {
...i,
token: token
}).then(() => {
location.reload();
});
location.reload();
});
},
@ -530,7 +553,7 @@ export default Vue.extend({
items: widgets.map(widget => ({
text: this.$t('_widgets.' + widget),
action: () => {
this.$store.dispatch('settings/addWidget', {
this.$store.commit('deviceUser/addWidget', {
name: widget,
id: uuid(),
data: {}
@ -542,11 +565,11 @@ export default Vue.extend({
},
removeWidget(widget) {
this.$store.dispatch('settings/removeWidget', widget);
this.$store.commit('deviceUser/removeWidget', widget);
},
saveHome() {
this.$store.dispatch('settings/setWidgets', this.widgets);
this.$store.commit('deviceUser/setWidgets', this.widgets);
}
}
});
@ -709,30 +732,42 @@ export default Vue.extend({
display: none;
}
> [data-icon] {
position: absolute;
top: 0;
left: 16px;
height: $header-height;
pointer-events: none;
font-size: 16px;
> .edit {
padding: 16px;
&.active {
color: var(--accent);
}
}
> .search {
$margin: 8px;
width: calc(100% - #{$post-button-size + $post-button-margin + $margin});
box-sizing: border-box;
margin-right: $margin;
padding: 0 12px 0 42px;
font-size: 1rem;
line-height: 38px;
border: none;
border-radius: 38px;
color: var(--fg);
background: var(--bg);
position: relative;
&:focus {
outline: none;
> input {
$margin: 8px;
width: 200px;
box-sizing: border-box;
margin-right: $margin;
padding: 0 12px 0 42px;
font-size: 1rem;
line-height: 38px;
border: none;
border-radius: 38px;
color: var(--fg);
background: var(--bg);
&:focus {
outline: none;
}
}
> [data-icon] {
position: absolute;
top: 0;
left: 16px;
height: 100%;
pointer-events: none;
font-size: 16px;
}
}
@ -971,12 +1006,6 @@ export default Vue.extend({
margin: 0 auto;
}
> .edit {
display: block;
font-size: 0.9em;
margin: 0 auto;
}
.customize-container {
margin: 8px 0;
background: #fff;

View File

@ -0,0 +1,94 @@
<template>
<component :is="hasRoute ? 'router-link' : 'a'" class="xlcxczvw _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
:title="url"
>
<slot></slot>
<fa :icon="faExternalLinkSquareAlt" v-if="target === '_blank'" class="icon"/>
</component>
</template>
<script lang="ts">
import Vue from 'vue';
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { url as local } from '../config';
import XUrlPreview from './url-preview-popup.vue';
export default Vue.extend({
props: {
url: {
type: String,
required: true,
},
rel: {
type: String,
required: false,
}
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute = isSelf && (
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/'));
return {
local,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
showTimer: null,
hideTimer: null,
preview: null,
faExternalLinkSquareAlt
};
},
methods: {
showPreview() {
if (!document.body.contains(this.$el)) return;
if (this.preview) return;
this.preview = new XUrlPreview({
parent: this,
propsData: {
url: this.url,
source: this.$el
}
}).$mount();
document.body.appendChild(this.preview.$el);
},
closePreview() {
if (this.preview) {
this.preview.destroyDom();
this.preview = null;
}
},
onMouseover() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
}
}
});
</script>
<style lang="scss" scoped>
.xlcxczvw {
word-break: break-all;
> .icon {
padding-left: 2px;
font-size: .9em;
font-weight: 400;
font-style: normal;
}
}
</style>

View File

@ -2,6 +2,7 @@ import Vue, { VNode } from 'vue';
import { MfmForest } from '../../mfm/types';
import { parse, parsePlain } from '../../mfm/parse';
import MkUrl from './url.vue';
import MkLink from './link.vue';
import MkMention from './mention.vue';
import { concat } from '../../prelude/array';
import MkFormula from './formula.vue';
@ -158,14 +159,12 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'link': {
return [createElement('a', {
attrs: {
class: 'link _link',
href: token.node.props.url,
return [createElement(MkLink, {
key: Math.random(),
props: {
url: token.node.props.url,
rel: 'nofollow noopener',
target: '_blank',
title: token.node.props.url,
}
},
}, genEl(token.children))];
}

View File

@ -179,7 +179,7 @@ export default Vue.extend({
watch: {
localOnly() {
this.$store.commit('device/setLocalOnly', this.localOnly);
this.$store.commit('deviceUser/setLocalOnly', this.localOnly);
}
},
@ -215,9 +215,9 @@ export default Vue.extend({
}
// デフォルト公開範囲
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? this.$store.state.device.visibility : this.$store.state.settings.defaultNoteVisibility);
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : this.$store.state.settings.defaultNoteVisibility);
this.localOnly = this.$store.state.settings.rememberNoteVisibility ? this.$store.state.device.localOnly : this.$store.state.settings.defaultNoteLocalOnly;
this.localOnly = this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.localOnly : this.$store.state.settings.defaultNoteLocalOnly;
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {

View File

@ -0,0 +1,56 @@
<template>
<div class="fgmtyycl _panel" :style="{ top: top + 'px', left: left + 'px' }">
<x-url-preview :url="url"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XUrlPreview from './url-preview.vue';
export default Vue.extend({
i18n,
components: {
XUrlPreview
},
props: {
url: {
type: String,
required: true
},
source: {
required: true
}
},
data() {
return {
u: null,
top: 0,
left: 0,
};
},
mounted() {
const rect = this.source.getBoundingClientRect();
const x = ((rect.left + (this.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
const y = rect.top + this.source.offsetHeight + window.pageYOffset;
this.top = y;
this.left = x;
},
});
</script>
<style lang="scss" scoped>
.fgmtyycl {
position: absolute;
z-index: 11000;
width: 500px;
overflow: hidden;
pointer-events: none;
}
</style>

View File

@ -1,5 +1,8 @@
<template>
<component :is="hasRoute ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target">
<component :is="hasRoute ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
>
<template v-if="!self">
<span class="schema">{{ schema }}//</span>
<span class="hostname">{{ hostname }}</span>
@ -20,9 +23,19 @@ import Vue from 'vue';
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { toUnicode as decodePunycode } from 'punycode';
import { url as local } from '../config';
import XUrlPreview from './url-preview-popup.vue';
export default Vue.extend({
props: ['url', 'rel'],
props: {
url: {
type: String,
required: true,
},
rel: {
type: String,
required: false,
}
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute = isSelf && (
@ -32,16 +45,19 @@ export default Vue.extend({
this.url.substr(local.length).startsWith('/tags/'));
return {
local,
schema: null,
hostname: null,
port: null,
pathname: null,
query: null,
hash: null,
schema: null as string | null,
hostname: null as string | null,
port: null as string | null,
pathname: null as string | null,
query: null as string | null,
hash: null as string | null,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
showTimer: null,
hideTimer: null,
preview: null,
faExternalLinkSquareAlt
};
},
@ -53,6 +69,38 @@ export default Vue.extend({
this.pathname = decodeURIComponent(url.pathname);
this.query = decodeURIComponent(url.search);
this.hash = decodeURIComponent(url.hash);
},
methods: {
showPreview() {
if (!document.body.contains(this.$el)) return;
if (this.preview) return;
this.preview = new XUrlPreview({
parent: this,
propsData: {
url: this.url,
source: this.$el
}
}).$mount();
document.body.appendChild(this.preview.$el);
},
closePreview() {
if (this.preview) {
this.preview.destroyDom();
this.preview = null;
}
},
onMouseover() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
}
}
});
</script>

View File

@ -4,7 +4,7 @@
<script lang="ts">
import Vue from 'vue';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments } from '@fortawesome/free-solid-svg-icons';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import XMenu from './menu.vue';
@ -43,7 +43,11 @@ export default Vue.extend({
icon: faListUl,
text: this.$t('addToList'),
action: this.pushList
}] as any;
}, this.$store.state.i.id != this.user.id ? {
icon: faUsers,
text: this.$t('inviteToGroup'),
action: this.inviteGroup
} : undefined] as any;
if (this.$store.getters.isSignedIn && this.$store.state.i.id != this.user.id) {
menu = menu.concat([null, {
@ -118,6 +122,42 @@ export default Vue.extend({
});
},
async inviteGroup() {
const groups = await this.$root.api('users/groups/owned');
if (groups.length === 0) {
this.$root.dialog({
type: 'error',
text: this.$t('youHaveNoGroups')
});
return;
}
const { canceled, result: groupId } = await this.$root.dialog({
type: null,
title: this.$t('group'),
select: {
items: groups.map(group => ({
value: group.id, text: group.name
}))
},
showCancelButton: true
});
if (canceled) return;
this.$root.api('users/groups/invite', {
groupId: groupId,
userId: this.user.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
},
async toggleMute() {
this.$root.api(this.user.isMuted ? 'mute/delete' : 'mute/create', {
userId: this.user.id

View File

@ -56,14 +56,14 @@ export default Vue.extend({
},
data() {
return {
v: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.device.visibility : (this.currentVisibility || this.$store.state.settings.defaultNoteVisibility),
v: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : (this.currentVisibility || this.$store.state.settings.defaultNoteVisibility),
faGlobe, faUnlock, faEnvelope, faHome
}
},
methods: {
choose(visibility) {
if (this.$store.state.settings.rememberNoteVisibility) {
this.$store.commit('device/setVisibility', visibility);
this.$store.commit('deviceUser/setVisibility', visibility);
}
this.$emit('chosen', visibility);
this.destroyDom();

View File

@ -1,3 +1,5 @@
import { ResizeObserver } from '@juggle/resize-observer';
export default {
inserted(el, binding, vn) {
const query = binding.value;
@ -52,13 +54,16 @@ export default {
calc();
el._sizeResizeCb_ = calc;
const ro = new ResizeObserver((entries, observer) => {
calc();
});
ro.observe(el);
window.addEventListener('resize', calc);
vn.context.$on('hook:activated', calc);
el._ro_ = ro;
},
unbind(el, binding, vn) {
window.removeEventListener('resize', el._sizeResizeCb_);
el._ro_.unobserve(el);
}
};

View File

@ -91,13 +91,12 @@ export default Vue.extend({
this.enableLocalTimeline = !meta.disableLocalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin
) && ['local', 'social'].includes(this.src)) this.src = 'home';
});
if (this.$store.state.device.tl) {
this.src = this.$store.state.device.tl.src;
if (this.src === 'list') {
this.list = this.$store.state.device.tl.arg;
} else if (this.src === 'antenna') {
this.antenna = this.$store.state.device.tl.arg;
}
this.src = this.$store.state.deviceUser.tl.src;
if (this.src === 'list') {
this.list = this.$store.state.deviceUser.tl.arg;
} else if (this.src === 'antenna') {
this.antenna = this.$store.state.deviceUser.tl.arg;
}
},
@ -164,7 +163,7 @@ export default Vue.extend({
},
saveSrc() {
this.$store.commit('device/setTl', {
this.$store.commit('deviceUser/setTl', {
src: this.src,
arg: this.src == 'list' ? this.list : this.antenna
});

View File

@ -1,5 +1,8 @@
<template>
<div class="mk-note-page">
<portal to="avatar" v-if="note"><mk-avatar class="avatar" :user="note.user" :disable-preview="true"/></portal>
<portal to="title" v-if="note">{{ $t('noteOf', { user: note.user.name }) }}</portal>
<transition name="zoom" mode="out-in">
<x-note v-if="note" :note="note" :key="note.id" :detail="true"/>
<div v-else-if="error">

View File

@ -16,7 +16,15 @@ const defaultSettings = {
wallpaper: null,
memo: null,
reactions: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'],
widgets: []
};
const defaultDeviceUserSettings = {
visibility: 'public',
localOnly: false,
widgets: [],
tl: {
src: 'home'
},
};
const defaultDeviceSettings = {
@ -27,16 +35,19 @@ const defaultDeviceSettings = {
autoReload: false,
accounts: [],
recentEmojis: [],
visibility: 'public',
localOnly: false,
themes: [],
theme: 'light',
animation: true,
userData: {},
};
function copy(data) {
return JSON.parse(JSON.stringify(data));
}
export default (os: MiOS) => new Vuex.Store({
plugins: [createPersistedState({
paths: ['i', 'device', 'settings']
paths: ['i', 'device', 'deviceUser', 'settings']
})],
state: {
@ -58,10 +69,11 @@ export default (os: MiOS) => new Vuex.Store({
},
actions: {
login(ctx, i) {
async login(ctx, i) {
ctx.commit('updateI', i);
ctx.dispatch('settings/merge', i.clientData);
ctx.dispatch('addAcount', { id: i.id, i: localStorage.getItem('i') });
ctx.commit('settings/init', i.clientData);
ctx.commit('deviceUser/init', ctx.state.device.userData[i.id] || {});
await ctx.dispatch('addAcount', { id: i.id, i: localStorage.getItem('i') });
},
addAcount(ctx, info) {
@ -74,14 +86,17 @@ export default (os: MiOS) => new Vuex.Store({
},
logout(ctx) {
ctx.commit('device/setUserData', { userId: ctx.state.i.id, data: ctx.state.deviceUser });
ctx.commit('updateI', null);
ctx.commit('settings/init', {});
ctx.commit('deviceUser/init', {});
localStorage.removeItem('i');
},
switchAccount(ctx, i) {
ctx.commit('updateI', i);
ctx.commit('settings/init', i.clientData);
async switchAccount(ctx, i) {
ctx.commit('device/setUserData', { userId: ctx.state.i.id, data: ctx.state.deviceUser });
localStorage.setItem('i', i.token);
await ctx.dispatch('login', i);
},
mergeMe(ctx, me) {
@ -90,7 +105,7 @@ export default (os: MiOS) => new Vuex.Store({
}
if (me.clientData) {
ctx.dispatch('settings/merge', me.clientData);
ctx.commit('settings/init', me.clientData);
}
},
},
@ -106,6 +121,32 @@ export default (os: MiOS) => new Vuex.Store({
state[x.key] = x.value;
},
setUserData(state, x: { userId: string; data: any }) {
state.userData[x.userId] = copy(x.data);
},
}
},
deviceUser: {
namespaced: true,
state: defaultDeviceUserSettings,
mutations: {
init(state, x) {
for (const [key, value] of Object.entries(defaultDeviceUserSettings)) {
if (x[key]) {
state[key] = x[key];
} else {
state[key] = value;
}
}
},
set(state, x: { key: string; value: any }) {
state[x.key] = x.value;
},
setTl(state, x) {
state.tl = {
src: x.src,
@ -120,6 +161,25 @@ export default (os: MiOS) => new Vuex.Store({
setLocalOnly(state, localOnly) {
state.localOnly = localOnly;
},
setWidgets(state, widgets) {
state.widgets = widgets;
},
addWidget(state, widget) {
state.widgets.unshift(widget);
},
removeWidget(state, widget) {
state.widgets = state.widgets.filter(w => w.id != widget.id);
},
updateWidget(state, x) {
const w = state.widgets.find(w => w.id == x.id);
if (w) {
w.data = x.data;
}
},
}
},
@ -145,13 +205,6 @@ export default (os: MiOS) => new Vuex.Store({
},
actions: {
merge(ctx, settings) {
if (settings == null) return;
for (const [key, value] of Object.entries(settings)) {
ctx.commit('set', { key, value });
}
},
set(ctx, x) {
ctx.commit('set', x);
@ -162,41 +215,6 @@ export default (os: MiOS) => new Vuex.Store({
});
}
},
setWidgets(ctx, widgets) {
ctx.state.widgets = widgets;
ctx.dispatch('updateWidgets');
},
addWidget(ctx, widget) {
ctx.state.widgets.unshift(widget);
ctx.dispatch('updateWidgets');
},
removeWidget(ctx, widget) {
ctx.state.widgets = ctx.state.widgets.filter(w => w.id != widget.id);
ctx.dispatch('updateWidgets');
},
updateWidget(ctx, x) {
const w = ctx.state.widgets.find(w => w.id == x.id);
if (w) {
w.data = x.data;
ctx.dispatch('updateWidgets');
}
},
updateWidgets(ctx) {
const widgets = ctx.state.widgets;
ctx.commit('set', {
key: 'widgets',
value: widgets
});
os.api('i/update-client-setting', {
name: 'widgets',
value: widgets
});
},
}
}
}

View File

@ -51,7 +51,7 @@ export default function <T extends object>(data: {
},
save() {
this.$store.dispatch('settings/updateWidget', this.widget);
this.$store.commit('deviceUser/updateWidget', this.widget);
}
}
});

View File

@ -3,6 +3,8 @@ import { Note } from '../../models/entities/note';
import { User } from '../../models/entities/user';
import { NoteUnreads, Antennas, AntennaNotes, Users } from '../../models';
// TODO: 状態が変化していない場合は各種イベントを送信しない
/**
* Mark a note as read
*/

View File

@ -102,6 +102,11 @@
normalize-path "^2.0.1"
through2 "^2.0.3"
"@juggle/resize-observer@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.0.2.tgz#f4f326806826c0da5221411374e4c54902c327bb"
integrity sha512-9fEfZIxZ1qTahMSX50pigfSJ/1N6X2oABhmgd4Dt2SsPkZi0lTlvoHu5V2yrGZ6m+oALfS4tJrpx9nbVIxwOYQ==
"@koa/cors@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.0.0.tgz#df021b4df2dadf1e2b04d7c8ddf93ba2d42519cb"