diff --git a/locales/fr.yml b/locales/fr.yml index fd8cbc8498..5f57fadeb4 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -88,7 +88,7 @@ common: i-like-sushi: "Je préfère les sushis (au pudding)" show-reversi-board-labels: "Afficher les étiquettes des lignes et colonnes dans Reversi" verified-user: "Utilisateur·trice vérifié·e" - disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" + disable-animated-mfm: "Désactiver les textes animés dans les publications" reversi: drawn: "Partie nulle" my-turn: "C’est votre tour" @@ -99,7 +99,7 @@ common: black: "Noirs" white: "Blancs" total: "Total" - this-turn: "Tour de {}" + this-turn: "Tour {}" widgets: analog-clock: "Horloge analogique" profile: "Profil" @@ -117,7 +117,7 @@ common: notifications: "Notifications" users: "Utilisateurs" polls: "Sondages" - post-form: "投稿フォーム" + post-form: "Formulaire de publication" messaging: "Messagerie" server: "Info sur le serveur" donation: "Dons" @@ -170,8 +170,8 @@ common/views/components/games/reversi/reversi.vue: waiting-for: "En attente de {}" cancel: "Annuler" common/views/components/games/reversi/reversi.game.vue: - surrender: "投了" - surrendered: "投了により" + surrender: "Se rendre" + surrendered: "Par abandon" common/views/components/games/reversi/reversi.index.vue: title: "Misskey Reversi" sub-title: "Jouer à Reversi avec vos amis·es !" @@ -201,10 +201,10 @@ common/views/components/games/reversi/reversi.room.vue: this-game-is-started-soon: "La partie commencera dans quelques instants" waiting-for-other: "En attente que l'adversaire soit prêt" waiting-for-me: "En attente que vous soyez prêt" - waiting-for-both: "準備中" + waiting-for-both: "En attente que vous soyez prêt" cancel: "Annuler" ready: "Prêt" - cancel-ready: "準備続行" + cancel-ready: "Annuler \"Je suis prêt\"" common/views/components/connect-failed.vue: title: "Impossible de se connecter au server." description: "Il y a soit un problème avec votre connexion internet, soit le serveur est hors-ligne ou en maintenance. Veuillez {ressayer} plus tard." @@ -721,7 +721,7 @@ desktop/views/components/timeline.vue: global: "Global" list: "Listes" desktop/views/components/ui.header.vue: - welcome-back: "Vous êtes de retour !" + welcome-back: "Content de vous revoir !" desktop/views/components/ui.header.account.vue: profile: "Votre profil" drive: "Drive" @@ -990,7 +990,7 @@ mobile/views/pages/drive.vue: drive: "Drive" more: "Afficher plus ..." mobile/views/pages/signup.vue: - lets-start: "📦 始めましょう" + lets-start: "Commençons ! 📦" mobile/views/pages/followers.vue: followers-of: "Abonné·e·s de {}" mobile/views/pages/following.vue: @@ -1059,9 +1059,9 @@ mobile/views/pages/settings.vue: i-am-under-limited-internet: "J'ai un accès Internet limité" circle-icons: "Utiliser des icônes circulaires" timeline: "Fil d'actualité" - show-reply-target: "リプライ先を表示する" + show-reply-target: "Afficher les réponses" show-my-renotes: "Afficher mes republications" - show-renoted-my-notes: "Renoteされた自分の投稿を表示する" + show-renoted-my-notes: "Afficher les notes que j'ai repartagé" post-style: "Style de la publication" post-style-standard: "Standard" post-style-smart: "Intelligent" diff --git a/locales/ko.yml b/locales/ko.yml index bc214b7943..83abddd378 100644 --- a/locales/ko.yml +++ b/locales/ko.yml @@ -5,125 +5,125 @@ meta: common: misskey: "A ⭐ of fediverse" about-title: "A ⭐ of fediverse." - about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。" + about: "Misskey를 찾아 주셔서 감사합니다. Misskey은 지구에서 태어난 <b>분산 마이크로 블로그 SNS </b> 입니다. Fediverse (다양한 SNS로 구성되는 우주)에 존재하는 다른 SNS와 상호 연결되어 있습니다. 잠시 도시의 번잡함에서 벗어나 새로운 인터넷에 다이브 해 보지 않겠습니까." adblock: - detected: "広告ブロッカーを無効にしてください" - warning: "<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。" - application-authorization: "アプリの連携" - close: "閉じる" + detected: "광고 차단기를 해제하십시오" + warning: "<strong>Misskey는 광고를 게재하지 않습니다</strong> 그러나 광고를 차단하는 기능 기능을 사용할 경우 일부 기능을 사용할 수 없게 될 가능성이나 결함이 발생하는 경우가 있습니다." + application-authorization: "앱의 연계" + close: "닫기" customization-tips: - title: "カスタマイズのヒント" - paragraph1: "ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。" - paragraph2: "一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。" - paragraph3: "ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。" - paragraph4: "カスタマイズを終了するには、右上の「完了」をクリックします。" + title: "사용자 정의 팁" + paragraph1: "홈 정의는 위젯을 추가 / 삭제하거나 드래그 앤 드롭하여 정렬 할 수 있습니다." + paragraph2: "일부 위젯은 <strong>오른쪽 클릭</strong> 하여 모양을 변경할 수 있습니다." + paragraph3: "위젯을 삭제하려면 헤더 <strong>\"휴지통\"</strong> 라고 쓰여진 영역으로 끌어다 놓습니다." + paragraph4: "사용자 정의를 종료하려면 오른쪽 상단의 '완료' 를 클릭합니다." gotit: "Got it!" notification: - file-uploaded: "ファイルがアップロードされました" - message-from: "{}さんからメッセージ:" - reversi-invited: "対局への招待があります" - reversi-invited-by: "{}さんから" - notified-by: "{}さんから" - reply-from: "{}さんから返信:" - quoted-by: "{}さんが引用:" + file-uploaded: "파일이 업로드되었습니다" + message-from: "{}씨로부터 메시지:" + reversi-invited: "대결에 초대되어 있습니다" + reversi-invited-by: "{}님" + notified-by: "{}님" + reply-from: "{}님으로부터 답글:" + quoted-by: "{}씨가 인용:" name: "Misskey" time: - unknown: "なぞのじかん" - future: "未来" - just_now: "たった今" - seconds_ago: "{}秒前" - minutes_ago: "{}分前" - hours_ago: "{}時間前" - days_ago: "{}日前" - weeks_ago: "{}週間前" - months_ago: "{}ヶ月前" - years_ago: "{}年前" - trash: "ゴミ箱" + unknown: "수수께끼의 시간" + future: "미래" + just_now: "방금" + seconds_ago: "{}초전" + minutes_ago: "{}분전" + hours_ago: "{}시간전" + days_ago: "{}일전" + weeks_ago: "{}주전" + months_ago: "{}개월전" + years_ago: "{}년전" + trash: "휴지통" date: - full-year: "年" - month: "月" - day: "日" - hours: "時" - minutes: "分" + full-year: "년" + month: "달" + day: "일" + hours: "때" + minutes: "분" weekday-short: - sunday: "日" - monday: "月" - tuesday: "火" - wednesday: "水" - thursday: "木" - friday: "金" - saturday: "土" + sunday: "일" + monday: "월" + tuesday: "화" + wednesday: "수" + thursday: "목" + friday: "금" + saturday: "토" weekday: - sunday: "日曜日" - monday: "月曜日" - tuesday: "火曜日" - wednesday: "水曜日" - thursday: "木曜日" - friday: "金曜日" - saturday: "土曜日" + sunday: "일요일" + monday: "월요일" + tuesday: "화요일" + wednesday: "수요일" + thursday: "목요일" + friday: "금요일" + saturday: "토요일" reactions: - like: "いいね" - love: "しゅき" - laugh: "笑" - hmm: "ふぅ~む" - surprise: "わお" - congrats: "おめでとう" - angry: "おこ" - confused: "こまこまのこまり" + like: "좋네" + love: "좋아" + laugh: "크크" + hmm: "음..." + surprise: "와우" + congrats: "받으세요" + angry: "화냈어" + confused: "곤란하고 있어" pudding: "Pudding" note-placeholders: - a: "今どうしてる?" - b: "何かありましたか?" - c: "何をお考えですか?" - d: "言いたいことは?" - e: "ここに書いてください" - f: "あなたが書くのを待っています..." - search: "検索" - delete: "削除" - loading: "読み込み中" - ok: "わかった" - update-available-title: "更新があります" - update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。" - my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。" - i-like-sushi: "私は(プリンよりむしろ)寿司が好き" - show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示" - verified-user: "認証済みのユーザー" - disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" + a: "지금 어떻게하고있어?" + b: "뭔가 있었습니까?" + c: "무엇을 생각하십니까?" + d: "말하고 싶은 것은?" + e: "여기에 써주십시오" + f: "당신이 쓸 것을 당신 기다리고 있습니다..." + search: "검색" + delete: "삭제" + loading: "로드 중" + ok: "확인" + update-available-title: "갱신이 있습니다" + update-available: "Misskey의 새로운 버전이 있습니다 ({newer} 현재 {current}을 사용 중). 페이지를 다시로드하면 업데이트가 적용됩니다." + my-token-regenerated: "당신의 토큰이 업데이트되어 있기 때문에 로그 아웃합니다." + i-like-sushi: "나는(푸딩보다 오히려)스시가 좋아" + show-reversi-board-labels: "리버시 보드의 행과 열 레이블을 표시" + verified-user: "인증 된 사용자" + disable-animated-mfm: "게시물의 문자 애니메이션을 비활성화 할" reversi: - drawn: "引き分け" - my-turn: "あなたのターンです" - opponent-turn: "相手のターンです" - turn-of: "{}のターンです" - past-turn-of: "{}のターン" - won: "{}の勝ち" - black: "黒" - white: "白" - total: "合計" - this-turn: "{}ターン目" + drawn: "무승부" + my-turn: "당신의 차례입니다" + opponent-turn: "상대의 차례입니다" + turn-of: "{}의 차례입니다" + past-turn-of: "{} 턴" + won: "{} 승리" + black: "검정" + white: "흰색" + total: "합계" + this-turn: "{}턴눈" widgets: - analog-clock: "アナログ時計" - profile: "プロフィール" - calendar: "カレンダー" - timemachine: "カレンダー(タイムマシン)" - activity: "アクティビティ" - rss: "RSSリーダー" - memo: "付箋" - trends: "トレンド" - photo-stream: "フォトストリーム" - posts-monitor: "投稿チャート" - slideshow: "スライドショー" - version: "バージョン" - broadcast: "ブロードキャスト" - notifications: "通知" - users: "おすすめユーザー" - polls: "アンケート" - post-form: "投稿フォーム" - messaging: "メッセージ" - server: "サーバー情報" - donation: "寄付のお願い" - nav: "ナビゲーション" - tips: "ヒント" - hashtags: "ハッシュタグ" + analog-clock: "아날로그 시계" + profile: "프로필" + calendar: "달력" + timemachine: "달력(타임머신)" + activity: "활동" + rss: "RSS 리더" + memo: "끈끈이" + trends: "트렌드" + photo-stream: "포토 스트림" + posts-monitor: "게시물 차트" + slideshow: "슬라이드 쇼" + version: "버전" + broadcast: "브로드 캐스트" + notifications: "통지" + users: "추천 사용자" + polls: "설문" + post-form: "게시 양식" + messaging: "메시지" + server: "서버 정보" + donation: "기부 요청" + nav: "네비게이션" + tips: "팁" + hashtags: "해시 태그" deck: widgets: "ウィジェット" home: "ホーム" diff --git a/locales/pl.yml b/locales/pl.yml index a0728a650b..14deadb990 100644 --- a/locales/pl.yml +++ b/locales/pl.yml @@ -15,8 +15,8 @@ common: title: "Wskazówki o dostosowywaniu" paragraph1: "Dostosowywanie strony głównej pozwala na dodawanie, usuwanie, przeciąganie i zmienianie kolejności widżetów." paragraph2: "一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。" - paragraph3: "ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。" - paragraph4: "カスタマイズを終了するには、右上の「完了」をクリックします。" + paragraph3: "Aby usunąć widżet, <strong>przeciągnij i upuść widżet do części nazwanej „Kosz”</strong> w nagłówku." + paragraph4: "Aby zakończyć dostosowywanie, naciśnij „Zakończ” w w prawym górnym rogu." gotit: "Rozumiem!" notification: file-uploaded: "Wysłano plik!" @@ -88,7 +88,7 @@ common: i-like-sushi: "Wolę sushi od puddingu" show-reversi-board-labels: "Pokazuj podpisy wierszy i kolumn w Reversi" verified-user: "Zweryfikowany użytkownik" - disable-animated-mfm: "投稿内の動きのあるテキストを無効にする" + disable-animated-mfm: "Wyłącz animowany tekst we wpisach" reversi: drawn: "Remis" my-turn: "Twoja kolej" @@ -142,58 +142,58 @@ common: stack-left: "Przypnij do lewej" pop-right: "Odepnij w prawo" auth/views/form.vue: - share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?" - permission-ask: "このアプリは次の権限を要求しています:" - account-read: "アカウントの情報を見る。" - account-write: "アカウントの情報を操作する。" - note-write: "投稿する。" - like-write: "いいねしたりいいね解除する。" - following-write: "フォローしたりフォロー解除する。" - drive-read: "ドライブを見る。" - drive-write: "ドライブを操作する。" - notification-read: "通知を見る。" - notification-write: "通知を操作する。" - cancel: "キャンセル" - accept: "アクセスを許可" + share-access: "Czy chcesz <b>zezwolić</b> <i>{{ app.name }}</i> na dostęp do Twojego konta?" + permission-ask: "Ta aplikacja wymaga następujących uprawnień:" + account-read: "Wyświetlanie informacji o koncie:" + account-write: "Modyfikowanie informacji o koncie:" + note-write: "Publikacja." + like-write: "Reagowanie na wpisy." + following-write: "Śledzenie i cofanie śledzenia." + drive-read: "Odczytywanie Twojego dysku." + drive-write: "Wysyłanie i usuwanie plików na Twoim dysku." + notification-read: "Odczytywanie Twoich powiadomień." + notification-write: "Zarządzanie Twoimi powiadomieniami." + cancel: "Anuluj" + accept: "Przyznaj dostęp." auth/views/index.vue: - loading: "読み込み中" - denied: "アプリケーションの連携をキャンセルしました。" - denied-paragraph: "このアプリがあなたのアカウントにアクセスすることはありません。" - already-authorized: "このアプリは既に連携済みです" + loading: "Ładowanie" + denied: "Odrzucono uwierzytelnianie aplikacji." + denied-paragraph: "Ta aplikacja nie uzyska dostępu do Twojego konta." + already-authorized: "Ta aplikacja została już uwierzytelniona." allowed: "アプリケーションの連携を許可しました" - callback-url: "アプリケーションに戻っています" - please-go-back: "アプリケーションに戻って、やっていってください。" - error: "セッションが存在しません。" - sign-in: "サインインしてください" + callback-url: "Powracam do aplikacji." + please-go-back: "Wróć do aplikacji." + error: "Sesja nie istnieje." + sign-in: "Proszę zalogować się." common/views/components/games/reversi/reversi.vue: matching: - waiting-for: "{}を待っています" - cancel: "キャンセル" + waiting-for: "Oczekiwanie na {}" + cancel: "Anuluj" common/views/components/games/reversi/reversi.game.vue: surrender: "投了" surrendered: "投了により" common/views/components/games/reversi/reversi.index.vue: title: "Misskey Reversi" - sub-title: "他のMisskeyユーザーとリバーシで対戦しよう" - invite: "招待" - rule: "遊び方" + sub-title: "Zagraj w Reversi ze znajomymi!" + invite: "Zaproś" + rule: "Jak grać" rule-desc: "リバーシは、相手と交互に石をボードに置いて、相手の石を挟んで自分の色に変えてゆき、最終的に残った石が多い方が勝ちというボードゲームです。" - mode-invite: "招待" - mode-invite-desc: "指定したユーザーと対戦するモードです。" - invitations: "対局の招待があります!" - my-games: "自分の対局" - all-games: "みんなの対局" - enter-username: "ユーザー名を入力してください" + mode-invite: "Zaproś" + mode-invite-desc: "Zaproś użytkownika do gry." + invitations: "Otrzymałeś(-aś) zaproszenie!" + my-games: "Moje gry" + all-games: "Wszystkie gry" + enter-username: "Wprowadź nazwę użytkownika" game-state: - ended: "終了" - playing: "進行中" + ended: "Zakończono" + playing: "W trakcie" common/views/components/games/reversi/reversi.room.vue: - settings-of-the-game: "ゲームの設定" - choose-map: "マップを選択" - random: "ランダム" - black-or-white: "先手/後手" + settings-of-the-game: "Ustawienia gry" + choose-map: "Wybierz mapę" + random: "Losowy" + black-or-white: "Czarny/biały" black-is: "{}が黒" - rules: "ルール" + rules: "Zasady" is-llotheo: "石の少ない方が勝ち(ロセオ)" looped-map: "ループマップ" can-put-everywhere: "どこでも置けるモード" @@ -366,23 +366,23 @@ common/views/widgets/slideshow.vue: folder: "Naciśnij i wybierz folder" no-image: "Brak obrazu w tym folderze" common/views/widgets/tips.vue: - tips-line1: "<kbd>t</kbd>でタイムラインにフォーカスできます" - tips-line2: "<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます" - tips-line3: "投稿フォームにはファイルをドラッグ&ドロップできます" + tips-line1: "Możesz przejść do osi czasu używając <kbd>t</kbd>." + tips-line2: "Otwórz formularz nowego wpisu używając <kbd>p</kbd> lub <kbd>n</kbd>." + tips-line3: "Możesz przeciągnąć i upuścić pliki w formularzu wpisu." tips-line4: "投稿フォームにクリップボードにある画像データをペーストできます" - tips-line5: "ドライブにファイルをドラッグ&ドロップしてアップロードできます" - tips-line6: "ドライブでファイルをドラッグしてフォルダ移動できます" + tips-line5: "Możesz wysłać pliki przeciągając i upuszczając je w Dysku." + tips-line6: "Możesz przenieść katalog przeciągając go w Dysku." tips-line7: "ドライブでフォルダをドラッグしてフォルダ移動できます" tips-line8: "Strona główna może zostać dostosowana w ustawieniach." tips-line9: "Misskey jest dostępny na licencji AGPLv3." tips-line10: "タイムマシンウィジェットを利用すると、簡単に過去のタイムラインに遡れます" - tips-line11: "投稿の ... をクリックして、投稿をユーザーページにピン留めできます" + tips-line11: "Możesz przypiąć wpis na stronie użytkownika klikając na „…”" tips-line13: "投稿に添付したファイルは全てドライブに保存されます" tips-line14: "ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます" - tips-line17: "「**」でテキストを囲むと**強調表示**されます" - tips-line19: "いくつかのウィンドウはブラウザの外に切り離すことができます" + tips-line17: "Oznaczenie tekstu **w ten sposób** wyróżni go." + tips-line19: "Część okien może zostać odłączona z przeglądarki." tips-line20: "カレンダーウィジェットのパーセンテージは、経過の割合を示しています" - tips-line21: "APIを利用してbotの開発なども行えます" + tips-line21: "Możesz też używać API, aby tworzyć boty." tips-line23: "まゆかわいいよまゆ" tips-line24: "Misskey zaczął działać w 2014." tips-line25: "Możesz otrzymywać powiadomienia nawet jeżeli Misskey nie jest otwarty w obsługiwanej przeglądarce." diff --git a/package.json b/package.json index 8520475717..11090ef422 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "misskey", "author": "syuilo <i@syuilo.com>", - "version": "5.14.0", - "clientVersion": "1.0.7997", + "version": "5.17.0", + "clientVersion": "1.0.8026", "codename": "nighthike", "main": "./built/index.js", "private": true, @@ -59,7 +59,7 @@ "@types/mocha": "5.2.3", "@types/mongodb": "3.1.3", "@types/ms": "0.7.30", - "@types/node": "10.5.5", + "@types/node": "10.5.6", "@types/portscanner": "2.1.0", "@types/pug": "2.0.4", "@types/qrcode": "1.2.0", @@ -88,7 +88,7 @@ "bootstrap-vue": "2.0.0-rc.11", "cafy": "11.3.0", "chalk": "2.4.1", - "commander": "2.16.0", + "commander": "2.17.0", "crc-32": "1.2.0", "css-loader": "1.0.0", "dateformat": "3.0.3", @@ -183,6 +183,7 @@ "showdown-highlightjs-extension": "0.1.2", "single-line-log": "1.1.2", "speakeasy": "2.0.0", + "stringz": "1.0.0", "style-loader": "0.21.0", "stylus": "0.54.5", "stylus-loader": "3.0.2", diff --git a/src/client/app/common/views/components/games/reversi/reversi.game.vue b/src/client/app/common/views/components/games/reversi/reversi.game.vue index 66973e1970..d1809d741f 100644 --- a/src/client/app/common/views/components/games/reversi/reversi.game.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.game.vue @@ -1,8 +1,9 @@ <template> <div class="xqnhankfuuilcwvhgsopeqncafzsquya"> + <button class="go-index" v-if="selfNav" @click="goIndex">%fa:arrow-left%</button> <header><b><router-link :to="blackUser | userPage">{{ blackUser | userName }}</router-link></b>(%i18n:common.reversi.black%) vs <b><router-link :to="whiteUser | userPage">{{ whiteUser | userName }}</router-link></b>(%i18n:common.reversi.white%)</header> - <div style="overflow: hidden"> + <div style="overflow: hidden; line-height: 28px;"> <p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}<mk-ellipsis/></p> <p class="turn" v-if="logPos != logs.length">{{ '%i18n:common.reversi.past-turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}</p> <p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p> @@ -69,7 +70,20 @@ import Reversi, { Color } from '../../../../../../../games/reversi/core'; import { url } from '../../../../../config'; export default Vue.extend({ - props: ['initGame', 'connection'], + props: { + initGame: { + type: Object, + require: true + }, + connection: { + type: Object, + require: true + }, + selfNav: { + type: Boolean, + require: true + } + }, data() { return { @@ -276,6 +290,10 @@ export default Vue.extend({ (this as any).api('games/reversi/games/surrender', { gameId: this.game.id }); + }, + + goIndex() { + this.$emit('go-index'); } } }); @@ -287,6 +305,14 @@ export default Vue.extend({ root(isDark) text-align center + > .go-index + position absolute + top 0 + left 0 + z-index 1 + width 42px + height 42px + > header padding 8px border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4 diff --git a/src/client/app/common/views/components/games/reversi/reversi.gameroom.vue b/src/client/app/common/views/components/games/reversi/reversi.gameroom.vue index 4969a9347e..1539c88de0 100644 --- a/src/client/app/common/views/components/games/reversi/reversi.gameroom.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.gameroom.vue @@ -1,7 +1,7 @@ <template> <div> <x-room v-if="!g.isStarted" :game="g" :connection="connection"/> - <x-game v-else :init-game="g" :connection="connection"/> + <x-game v-else :init-game="g" :connection="connection" :self-nav="selfNav" @go-index="goIndex"/> </div> </template> @@ -16,7 +16,16 @@ export default Vue.extend({ XGame, XRoom }, - props: ['game'], + props: { + game: { + type: Object, + required: true + }, + selfNav: { + type: Boolean, + require: true + } + }, data() { return { connection: null, @@ -36,6 +45,9 @@ export default Vue.extend({ onStarted(game) { Object.assign(this.g, game); this.$forceUpdate(); + }, + goIndex() { + this.$emit('go-index'); } } }); diff --git a/src/client/app/common/views/components/games/reversi/reversi.index.vue b/src/client/app/common/views/components/games/reversi/reversi.index.vue index 026159a0fd..d4d35f6a86 100644 --- a/src/client/app/common/views/components/games/reversi/reversi.index.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.index.vue @@ -96,11 +96,7 @@ export default Vue.extend({ methods: { go(game) { - (this as any).api('games/reversi/games/show', { - gameId: game.id - }).then(game => { - this.$emit('go', game); - }); + this.$emit('go', game); }, match() { diff --git a/src/client/app/common/views/components/games/reversi/reversi.vue b/src/client/app/common/views/components/games/reversi/reversi.vue index d99634a950..223ec4597a 100644 --- a/src/client/app/common/views/components/games/reversi/reversi.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.vue @@ -1,7 +1,7 @@ <template> <div class="vchtoekanapleubgzioubdtmlkribzfd"> <div v-if="game"> - <x-gameroom :game="game"/> + <x-gameroom :game="game" :self-nav="selfNav" @go-index="goIndex"/> </div> <div class="matching" v-else-if="matching"> <h1>{{ '%i18n:@matching.waiting-for%'.split('{}')[0] }}<b>{{ matching | userName }}</b>{{ '%i18n:@matching.waiting-for%'.split('{}')[1] }}<mk-ellipsis/></h1> @@ -34,6 +34,11 @@ export default Vue.extend({ gameId: { type: String, required: false + }, + selfNav: { + type: Boolean, + require: false, + default: true } }, @@ -95,18 +100,24 @@ export default Vue.extend({ (this as any).api('games/reversi/games/show', { gameId: this.gameId }).then(game => { - this.nav(game, true); + this.game = game; Progress.done(); }); } }, - nav(game, silent) { - this.matching = null; - this.game = game; + async nav(game, actualNav = true) { + if (this.selfNav) { + // 受け取ったゲーム情報が省略されたものなら完全な情報を取得する + if (game != null && (game.settings == null || game.settings.map == null)) { + game = await (this as any).api('games/reversi/games/show', { + gameId: game.id + }); + } - if (!silent) { - this.$emit('nav', this.game); + this.game = game; + } else { + this.$emit('nav', game, actualNav); } }, @@ -125,7 +136,8 @@ export default Vue.extend({ }).then(game => { if (game) { this.matching = null; - this.game = game; + + this.nav(game); } }); }, @@ -133,6 +145,11 @@ export default Vue.extend({ onMatched(game) { this.matching = null; this.game = game; + this.nav(game, false); + }, + + goIndex() { + this.nav(null); } } }); diff --git a/src/client/app/common/views/components/misskey-flavored-markdown.ts b/src/client/app/common/views/components/misskey-flavored-markdown.ts index f9c97bd35a..e97da4302c 100644 --- a/src/client/app/common/views/components/misskey-flavored-markdown.ts +++ b/src/client/app/common/views/components/misskey-flavored-markdown.ts @@ -1,5 +1,6 @@ import Vue from 'vue'; import * as emojilib from 'emojilib'; +import { length } from 'stringz'; import parse from '../../../../../mfm/parse'; import getAcct from '../../../../../misc/acct/render'; import { url } from '../../../config'; @@ -40,10 +41,13 @@ export default Vue.component('misskey-flavored-markdown', { ast = this.ast; } + let bigCount = 0; + let motionCount = 0; + // Parse ast to DOM const els = flatten(ast.map(token => { switch (token.type) { - case 'text': + case 'text': { const text = token.content.replace(/(\r\n|\n|\r)/g, '\n'); if (this.shouldBreak) { @@ -54,30 +58,52 @@ export default Vue.component('misskey-flavored-markdown', { } else { return createElement('span', text.replace(/\n/g, ' ')); } + } - case 'bold': + case 'bold': { return createElement('b', token.bold); + } - case 'big': + case 'big': { + bigCount++; + const isLong = length(token.big) > 10; + const isMany = bigCount > 3; return (createElement as any)('strong', { attrs: { - style: 'display: inline-block; font-size: 200%;' + style: `display: inline-block; font-size: ${ isMany ? '100%' : '150%' };` }, - directives: [this.$store.state.settings.disableAnimatedMfm ? {} : { + directives: [this.$store.state.settings.disableAnimatedMfm || isLong || isMany ? {} : { name: 'animate-css', value: { classes: 'tada', iteration: 'infinite' } }] }, token.big); + } - case 'url': + case 'motion': { + motionCount++; + const isLong = length(token.motion) > 10; + const isMany = motionCount > 3; + return (createElement as any)('span', { + attrs: { + style: 'display: inline-block;' + }, + directives: [this.$store.state.settings.disableAnimatedMfm || isLong || isMany ? {} : { + name: 'animate-css', + value: { classes: 'rubberBand', iteration: 'infinite' } + }] + }, token.motion); + } + + case 'url': { return createElement(MkUrl, { props: { url: token.content, target: '_blank' } }); + } - case 'link': + case 'link': { return createElement('a', { attrs: { class: 'link', @@ -86,8 +112,9 @@ export default Vue.component('misskey-flavored-markdown', { title: token.url } }, token.title); + } - case 'mention': + case 'mention': { return (createElement as any)('a', { attrs: { href: `${url}/@${getAcct(token)}`, @@ -99,16 +126,18 @@ export default Vue.component('misskey-flavored-markdown', { value: token.content }] }, token.content); + } - case 'hashtag': + case 'hashtag': { return createElement('a', { attrs: { href: `${url}/tags/${encodeURIComponent(token.hashtag)}`, target: '_blank' } }, token.content); + } - case 'code': + case 'code': { return createElement('pre', { class: 'code' }, [ @@ -118,15 +147,17 @@ export default Vue.component('misskey-flavored-markdown', { } }) ]); + } - case 'inline-code': + case 'inline-code': { return createElement('code', { domProps: { innerHTML: token.html } }); + } - case 'quote': + case 'quote': { const text2 = token.quote.replace(/(\r\n|\n|\r)/g, '\n'); if (this.shouldBreak) { @@ -145,27 +176,32 @@ export default Vue.component('misskey-flavored-markdown', { } }, text2.replace(/\n/g, ' ')); } + } - case 'title': + case 'title': { return createElement('div', { attrs: { class: 'title' } }, token.title); + } - case 'emoji': + case 'emoji': { const emoji = emojilib.lib[token.emoji]; return createElement('span', emoji ? emoji.char : token.content); + } - case 'search': + case 'search': { return createElement(MkGoogle, { props: { q: token.query } }); + } - default: + default: { console.log('unknown ast type:', token.type); + } } })); diff --git a/src/client/app/common/views/components/ui/form/button.vue b/src/client/app/common/views/components/ui/form/button.vue index 6e1475bc38..9c37b3118b 100644 --- a/src/client/app/common/views/components/ui/form/button.vue +++ b/src/client/app/common/views/components/ui/form/button.vue @@ -45,6 +45,9 @@ root(isDark) color isDark ? #fff : #606266 transition 0.1s + * + pointer-events none + &:hover &:focus color $theme-color diff --git a/src/client/app/desktop/views/pages/games/reversi.vue b/src/client/app/desktop/views/pages/games/reversi.vue index 590bda2d86..ce9b42c65f 100644 --- a/src/client/app/desktop/views/pages/games/reversi.vue +++ b/src/client/app/desktop/views/pages/games/reversi.vue @@ -1,6 +1,6 @@ <template> <component :is="ui ? 'mk-ui' : 'div'"> - <mk-reversi :game-id="$route.params.game" @nav="nav"/> + <mk-reversi :game-id="$route.params.game" @nav="nav" :self-nav="false"/> </component> </template> @@ -14,9 +14,14 @@ export default Vue.extend({ } }, methods: { - nav(game) { - history.pushState(null, null, '/reversi/' + game.id); - }, + nav(game, actualNav) { + if (actualNav) { + this.$router.push('/reversi/' + game.id); + } else { + // TODO: https://github.com/vuejs/vue-router/issues/703 + this.$router.push('/reversi/' + game.id); + } + } } }); </script> diff --git a/src/client/app/mobile/views/pages/games/reversi.vue b/src/client/app/mobile/views/pages/games/reversi.vue index 7118644ef3..e6e6325f8b 100644 --- a/src/client/app/mobile/views/pages/games/reversi.vue +++ b/src/client/app/mobile/views/pages/games/reversi.vue @@ -1,7 +1,7 @@ <template> <mk-ui> <span slot="header">%fa:gamepad%%i18n:@reversi%</span> - <mk-reversi :game-id="$route.params.game" @nav="nav"/> + <mk-reversi :game-id="$route.params.game" @nav="nav" :self-nav="false"/> </mk-ui> </template> @@ -14,8 +14,13 @@ export default Vue.extend({ document.documentElement.style.background = '#fff'; }, methods: { - nav(game) { - history.pushState(null, null, '/reversi/' + game.id); + nav(game, actualNav) { + if (actualNav) { + this.$router.push('/reversi/' + game.id); + } else { + // TODO: https://github.com/vuejs/vue-router/issues/703 + this.$router.push('/reversi/' + game.id); + } } } }); diff --git a/src/mfm/html.ts b/src/mfm/html.ts index dfe291b3a5..c11bd55cf4 100644 --- a/src/mfm/html.ts +++ b/src/mfm/html.ts @@ -18,6 +18,12 @@ const handlers: { [key: string]: (window: any, token: any, mentionedRemoteUsers: document.body.appendChild(b); }, + motion({ document }, { big }) { + const b = document.createElement('strong'); + b.textContent = big; + document.body.appendChild(b); + }, + code({ document }, { code }) { const pre = document.createElement('pre'); const inner = document.createElement('code'); diff --git a/src/mfm/parse/elements/big.ts b/src/mfm/parse/elements/big.ts index ca798986e9..8e39c75a55 100644 --- a/src/mfm/parse/elements/big.ts +++ b/src/mfm/parse/elements/big.ts @@ -1,5 +1,5 @@ /** - * Bold + * Big */ export type TextElementBig = { diff --git a/src/mfm/parse/elements/motion.ts b/src/mfm/parse/elements/motion.ts new file mode 100644 index 0000000000..9e7370e071 --- /dev/null +++ b/src/mfm/parse/elements/motion.ts @@ -0,0 +1,20 @@ +/** + * Motion + */ + +export type TextElementMotion = { + type: 'motion' + content: string + motion: string +}; + +export default function(text: string) { + const match = text.match(/^\(\(\((.+?)\)\)\)/) || text.match(/^<motion>(.+?)<\/motion>/); + if (!match) return null; + const motion = match[0]; + return { + type: 'motion', + content: motion, + motion: match[1] + } as TextElementMotion; +} diff --git a/src/mfm/parse/index.ts b/src/mfm/parse/index.ts index 066c062559..99c00ae649 100644 --- a/src/mfm/parse/index.ts +++ b/src/mfm/parse/index.ts @@ -14,6 +14,7 @@ import { TextElementQuote } from './elements/quote'; import { TextElementSearch } from './elements/search'; import { TextElementTitle } from './elements/title'; import { TextElementUrl } from './elements/url'; +import { TextElementMotion } from './elements/motion'; const elements = [ require('./elements/big'), @@ -27,7 +28,8 @@ const elements = [ require('./elements/inline-code'), require('./elements/quote'), require('./elements/emoji'), - require('./elements/search') + require('./elements/search'), + require('./elements/motion') ].map(element => element.default as TextElementProcessor); export type TextElement = { type: 'text', content: string } @@ -42,7 +44,8 @@ export type TextElement = { type: 'text', content: string } | TextElementQuote | TextElementSearch | TextElementTitle - | TextElementUrl; + | TextElementUrl + | TextElementMotion; export type TextElementProcessor = (text: string, i: number) => TextElement | TextElement[]; export default (source: string): TextElement[] => { diff --git a/src/server/api/endpoints/games/reversi/games/surrender.ts b/src/server/api/endpoints/games/reversi/games/surrender.ts index dc9908aef1..49821650ed 100644 --- a/src/server/api/endpoints/games/reversi/games/surrender.ts +++ b/src/server/api/endpoints/games/reversi/games/surrender.ts @@ -12,7 +12,7 @@ export const meta = { requireCredential: true, params: { - gameId: $.type(ID).optional.note({ + gameId: $.type(ID).note({ desc: { ja: '投了したい対局' } diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index 9024740a96..c8d588eaaf 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -11,7 +11,7 @@ const handler = new EventEmitter(); let bot: IUser; -const post = async (text: string) => { +const post = async (text: string, home = true) => { if (bot == null) { const account = await User.findOne({ usernameLower: config.github_bot.username.toLowerCase() @@ -25,7 +25,7 @@ const post = async (text: string) => { } } - createNote(bot, { text, visibility: 'home' }); + createNote(bot, { text, visibility: home ? 'home' : 'public' }); }; // Init router @@ -130,7 +130,7 @@ handler.on('issue_comment', event => { handler.on('watch', event => { const sender = event.sender; - post(`⭐️ Starred by **${sender.login}** ⭐️`); + post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false); }); handler.on('fork', event => { diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 64556c160a..4f90a19f2c 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -103,6 +103,24 @@ export default async (user: IUser, data: Option, silent = false) => new Promise< data.visibleUsers = data.visibleUsers.filter(x => x != null); } + if (data.reply && data.reply.deletedAt != null) { + return rej(); + } + + if (data.renote && data.renote.deletedAt != null) { + return rej(); + } + + // リプライ先が自分以外の非公開の投稿なら禁止 + if (data.reply && data.reply.visibility == 'private' && !data.reply.userId.equals(user._id)) { + return rej(); + } + + // Renote先が自分以外の非公開の投稿なら禁止 + if (data.renote && data.renote.visibility == 'private' && !data.renote.userId.equals(user._id)) { + return rej(); + } + if (data.text) { data.text = data.text.trim(); } diff --git a/test/mfm.ts b/test/mfm.ts index 1a22e1491b..706c4c549a 100644 --- a/test/mfm.ts +++ b/test/mfm.ts @@ -34,11 +34,25 @@ describe('Text', () => { it('big', () => { const tokens = analyze('***Strawberry*** Pasta'); assert.deepEqual([ - { type: 'big', content: '***Strawberry***', bold: 'Strawberry' }, + { type: 'big', content: '***Strawberry***', big: 'Strawberry' }, { type: 'text', content: ' Pasta' } ], tokens); }); + it('motion', () => { + const tokens1 = analyze('(((Strawberry))) Pasta'); + assert.deepEqual([ + { type: 'motion', content: '(((Strawberry)))', motion: 'Strawberry' }, + { type: 'text', content: ' Pasta' } + ], tokens1); + + const tokens2 = analyze('<motion>Strawberry</motion> Pasta'); + assert.deepEqual([ + { type: 'motion', content: '<motion>Strawberry</motion>', motion: 'Strawberry' }, + { type: 'text', content: ' Pasta' } + ], tokens2); + }); + it('mention', () => { const tokens = analyze('@himawari お腹ペコい'); assert.deepEqual([