Merge branch 'master' into l10n_master
This commit is contained in:
		
						commit
						0b154ac7ba
					
				
					 90 changed files with 919 additions and 326 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
/node_modules
 | 
					/node_modules
 | 
				
			||||||
/build
 | 
					/build
 | 
				
			||||||
/built
 | 
					/built
 | 
				
			||||||
 | 
					built
 | 
				
			||||||
/data
 | 
					/data
 | 
				
			||||||
/.cache-loader
 | 
					/.cache-loader
 | 
				
			||||||
npm-debug.log
 | 
					npm-debug.log
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,8 @@ common:
 | 
				
			||||||
    warning: "<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。"
 | 
					    warning: "<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。"
 | 
				
			||||||
  application-authorization: "アプリの連携"
 | 
					  application-authorization: "アプリの連携"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
 | 
					  do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
 | 
				
			||||||
 | 
					  got-it: "わかった"
 | 
				
			||||||
  customization-tips:
 | 
					  customization-tips:
 | 
				
			||||||
    title: "カスタマイズのヒント"
 | 
					    title: "カスタマイズのヒント"
 | 
				
			||||||
    paragraph1: "ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。"
 | 
					    paragraph1: "ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。"
 | 
				
			||||||
| 
						 | 
					@ -26,7 +28,6 @@ common:
 | 
				
			||||||
    notified-by: "{}さんから"
 | 
					    notified-by: "{}さんから"
 | 
				
			||||||
    reply-from: "{}さんから返信:"
 | 
					    reply-from: "{}さんから返信:"
 | 
				
			||||||
    quoted-by: "{}さんが引用:"
 | 
					    quoted-by: "{}さんが引用:"
 | 
				
			||||||
  name: "Misskey"
 | 
					 | 
				
			||||||
  time:
 | 
					  time:
 | 
				
			||||||
    unknown: "なぞのじかん"
 | 
					    unknown: "なぞのじかん"
 | 
				
			||||||
    future: "未来"
 | 
					    future: "未来"
 | 
				
			||||||
| 
						 | 
					@ -38,16 +39,10 @@ common:
 | 
				
			||||||
    weeks_ago: "{}週間前"
 | 
					    weeks_ago: "{}週間前"
 | 
				
			||||||
    months_ago: "{}ヶ月前"
 | 
					    months_ago: "{}ヶ月前"
 | 
				
			||||||
    years_ago: "{}年前"
 | 
					    years_ago: "{}年前"
 | 
				
			||||||
 | 
					  month-and-day: "{month}月 {day}日"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  trash: "ゴミ箱"
 | 
					  trash: "ゴミ箱"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  date:
 | 
					 | 
				
			||||||
    full-year: "年"
 | 
					 | 
				
			||||||
    month: "月"
 | 
					 | 
				
			||||||
    day: "日"
 | 
					 | 
				
			||||||
    hours: "時"
 | 
					 | 
				
			||||||
    minutes: "分"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  weekday-short:
 | 
					  weekday-short:
 | 
				
			||||||
    sunday: "日"
 | 
					    sunday: "日"
 | 
				
			||||||
    monday: "月"
 | 
					    monday: "月"
 | 
				
			||||||
| 
						 | 
					@ -95,6 +90,7 @@ common:
 | 
				
			||||||
  i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
 | 
					  i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
 | 
				
			||||||
  show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示"
 | 
					  show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示"
 | 
				
			||||||
  verified-user: "認証済みのユーザー"
 | 
					  verified-user: "認証済みのユーザー"
 | 
				
			||||||
 | 
					  disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reversi:
 | 
					  reversi:
 | 
				
			||||||
    drawn: "引き分け"
 | 
					    drawn: "引き分け"
 | 
				
			||||||
| 
						 | 
					@ -182,6 +178,10 @@ common/views/components/games/reversi/reversi.vue:
 | 
				
			||||||
    waiting-for: "{}を待っています"
 | 
					    waiting-for: "{}を待っています"
 | 
				
			||||||
    cancel: "キャンセル"
 | 
					    cancel: "キャンセル"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					common/views/components/games/reversi/reversi.game.vue:
 | 
				
			||||||
 | 
					  surrender: "投了"
 | 
				
			||||||
 | 
					  surrendered: "投了により"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
common/views/components/games/reversi/reversi.index.vue:
 | 
					common/views/components/games/reversi/reversi.index.vue:
 | 
				
			||||||
  title: "Misskey Reversi"
 | 
					  title: "Misskey Reversi"
 | 
				
			||||||
  sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
 | 
					  sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
 | 
				
			||||||
| 
						 | 
					@ -209,7 +209,7 @@ common/views/components/games/reversi/reversi.room.vue:
 | 
				
			||||||
  looped-map: "ループマップ"
 | 
					  looped-map: "ループマップ"
 | 
				
			||||||
  can-put-everywhere: "どこでも置けるモード"
 | 
					  can-put-everywhere: "どこでも置けるモード"
 | 
				
			||||||
  settings-of-the-bot: "Botの設定"
 | 
					  settings-of-the-bot: "Botの設定"
 | 
				
			||||||
  this-gane-is-started-soon: "ゲームは数秒後に開始されます"
 | 
					  this-game-is-started-soon: "ゲームは数秒後に開始されます"
 | 
				
			||||||
  waiting-for-other: "相手の準備が完了するのを待っています"
 | 
					  waiting-for-other: "相手の準備が完了するのを待っています"
 | 
				
			||||||
  waiting-for-me: "あなたの準備が完了するのを待っています"
 | 
					  waiting-for-me: "あなたの準備が完了するのを待っています"
 | 
				
			||||||
  waiting-for-both: "準備中"
 | 
					  waiting-for-both: "準備中"
 | 
				
			||||||
| 
						 | 
					@ -254,12 +254,14 @@ common/views/components/messaging-room.vue:
 | 
				
			||||||
  no-history: "これより過去の履歴はありません"
 | 
					  no-history: "これより過去の履歴はありません"
 | 
				
			||||||
  resize-form: "ドラッグしてフォームの広さを調整"
 | 
					  resize-form: "ドラッグしてフォームの広さを調整"
 | 
				
			||||||
  new-message: "新しいメッセージがあります"
 | 
					  new-message: "新しいメッセージがあります"
 | 
				
			||||||
 | 
					  only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
common/views/components/messaging-room.form.vue:
 | 
					common/views/components/messaging-room.form.vue:
 | 
				
			||||||
  input-message-here: "ここにメッセージを入力"
 | 
					  input-message-here: "ここにメッセージを入力"
 | 
				
			||||||
  send: "送信"
 | 
					  send: "送信"
 | 
				
			||||||
  attach-from-local: "PCからファイルを添付する"
 | 
					  attach-from-local: "PCからファイルを添付する"
 | 
				
			||||||
  attach-from-drive: "ドライブからファイルを添付する"
 | 
					  attach-from-drive: "ドライブからファイルを添付する"
 | 
				
			||||||
 | 
					  only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
common/views/components/messaging-room.message.vue:
 | 
					common/views/components/messaging-room.message.vue:
 | 
				
			||||||
  is-read: "既読"
 | 
					  is-read: "既読"
 | 
				
			||||||
| 
						 | 
					@ -306,6 +308,8 @@ common/views/components/signin.vue:
 | 
				
			||||||
  token: "トークン"
 | 
					  token: "トークン"
 | 
				
			||||||
  signing-in: "やってます..."
 | 
					  signing-in: "やってます..."
 | 
				
			||||||
  signin: "サインイン"
 | 
					  signin: "サインイン"
 | 
				
			||||||
 | 
					  or: "または"
 | 
				
			||||||
 | 
					  signin-with-twitter: "Twitterでログイン"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
common/views/components/signup.vue:
 | 
					common/views/components/signup.vue:
 | 
				
			||||||
  username: "ユーザー名"
 | 
					  username: "ユーザー名"
 | 
				
			||||||
| 
						 | 
					@ -433,6 +437,18 @@ common/views/pages/follow.vue:
 | 
				
			||||||
  request-pending: "フォロー許可待ち"
 | 
					  request-pending: "フォロー許可待ち"
 | 
				
			||||||
  follow-request: "フォロー申請"
 | 
					  follow-request: "フォロー申請"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					desktop:
 | 
				
			||||||
 | 
					  banner-crop-title: "バナーとして表示する部分を選択"
 | 
				
			||||||
 | 
					  banner: "バナー"
 | 
				
			||||||
 | 
					  uploading-banner: "新しいバナーをアップロードしています"
 | 
				
			||||||
 | 
					  banner-updated: "バナーを更新しました"
 | 
				
			||||||
 | 
					  choose-banner: "バナーにする画像を選択"
 | 
				
			||||||
 | 
					  avatar-crop-title: "アバターとして表示する部分を選択"
 | 
				
			||||||
 | 
					  avatar: "アバター"
 | 
				
			||||||
 | 
					  uploading-avatar: "新しいアバターをアップロードしています"
 | 
				
			||||||
 | 
					  avatar-updated: "アバターを更新しました"
 | 
				
			||||||
 | 
					  choose-avatar: "アバターにする画像を選択"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/activity.chart.vue:
 | 
					desktop/views/components/activity.chart.vue:
 | 
				
			||||||
  total: "Black ... Total"
 | 
					  total: "Black ... Total"
 | 
				
			||||||
  notes: "Blue ... Notes"
 | 
					  notes: "Blue ... Notes"
 | 
				
			||||||
| 
						 | 
					@ -592,6 +608,8 @@ desktop/views/components/notes.note.vue:
 | 
				
			||||||
  detail: "詳細"
 | 
					  detail: "詳細"
 | 
				
			||||||
  private: "この投稿は非公開です"
 | 
					  private: "この投稿は非公開です"
 | 
				
			||||||
  deleted: "この投稿は削除されました"
 | 
					  deleted: "この投稿は削除されました"
 | 
				
			||||||
 | 
					  hide: "隠す"
 | 
				
			||||||
 | 
					  see-more: "もっと見る"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/notes.vue:
 | 
					desktop/views/components/notes.vue:
 | 
				
			||||||
  error: "読み込みに失敗しました。"
 | 
					  error: "読み込みに失敗しました。"
 | 
				
			||||||
| 
						 | 
					@ -630,6 +648,7 @@ desktop/views/components/post-form.vue:
 | 
				
			||||||
  geolocation-alert: "お使いの端末は位置情報に対応していません"
 | 
					  geolocation-alert: "お使いの端末は位置情報に対応していません"
 | 
				
			||||||
  error: "エラー"
 | 
					  error: "エラー"
 | 
				
			||||||
  enter-username: "ユーザー名を入力してください"
 | 
					  enter-username: "ユーザー名を入力してください"
 | 
				
			||||||
 | 
					  annotations: "内容への注釈 (オプション)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/post-form-window.vue:
 | 
					desktop/views/components/post-form-window.vue:
 | 
				
			||||||
  note: "新規投稿"
 | 
					  note: "新規投稿"
 | 
				
			||||||
| 
						 | 
					@ -818,6 +837,7 @@ desktop/views/components/timeline.vue:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/ui.header.vue:
 | 
					desktop/views/components/ui.header.vue:
 | 
				
			||||||
  welcome-back: "おかえりなさい、"
 | 
					  welcome-back: "おかえりなさい、"
 | 
				
			||||||
 | 
					  adjective: "さん"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/ui.header.account.vue:
 | 
					desktop/views/components/ui.header.account.vue:
 | 
				
			||||||
  profile: "プロフィール"
 | 
					  profile: "プロフィール"
 | 
				
			||||||
| 
						 | 
					@ -850,11 +870,10 @@ desktop/views/components/received-follow-requests-window.vue:
 | 
				
			||||||
  accept: "承認"
 | 
					  accept: "承認"
 | 
				
			||||||
  reject: "拒否"
 | 
					  reject: "拒否"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
desktop/views/components/user-lists-window.vue:
 | 
					desktop/views/components/user-lists-window.vue:
 | 
				
			||||||
  title: "リスト"
 | 
					  title: "リスト"
 | 
				
			||||||
  create-list: "リストを作成"
 | 
					  create-list: "リストを作成"
 | 
				
			||||||
 | 
					  list-name: "リスト名"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/user-preview.vue:
 | 
					desktop/views/components/user-preview.vue:
 | 
				
			||||||
  notes: "投稿"
 | 
					  notes: "投稿"
 | 
				
			||||||
| 
						 | 
					@ -959,6 +978,8 @@ desktop/views/pages/user/user.profile.vue:
 | 
				
			||||||
  mute: "ミュートする"
 | 
					  mute: "ミュートする"
 | 
				
			||||||
  muted: "ミュートしています"
 | 
					  muted: "ミュートしています"
 | 
				
			||||||
  unmute: "ミュート解除"
 | 
					  unmute: "ミュート解除"
 | 
				
			||||||
 | 
					  push-to-a-list: "リストに追加"
 | 
				
			||||||
 | 
					  list-pushed: "{user}を{list}に追加しました。"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/user/user.header.vue:
 | 
					desktop/views/pages/user/user.header.vue:
 | 
				
			||||||
  posts: "投稿"
 | 
					  posts: "投稿"
 | 
				
			||||||
| 
						 | 
					@ -1114,6 +1135,7 @@ mobile/views/components/timeline.vue:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mobile/views/components/ui.header.vue:
 | 
					mobile/views/components/ui.header.vue:
 | 
				
			||||||
  welcome-back: "おかえりなさい、"
 | 
					  welcome-back: "おかえりなさい、"
 | 
				
			||||||
 | 
					  adjective: "さん"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mobile/views/components/ui.nav.vue:
 | 
					mobile/views/components/ui.nav.vue:
 | 
				
			||||||
  timeline: "タイムライン"
 | 
					  timeline: "タイムライン"
 | 
				
			||||||
| 
						 | 
					@ -1175,6 +1197,8 @@ mobile/views/pages/welcome.vue:
 | 
				
			||||||
mobile/views/pages/widgets.vue:
 | 
					mobile/views/pages/widgets.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  widgets-hints: "ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。"
 | 
					  widgets-hints: "ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。"
 | 
				
			||||||
 | 
					  add-widget: "追加"
 | 
				
			||||||
 | 
					  customization-tips: "カスタマイズのヒント"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mobile/views/pages/widgets/activity.vue:
 | 
					mobile/views/pages/widgets/activity.vue:
 | 
				
			||||||
  activity: "アクティビティ"
 | 
					  activity: "アクティビティ"
 | 
				
			||||||
| 
						 | 
					@ -1223,6 +1247,7 @@ mobile/views/pages/settings/settings.profile.vue:
 | 
				
			||||||
mobile/views/pages/search.vue:
 | 
					mobile/views/pages/search.vue:
 | 
				
			||||||
  search: "検索"
 | 
					  search: "検索"
 | 
				
			||||||
  empty: "「{}」に関する投稿は見つかりませんでした。"
 | 
					  empty: "「{}」に関する投稿は見つかりませんでした。"
 | 
				
			||||||
 | 
					  not-found: "「{}」に関する投稿は見つかりませんでした。"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mobile/views/pages/selectdrive.vue:
 | 
					mobile/views/pages/selectdrive.vue:
 | 
				
			||||||
  select-file: "ファイルを選択"
 | 
					  select-file: "ファイルを選択"
 | 
				
			||||||
| 
						 | 
					@ -1327,3 +1352,7 @@ docs:
 | 
				
			||||||
      name: "名前"
 | 
					      name: "名前"
 | 
				
			||||||
      type: "型"
 | 
					      type: "型"
 | 
				
			||||||
      description: "説明"
 | 
					      description: "説明"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dev/views/index.vue:
 | 
				
			||||||
 | 
					  manage-apps: "アプリの管理"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										29
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										29
									
								
								package.json
									
										
									
									
									
								
							| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"name": "misskey",
 | 
						"name": "misskey",
 | 
				
			||||||
	"author": "syuilo <i@syuilo.com>",
 | 
						"author": "syuilo <i@syuilo.com>",
 | 
				
			||||||
	"version": "5.10.0",
 | 
						"version": "5.20.1",
 | 
				
			||||||
	"clientVersion": "1.0.7828",
 | 
						"clientVersion": "1.0.8105",
 | 
				
			||||||
	"codename": "nighthike",
 | 
						"codename": "nighthike",
 | 
				
			||||||
	"main": "./built/index.js",
 | 
						"main": "./built/index.js",
 | 
				
			||||||
	"private": true,
 | 
						"private": true,
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,6 @@
 | 
				
			||||||
		"@types/is-root": "1.0.0",
 | 
							"@types/is-root": "1.0.0",
 | 
				
			||||||
		"@types/is-url": "1.2.28",
 | 
							"@types/is-url": "1.2.28",
 | 
				
			||||||
		"@types/js-yaml": "3.11.2",
 | 
							"@types/js-yaml": "3.11.2",
 | 
				
			||||||
		"@types/jsdom": "11.0.6",
 | 
					 | 
				
			||||||
		"@types/koa": "2.0.46",
 | 
							"@types/koa": "2.0.46",
 | 
				
			||||||
		"@types/koa-bodyparser": "5.0.1",
 | 
							"@types/koa-bodyparser": "5.0.1",
 | 
				
			||||||
		"@types/koa-compress": "2.0.8",
 | 
							"@types/koa-compress": "2.0.8",
 | 
				
			||||||
| 
						 | 
					@ -60,8 +59,7 @@
 | 
				
			||||||
		"@types/mocha": "5.2.3",
 | 
							"@types/mocha": "5.2.3",
 | 
				
			||||||
		"@types/mongodb": "3.1.3",
 | 
							"@types/mongodb": "3.1.3",
 | 
				
			||||||
		"@types/ms": "0.7.30",
 | 
							"@types/ms": "0.7.30",
 | 
				
			||||||
		"@types/node": "10.5.5",
 | 
							"@types/node": "10.5.7",
 | 
				
			||||||
		"@types/parse5": "5.0.0",
 | 
					 | 
				
			||||||
		"@types/portscanner": "2.1.0",
 | 
							"@types/portscanner": "2.1.0",
 | 
				
			||||||
		"@types/pug": "2.0.4",
 | 
							"@types/pug": "2.0.4",
 | 
				
			||||||
		"@types/qrcode": "1.2.0",
 | 
							"@types/qrcode": "1.2.0",
 | 
				
			||||||
| 
						 | 
					@ -90,7 +88,7 @@
 | 
				
			||||||
		"bootstrap-vue": "2.0.0-rc.11",
 | 
							"bootstrap-vue": "2.0.0-rc.11",
 | 
				
			||||||
		"cafy": "11.3.0",
 | 
							"cafy": "11.3.0",
 | 
				
			||||||
		"chalk": "2.4.1",
 | 
							"chalk": "2.4.1",
 | 
				
			||||||
		"commander": "2.16.0",
 | 
							"commander": "2.17.1",
 | 
				
			||||||
		"crc-32": "1.2.0",
 | 
							"crc-32": "1.2.0",
 | 
				
			||||||
		"css-loader": "1.0.0",
 | 
							"css-loader": "1.0.0",
 | 
				
			||||||
		"dateformat": "3.0.3",
 | 
							"dateformat": "3.0.3",
 | 
				
			||||||
| 
						 | 
					@ -100,7 +98,7 @@
 | 
				
			||||||
		"diskusage": "0.2.4",
 | 
							"diskusage": "0.2.4",
 | 
				
			||||||
		"dompurify": "1.0.5",
 | 
							"dompurify": "1.0.5",
 | 
				
			||||||
		"elasticsearch": "15.1.1",
 | 
							"elasticsearch": "15.1.1",
 | 
				
			||||||
		"element-ui": "2.4.5",
 | 
							"element-ui": "2.4.6",
 | 
				
			||||||
		"emojilib": "2.3.0",
 | 
							"emojilib": "2.3.0",
 | 
				
			||||||
		"escape-regexp": "0.0.1",
 | 
							"escape-regexp": "0.0.1",
 | 
				
			||||||
		"eslint": "5.0.1",
 | 
							"eslint": "5.0.1",
 | 
				
			||||||
| 
						 | 
					@ -108,7 +106,7 @@
 | 
				
			||||||
		"eventemitter3": "3.1.0",
 | 
							"eventemitter3": "3.1.0",
 | 
				
			||||||
		"exif-js": "2.3.0",
 | 
							"exif-js": "2.3.0",
 | 
				
			||||||
		"file-loader": "1.1.11",
 | 
							"file-loader": "1.1.11",
 | 
				
			||||||
		"file-type": "8.1.0",
 | 
							"file-type": "9.0.0",
 | 
				
			||||||
		"fuckadblock": "3.2.1",
 | 
							"fuckadblock": "3.2.1",
 | 
				
			||||||
		"gulp": "3.9.1",
 | 
							"gulp": "3.9.1",
 | 
				
			||||||
		"gulp-cssnano": "2.1.3",
 | 
							"gulp-cssnano": "2.1.3",
 | 
				
			||||||
| 
						 | 
					@ -156,7 +154,7 @@
 | 
				
			||||||
		"monk": "6.0.6",
 | 
							"monk": "6.0.6",
 | 
				
			||||||
		"ms": "2.1.1",
 | 
							"ms": "2.1.1",
 | 
				
			||||||
		"nan": "2.10.0",
 | 
							"nan": "2.10.0",
 | 
				
			||||||
		"node-sass": "4.9.2",
 | 
							"node-sass": "4.9.3",
 | 
				
			||||||
		"node-sass-json-importer": "3.3.1",
 | 
							"node-sass-json-importer": "3.3.1",
 | 
				
			||||||
		"nprogress": "0.2.0",
 | 
							"nprogress": "0.2.0",
 | 
				
			||||||
		"object-assign-deep": "0.4.0",
 | 
							"object-assign-deep": "0.4.0",
 | 
				
			||||||
| 
						 | 
					@ -185,11 +183,12 @@
 | 
				
			||||||
		"showdown-highlightjs-extension": "0.1.2",
 | 
							"showdown-highlightjs-extension": "0.1.2",
 | 
				
			||||||
		"single-line-log": "1.1.2",
 | 
							"single-line-log": "1.1.2",
 | 
				
			||||||
		"speakeasy": "2.0.0",
 | 
							"speakeasy": "2.0.0",
 | 
				
			||||||
		"style-loader": "0.21.0",
 | 
							"stringz": "1.0.0",
 | 
				
			||||||
 | 
							"style-loader": "0.22.1",
 | 
				
			||||||
		"stylus": "0.54.5",
 | 
							"stylus": "0.54.5",
 | 
				
			||||||
		"stylus-loader": "3.0.2",
 | 
							"stylus-loader": "3.0.2",
 | 
				
			||||||
		"summaly": "2.0.6",
 | 
							"summaly": "2.1.2",
 | 
				
			||||||
		"systeminformation": "3.42.8",
 | 
							"systeminformation": "3.42.9",
 | 
				
			||||||
		"syuilo-password-strength": "0.0.1",
 | 
							"syuilo-password-strength": "0.0.1",
 | 
				
			||||||
		"textarea-caret": "3.1.0",
 | 
							"textarea-caret": "3.1.0",
 | 
				
			||||||
		"tmp": "0.0.33",
 | 
							"tmp": "0.0.33",
 | 
				
			||||||
| 
						 | 
					@ -197,7 +196,7 @@
 | 
				
			||||||
		"ts-node": "7.0.0",
 | 
							"ts-node": "7.0.0",
 | 
				
			||||||
		"tslint": "5.10.0",
 | 
							"tslint": "5.10.0",
 | 
				
			||||||
		"typescript": "2.9.2",
 | 
							"typescript": "2.9.2",
 | 
				
			||||||
		"typescript-eslint-parser": "17.0.1",
 | 
							"typescript-eslint-parser": "18.0.0",
 | 
				
			||||||
		"uglify-es": "3.3.9",
 | 
							"uglify-es": "3.3.9",
 | 
				
			||||||
		"url-loader": "1.0.1",
 | 
							"url-loader": "1.0.1",
 | 
				
			||||||
		"uuid": "3.3.2",
 | 
							"uuid": "3.3.2",
 | 
				
			||||||
| 
						 | 
					@ -206,7 +205,7 @@
 | 
				
			||||||
		"vue-cropperjs": "2.2.1",
 | 
							"vue-cropperjs": "2.2.1",
 | 
				
			||||||
		"vue-js-modal": "1.3.16",
 | 
							"vue-js-modal": "1.3.16",
 | 
				
			||||||
		"vue-json-tree-view": "2.1.4",
 | 
							"vue-json-tree-view": "2.1.4",
 | 
				
			||||||
		"vue-loader": "15.2.6",
 | 
							"vue-loader": "15.3.0",
 | 
				
			||||||
		"vue-router": "3.0.1",
 | 
							"vue-router": "3.0.1",
 | 
				
			||||||
		"vue-style-loader": "4.1.1",
 | 
							"vue-style-loader": "4.1.1",
 | 
				
			||||||
		"vue-template-compiler": "2.5.17",
 | 
							"vue-template-compiler": "2.5.17",
 | 
				
			||||||
| 
						 | 
					@ -215,7 +214,7 @@
 | 
				
			||||||
		"vuex-persistedstate": "2.5.4",
 | 
							"vuex-persistedstate": "2.5.4",
 | 
				
			||||||
		"web-push": "3.3.2",
 | 
							"web-push": "3.3.2",
 | 
				
			||||||
		"webfinger.js": "2.6.6",
 | 
							"webfinger.js": "2.6.6",
 | 
				
			||||||
		"webpack": "4.16.4",
 | 
							"webpack": "4.16.5",
 | 
				
			||||||
		"webpack-cli": "3.1.0",
 | 
							"webpack-cli": "3.1.0",
 | 
				
			||||||
		"websocket": "1.0.26",
 | 
							"websocket": "1.0.26",
 | 
				
			||||||
		"ws": "6.0.0",
 | 
							"ws": "6.0.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,14 +8,14 @@ import VueRouter from 'vue-router';
 | 
				
			||||||
import './style.styl';
 | 
					import './style.styl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import init from '../init';
 | 
					import init from '../init';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import Index from './views/index.vue';
 | 
					import Index from './views/index.vue';
 | 
				
			||||||
 | 
					import * as config from '../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * init
 | 
					 * init
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
init(launch => {
 | 
					init(launch => {
 | 
				
			||||||
	document.title = '%i18n:common.name% | %i18n:common.application-authorization%';
 | 
						document.title = `${config.name} | %i18n:common.application-authorization%`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Init router
 | 
						// Init router
 | 
				
			||||||
	const router = new VueRouter({
 | 
						const router = new VueRouter({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ export default function(type, data): Notification {
 | 
				
			||||||
			switch (data.type) {
 | 
								switch (data.type) {
 | 
				
			||||||
				case 'mention':
 | 
									case 'mention':
 | 
				
			||||||
					return {
 | 
										return {
 | 
				
			||||||
						title: '%i18n:common.notification.notified-by%'.split("{}")[0] + `${getUserName(data.user)}さんから:` + '%i18n:common.notification.notified-by%'.split("{}")[1],
 | 
											title: '%i18n:common.notification.notified-by%'.split("{}")[0] + `${getUserName(data.user)}:` + '%i18n:common.notification.notified-by%'.split("{}")[1],
 | 
				
			||||||
						body: getNoteSummary(data),
 | 
											body: getNoteSummary(data),
 | 
				
			||||||
						icon: data.user.avatarUrl
 | 
											icon: data.user.avatarUrl
 | 
				
			||||||
					};
 | 
										};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +0,0 @@
 | 
				
			||||||
export default date => {
 | 
					 | 
				
			||||||
	if (typeof date == 'string') date = new Date(date);
 | 
					 | 
				
			||||||
	return (
 | 
					 | 
				
			||||||
		date.getFullYear()    + '%i18n:common.date.full-year%' +
 | 
					 | 
				
			||||||
		(date.getMonth() + 1) + '%i18n:common.date.month%' +
 | 
					 | 
				
			||||||
		date.getDate()        + '%i18n:common.date.day%' +
 | 
					 | 
				
			||||||
		' ' +
 | 
					 | 
				
			||||||
		date.getHours()       + '%i18n:common.date.hours%' +
 | 
					 | 
				
			||||||
		date.getMinutes()     + '%i18n:common.date.minutes%' +
 | 
					 | 
				
			||||||
		' ' +
 | 
					 | 
				
			||||||
		`(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})`
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					@ -132,7 +132,7 @@ export default Vue.extend({
 | 
				
			||||||
					this.users = users;
 | 
										this.users = users;
 | 
				
			||||||
					this.fetching = false;
 | 
										this.fetching = false;
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					(this as any).api('users/search_by_username', {
 | 
										(this as any).api('users/search', {
 | 
				
			||||||
						query: this.q,
 | 
											query: this.q,
 | 
				
			||||||
						limit: 30
 | 
											limit: 30
 | 
				
			||||||
					}).then(users => {
 | 
										}).then(users => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
	<router-link class="mk-avatar" :to="user | userPage" :title="user | acct" :target="target" :style="style" v-if="disablePreview"></router-link>
 | 
						<span class="mk-avatar" :title="user | acct" :style="style" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick"></span>
 | 
				
			||||||
	<router-link class="mk-avatar" :to="user | userPage" :title="user | acct" :target="target" :style="style" v-else v-user-preview="user.id"></router-link>
 | 
						<span class="mk-avatar" :title="user | acct" :style="style" v-else-if="disableLink && disablePreview" @click="onClick"></span>
 | 
				
			||||||
 | 
						<router-link class="mk-avatar" :to="user | userPage" :title="user | acct" :target="target" :style="style" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id"></router-link>
 | 
				
			||||||
 | 
						<router-link class="mk-avatar" :to="user | userPage" :title="user | acct" :target="target" :style="style" v-else-if="!disableLink && disablePreview"></router-link>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
| 
						 | 
					@ -15,6 +17,10 @@ export default Vue.extend({
 | 
				
			||||||
			required: false,
 | 
								required: false,
 | 
				
			||||||
			default: null
 | 
								default: null
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							disableLink: {
 | 
				
			||||||
 | 
								required: false,
 | 
				
			||||||
 | 
								default: false
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		disablePreview: {
 | 
							disablePreview: {
 | 
				
			||||||
			required: false,
 | 
								required: false,
 | 
				
			||||||
			default: false
 | 
								default: false
 | 
				
			||||||
| 
						 | 
					@ -35,6 +41,11 @@ export default Vue.extend({
 | 
				
			||||||
				borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
 | 
									borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onClick(e) {
 | 
				
			||||||
 | 
								this.$emit('click', e);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,18 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
 | 
					<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
 | 
				
			||||||
	<header><b>{{ blackUser | userName }}</b>(%i18n:common.reversi.black%) vs <b>{{ whiteUser | userName }}</b>(%i18n:common.reversi.white%)</header>
 | 
						<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="!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="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>
 | 
							<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p>
 | 
				
			||||||
		<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
 | 
							<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
 | 
				
			||||||
		<p class="result" v-if="game.isEnded && logPos == logs.length">
 | 
							<p class="result" v-if="game.isEnded && logPos == logs.length">
 | 
				
			||||||
			<template v-if="game.winner">{{ '%i18n:common.reversi.won%'.replace('{}', $options.filters.userName(game.winner)) }}{{ game.settings.isLlotheo ? ' (ロセオ)' : '' }}</template>
 | 
								<template v-if="game.winner">
 | 
				
			||||||
 | 
									<span>{{ '%i18n:common.reversi.won%'.replace('{}', $options.filters.userName(game.winner)) }}</span>
 | 
				
			||||||
 | 
									<span v-if="game.surrendered != null"> (%i18n:@surrendered%)</span>
 | 
				
			||||||
 | 
								</template>
 | 
				
			||||||
			<template v-else>%i18n:common.reversi.drawn%</template>
 | 
								<template v-else>%i18n:common.reversi.drawn%</template>
 | 
				
			||||||
		</p>
 | 
							</p>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
| 
						 | 
					@ -41,6 +45,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<p class="status"><b>{{ '%i18n:common.reversi.this-turn%'.split('{}')[0] }}{{ logPos }}{{ '%i18n:common.reversi.this-turn%'.split('{}')[1] }}</b> %i18n:common.reversi.black%:{{ o.blackCount }} %i18n:common.reversi.white%:{{ o.whiteCount }} %i18n:common.reversi.total%:{{ o.blackCount + o.whiteCount }}</p>
 | 
						<p class="status"><b>{{ '%i18n:common.reversi.this-turn%'.split('{}')[0] }}{{ logPos }}{{ '%i18n:common.reversi.this-turn%'.split('{}')[1] }}</b> %i18n:common.reversi.black%:{{ o.blackCount }} %i18n:common.reversi.white%:{{ o.whiteCount }} %i18n:common.reversi.total%:{{ o.blackCount + o.whiteCount }}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class="actions" v-if="!game.isEnded && iAmPlayer">
 | 
				
			||||||
 | 
							<form-button @click="surrender">%i18n:@surrender%</form-button>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="player" v-if="game.isEnded">
 | 
						<div class="player" v-if="game.isEnded">
 | 
				
			||||||
		<el-button-group>
 | 
							<el-button-group>
 | 
				
			||||||
			<el-button type="primary" @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</el-button>
 | 
								<el-button type="primary" @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</el-button>
 | 
				
			||||||
| 
						 | 
					@ -62,7 +70,20 @@ import Reversi, { Color } from '../../../../../../../games/reversi/core';
 | 
				
			||||||
import { url } from '../../../../../config';
 | 
					import { url } from '../../../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: ['initGame', 'connection'],
 | 
						props: {
 | 
				
			||||||
 | 
							initGame: {
 | 
				
			||||||
 | 
								type: Object,
 | 
				
			||||||
 | 
								require: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							connection: {
 | 
				
			||||||
 | 
								type: Object,
 | 
				
			||||||
 | 
								require: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							selfNav: {
 | 
				
			||||||
 | 
								type: Boolean,
 | 
				
			||||||
 | 
								require: true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
| 
						 | 
					@ -79,22 +100,27 @@ export default Vue.extend({
 | 
				
			||||||
			if (!this.$store.getters.isSignedIn) return false;
 | 
								if (!this.$store.getters.isSignedIn) return false;
 | 
				
			||||||
			return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id;
 | 
								return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		myColor(): Color {
 | 
							myColor(): Color {
 | 
				
			||||||
			if (!this.iAmPlayer) return null;
 | 
								if (!this.iAmPlayer) return null;
 | 
				
			||||||
			if (this.game.user1Id == this.$store.state.i.id && this.game.black == 1) return true;
 | 
								if (this.game.user1Id == this.$store.state.i.id && this.game.black == 1) return true;
 | 
				
			||||||
			if (this.game.user2Id == this.$store.state.i.id && this.game.black == 2) return true;
 | 
								if (this.game.user2Id == this.$store.state.i.id && this.game.black == 2) return true;
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		opColor(): Color {
 | 
							opColor(): Color {
 | 
				
			||||||
			if (!this.iAmPlayer) return null;
 | 
								if (!this.iAmPlayer) return null;
 | 
				
			||||||
			return this.myColor === true ? false : true;
 | 
								return this.myColor === true ? false : true;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		blackUser(): any {
 | 
							blackUser(): any {
 | 
				
			||||||
			return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
								return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		whiteUser(): any {
 | 
							whiteUser(): any {
 | 
				
			||||||
			return this.game.black == 1 ? this.game.user2 : this.game.user1;
 | 
								return this.game.black == 1 ? this.game.user2 : this.game.user1;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		turnUser(): any {
 | 
							turnUser(): any {
 | 
				
			||||||
			if (this.o.turn === true) {
 | 
								if (this.o.turn === true) {
 | 
				
			||||||
				return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
									return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
				
			||||||
| 
						 | 
					@ -104,11 +130,13 @@ export default Vue.extend({
 | 
				
			||||||
				return null;
 | 
									return null;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		isMyTurn(): boolean {
 | 
							isMyTurn(): boolean {
 | 
				
			||||||
			if (!this.iAmPlayer) return false;
 | 
								if (!this.iAmPlayer) return false;
 | 
				
			||||||
			if (this.turnUser == null) return false;
 | 
								if (this.turnUser == null) return false;
 | 
				
			||||||
			return this.turnUser.id == this.$store.state.i.id;
 | 
								return this.turnUser.id == this.$store.state.i.id;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cellsStyle(): any {
 | 
							cellsStyle(): any {
 | 
				
			||||||
			return {
 | 
								return {
 | 
				
			||||||
				'grid-template-rows': `repeat(${this.game.settings.map.length}, 1fr)`,
 | 
									'grid-template-rows': `repeat(${this.game.settings.map.length}, 1fr)`,
 | 
				
			||||||
| 
						 | 
					@ -165,11 +193,13 @@ export default Vue.extend({
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		this.connection.on('set', this.onSet);
 | 
							this.connection.on('set', this.onSet);
 | 
				
			||||||
		this.connection.on('rescue', this.onRescue);
 | 
							this.connection.on('rescue', this.onRescue);
 | 
				
			||||||
 | 
							this.connection.on('ended', this.onEnded);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	beforeDestroy() {
 | 
						beforeDestroy() {
 | 
				
			||||||
		this.connection.off('set', this.onSet);
 | 
							this.connection.off('set', this.onSet);
 | 
				
			||||||
		this.connection.off('rescue', this.onRescue);
 | 
							this.connection.off('rescue', this.onRescue);
 | 
				
			||||||
 | 
							this.connection.off('ended', this.onEnded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clearInterval(this.pollingClock);
 | 
							clearInterval(this.pollingClock);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					@ -215,6 +245,10 @@ export default Vue.extend({
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onEnded(x) {
 | 
				
			||||||
 | 
								this.game = x.game;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		checkEnd() {
 | 
							checkEnd() {
 | 
				
			||||||
			this.game.isEnded = this.o.isEnded;
 | 
								this.game.isEnded = this.o.isEnded;
 | 
				
			||||||
			if (this.game.isEnded) {
 | 
								if (this.game.isEnded) {
 | 
				
			||||||
| 
						 | 
					@ -250,6 +284,16 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this.checkEnd();
 | 
								this.checkEnd();
 | 
				
			||||||
			this.$forceUpdate();
 | 
								this.$forceUpdate();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							surrender() {
 | 
				
			||||||
 | 
								(this as any).api('games/reversi/games/surrender', {
 | 
				
			||||||
 | 
									gameId: this.game.id
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							goIndex() {
 | 
				
			||||||
 | 
								this.$emit('go-index');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -261,10 +305,21 @@ export default Vue.extend({
 | 
				
			||||||
root(isDark)
 | 
					root(isDark)
 | 
				
			||||||
	text-align center
 | 
						text-align center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .go-index
 | 
				
			||||||
 | 
							position absolute
 | 
				
			||||||
 | 
							top 0
 | 
				
			||||||
 | 
							left 0
 | 
				
			||||||
 | 
							z-index 1
 | 
				
			||||||
 | 
							width 42px
 | 
				
			||||||
 | 
							height 42px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> header
 | 
						> header
 | 
				
			||||||
		padding 8px
 | 
							padding 8px
 | 
				
			||||||
		border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4
 | 
							border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							a
 | 
				
			||||||
 | 
								color inherit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .board
 | 
						> .board
 | 
				
			||||||
		width calc(100% - 16px)
 | 
							width calc(100% - 16px)
 | 
				
			||||||
		max-width 500px
 | 
							max-width 500px
 | 
				
			||||||
| 
						 | 
					@ -381,6 +436,9 @@ root(isDark)
 | 
				
			||||||
		margin 0
 | 
							margin 0
 | 
				
			||||||
		padding 16px 0
 | 
							padding 16px 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .actions
 | 
				
			||||||
 | 
							padding-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .player
 | 
						> .player
 | 
				
			||||||
		padding-bottom 32px
 | 
							padding-bottom 32px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<x-room v-if="!g.isStarted" :game="g" :connection="connection"/>
 | 
						<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>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,16 @@ export default Vue.extend({
 | 
				
			||||||
		XGame,
 | 
							XGame,
 | 
				
			||||||
		XRoom
 | 
							XRoom
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	props: ['game'],
 | 
						props: {
 | 
				
			||||||
 | 
							game: {
 | 
				
			||||||
 | 
								type: Object,
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							selfNav: {
 | 
				
			||||||
 | 
								type: Boolean,
 | 
				
			||||||
 | 
								require: true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
| 
						 | 
					@ -36,6 +45,9 @@ export default Vue.extend({
 | 
				
			||||||
		onStarted(game) {
 | 
							onStarted(game) {
 | 
				
			||||||
			Object.assign(this.g, game);
 | 
								Object.assign(this.g, game);
 | 
				
			||||||
			this.$forceUpdate();
 | 
								this.$forceUpdate();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							goIndex() {
 | 
				
			||||||
 | 
								this.$emit('go-index');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,11 +96,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		go(game) {
 | 
							go(game) {
 | 
				
			||||||
			(this as any).api('games/reversi/games/show', {
 | 
								this.$emit('go', game);
 | 
				
			||||||
				gameId: game.id
 | 
					 | 
				
			||||||
			}).then(game => {
 | 
					 | 
				
			||||||
				this.$emit('go', game);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		match() {
 | 
							match() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="card" v-if="form">
 | 
							<div class="card form" v-if="form">
 | 
				
			||||||
			<header>
 | 
								<header>
 | 
				
			||||||
				<span>%i18n:@settings-of-the-bot%</span>
 | 
									<span>%i18n:@settings-of-the-bot%</span>
 | 
				
			||||||
			</header>
 | 
								</header>
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,7 @@
 | 
				
			||||||
						:key="message.id"/>
 | 
											:key="message.id"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<template v-for="item in form">
 | 
									<template v-for="item in form">
 | 
				
			||||||
					<mk-switch v-if="item.type == 'button'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm($event, item)">{{ item.desc || '' }}</mk-switch>
 | 
										<mk-switch v-if="item.type == 'switch'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm(item)">{{ item.desc || '' }}</mk-switch>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<div class="card" v-if="item.type == 'radio'" :key="item.id">
 | 
										<div class="card" v-if="item.type == 'radio'" :key="item.id">
 | 
				
			||||||
						<header>
 | 
											<header>
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,17 @@
 | 
				
			||||||
						</header>
 | 
											</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						<div>
 | 
											<div>
 | 
				
			||||||
							<el-radio v-for="(r, i) in item.items" :key="item.id + ':' + i" v-model="item.value" :label="r.value" @change="onChangeForm($event, item)">{{ r.label }}</el-radio>
 | 
												<form-radio v-for="(r, i) in item.items" :key="item.id + ':' + i" v-model="item.value" :value="r.value" @change="onChangeForm(item)">{{ r.label }}</form-radio>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<div class="card" v-if="item.type == 'slider'" :key="item.id">
 | 
				
			||||||
 | 
											<header>
 | 
				
			||||||
 | 
												<span>{{ item.label }}</span>
 | 
				
			||||||
 | 
											</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<div>
 | 
				
			||||||
 | 
												<input type="range" :min="item.min" :max="item.max" :step="item.step || 1" v-model="item.value" @change="onChangeForm(item)"/>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,7 +93,7 @@
 | 
				
			||||||
						</header>
 | 
											</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						<div>
 | 
											<div>
 | 
				
			||||||
							<el-input v-model="item.value" @change="onChangeForm($event, item)"/>
 | 
												<el-input v-model="item.value" @change="onChangeForm(item)"/>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</template>
 | 
									</template>
 | 
				
			||||||
| 
						 | 
					@ -93,7 +103,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<footer>
 | 
						<footer>
 | 
				
			||||||
		<p class="status">
 | 
							<p class="status">
 | 
				
			||||||
			<template v-if="isAccepted && isOpAccepted">%i18n:@this-gane-is-started-soon%<mk-ellipsis/></template>
 | 
								<template v-if="isAccepted && isOpAccepted">%i18n:@this-game-is-started-soon%<mk-ellipsis/></template>
 | 
				
			||||||
			<template v-if="isAccepted && !isOpAccepted">%i18n:@waiting-for-other%<mk-ellipsis/></template>
 | 
								<template v-if="isAccepted && !isOpAccepted">%i18n:@waiting-for-other%<mk-ellipsis/></template>
 | 
				
			||||||
			<template v-if="!isAccepted && isOpAccepted">%i18n:@waiting-for-me%</template>
 | 
								<template v-if="!isAccepted && isOpAccepted">%i18n:@waiting-for-me%</template>
 | 
				
			||||||
			<template v-if="!isAccepted && !isOpAccepted">%i18n:@waiting-for-both%<mk-ellipsis/></template>
 | 
								<template v-if="!isAccepted && !isOpAccepted">%i18n:@waiting-for-both%<mk-ellipsis/></template>
 | 
				
			||||||
| 
						 | 
					@ -210,11 +220,11 @@ export default Vue.extend({
 | 
				
			||||||
			this.messages.unshift(x.message);
 | 
								this.messages.unshift(x.message);
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onChangeForm(v, item) {
 | 
							onChangeForm(item) {
 | 
				
			||||||
			this.connection.send({
 | 
								this.connection.send({
 | 
				
			||||||
				type: 'update-form',
 | 
									type: 'update-form',
 | 
				
			||||||
				id: item.id,
 | 
									id: item.id,
 | 
				
			||||||
				value: v
 | 
									value: item.value
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -274,6 +284,9 @@ root(isDark)
 | 
				
			||||||
						color isDark ? #fff : #606266
 | 
											color isDark ? #fff : #606266
 | 
				
			||||||
						cursor pointer
 | 
											cursor pointer
 | 
				
			||||||
						transition border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1)
 | 
											transition border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1)
 | 
				
			||||||
 | 
											-webkit-appearance none
 | 
				
			||||||
 | 
											-moz-appearance none
 | 
				
			||||||
 | 
											appearance none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						&:hover
 | 
											&:hover
 | 
				
			||||||
							border-color isDark ? #a7aebd : #c0c4cc
 | 
												border-color isDark ? #a7aebd : #c0c4cc
 | 
				
			||||||
| 
						 | 
					@ -312,12 +325,20 @@ root(isDark)
 | 
				
			||||||
							&[data-none]
 | 
												&[data-none]
 | 
				
			||||||
								border-color transparent
 | 
													border-color transparent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								&.form
 | 
				
			||||||
 | 
									> div
 | 
				
			||||||
 | 
										> .card + .card
 | 
				
			||||||
 | 
											margin-top 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										input[type='range']
 | 
				
			||||||
 | 
											width 100%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		.card
 | 
							.card
 | 
				
			||||||
			max-width 400px
 | 
								max-width 400px
 | 
				
			||||||
			border-radius 4px
 | 
								border-radius 4px
 | 
				
			||||||
			background isDark ? #282C37 : #fff
 | 
								background isDark ? #282C37 : #fff
 | 
				
			||||||
			color isDark ? #fff : #303133
 | 
								color isDark ? #fff : #303133
 | 
				
			||||||
			box-shadow 0 2px 12px 0 rgba(#000, 0.1)
 | 
								box-shadow 0 2px 12px 0 rgba(#000, isDark ? 0.7 : 0.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			> header
 | 
								> header
 | 
				
			||||||
				padding 18px 20px
 | 
									padding 18px 20px
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="vchtoekanapleubgzioubdtmlkribzfd">
 | 
					<div class="vchtoekanapleubgzioubdtmlkribzfd">
 | 
				
			||||||
	<div v-if="game">
 | 
						<div v-if="game">
 | 
				
			||||||
		<x-gameroom :game="game"/>
 | 
							<x-gameroom :game="game" :self-nav="selfNav" @go-index="goIndex"/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="matching" v-else-if="matching">
 | 
						<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>
 | 
							<h1>{{ '%i18n:@matching.waiting-for%'.split('{}')[0] }}<b>{{ matching | userName }}</b>{{ '%i18n:@matching.waiting-for%'.split('{}')[1] }}<mk-ellipsis/></h1>
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,9 @@
 | 
				
			||||||
			<form-button round @click="cancel">%i18n:@matching.cancel%</form-button>
 | 
								<form-button round @click="cancel">%i18n:@matching.cancel%</form-button>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
						<div v-else-if="gameId">
 | 
				
			||||||
 | 
							...
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
	<div class="index" v-else>
 | 
						<div class="index" v-else>
 | 
				
			||||||
		<x-index @go="nav" @matching="onMatching"/>
 | 
							<x-index @go="nav" @matching="onMatching"/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
| 
						 | 
					@ -31,6 +34,11 @@ export default Vue.extend({
 | 
				
			||||||
		gameId: {
 | 
							gameId: {
 | 
				
			||||||
			type: String,
 | 
								type: String,
 | 
				
			||||||
			required: false
 | 
								required: false
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							selfNav: {
 | 
				
			||||||
 | 
								type: Boolean,
 | 
				
			||||||
 | 
								require: false,
 | 
				
			||||||
 | 
								default: true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,22 +53,18 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	watch: {
 | 
						watch: {
 | 
				
			||||||
		gameId(id) {
 | 
							game() {
 | 
				
			||||||
			if (id == null) {
 | 
								this.$emit('gamed', this.game);
 | 
				
			||||||
				this.game = null;
 | 
							},
 | 
				
			||||||
			} else {
 | 
					
 | 
				
			||||||
				Progress.start();
 | 
							gameId() {
 | 
				
			||||||
				(this as any).api('games/reversi/games/show', {
 | 
								this.fetch();
 | 
				
			||||||
					gameId: id
 | 
					 | 
				
			||||||
				}).then(game => {
 | 
					 | 
				
			||||||
					this.nav(game, true);
 | 
					 | 
				
			||||||
					Progress.done();
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
 | 
							this.fetch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (this.$store.getters.isSignedIn) {
 | 
							if (this.$store.getters.isSignedIn) {
 | 
				
			||||||
			this.connection = (this as any).os.streams.reversiStream.getConnection();
 | 
								this.connection = (this as any).os.streams.reversiStream.getConnection();
 | 
				
			||||||
			this.connectionId = (this as any).os.streams.reversiStream.use();
 | 
								this.connectionId = (this as any).os.streams.reversiStream.use();
 | 
				
			||||||
| 
						 | 
					@ -88,12 +92,32 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		nav(game, silent) {
 | 
							fetch() {
 | 
				
			||||||
			this.matching = null;
 | 
								if (this.gameId == null) {
 | 
				
			||||||
			this.game = game;
 | 
									this.game = null;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									Progress.start();
 | 
				
			||||||
 | 
									(this as any).api('games/reversi/games/show', {
 | 
				
			||||||
 | 
										gameId: this.gameId
 | 
				
			||||||
 | 
									}).then(game => {
 | 
				
			||||||
 | 
										this.game = game;
 | 
				
			||||||
 | 
										Progress.done();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!silent) {
 | 
							async nav(game, actualNav = true) {
 | 
				
			||||||
				this.$emit('nav', this.game);
 | 
								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
 | 
				
			||||||
 | 
										});
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.game = game;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									this.$emit('nav', game, actualNav);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,7 +136,8 @@ export default Vue.extend({
 | 
				
			||||||
			}).then(game => {
 | 
								}).then(game => {
 | 
				
			||||||
				if (game) {
 | 
									if (game) {
 | 
				
			||||||
					this.matching = null;
 | 
										this.matching = null;
 | 
				
			||||||
					this.game = game;
 | 
					
 | 
				
			||||||
 | 
										this.nav(game);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					@ -120,6 +145,11 @@ export default Vue.extend({
 | 
				
			||||||
		onMatched(game) {
 | 
							onMatched(game) {
 | 
				
			||||||
			this.matching = null;
 | 
								this.matching = null;
 | 
				
			||||||
			this.game = game;
 | 
								this.game = game;
 | 
				
			||||||
 | 
								this.nav(game, false);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							goIndex() {
 | 
				
			||||||
 | 
								this.nav(null);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,7 +83,7 @@ export default Vue.extend({
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				if (items[0].kind == 'file') {
 | 
									if (items[0].kind == 'file') {
 | 
				
			||||||
					alert('メッセージに添付できるのはひとつのファイルのみです');
 | 
										alert('%i18n:only-one-file-attached%');
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					@ -105,7 +105,7 @@ export default Vue.extend({
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			} else if (e.dataTransfer.files.length > 1) {
 | 
								} else if (e.dataTransfer.files.length > 1) {
 | 
				
			||||||
				e.preventDefault();
 | 
									e.preventDefault();
 | 
				
			||||||
				alert('メッセージに添付できるのはひとつのファイルのみです');
 | 
									alert('%i18n:only-one-file-attached%');
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(message.createdAt).getDate();
 | 
									const date = new Date(message.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(message.createdAt).getMonth() + 1;
 | 
									const month = new Date(message.createdAt).getMonth() + 1;
 | 
				
			||||||
				message._date = date;
 | 
									message._date = date;
 | 
				
			||||||
				message._datetext = `${month}月 ${date}日`;
 | 
									message._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return message;
 | 
									return message;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					@ -111,7 +111,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.form.upload(e.dataTransfer.files[0]);
 | 
									this.form.upload(e.dataTransfer.files[0]);
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			} else if (e.dataTransfer.files.length > 1) {
 | 
								} else if (e.dataTransfer.files.length > 1) {
 | 
				
			||||||
				alert('メッセージに添付できるのはひとつのファイルのみです');
 | 
									alert('%i18n:@only-one-file-attached%');
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import * as emojilib from 'emojilib';
 | 
					import * as emojilib from 'emojilib';
 | 
				
			||||||
 | 
					import { length } from 'stringz';
 | 
				
			||||||
import parse from '../../../../../mfm/parse';
 | 
					import parse from '../../../../../mfm/parse';
 | 
				
			||||||
import getAcct from '../../../../../misc/acct/render';
 | 
					import getAcct from '../../../../../misc/acct/render';
 | 
				
			||||||
import { url } from '../../../config';
 | 
					import { url } from '../../../config';
 | 
				
			||||||
| 
						 | 
					@ -40,10 +41,13 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			||||||
			ast = this.ast;
 | 
								ast = this.ast;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let bigCount = 0;
 | 
				
			||||||
 | 
							let motionCount = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Parse ast to DOM
 | 
							// Parse ast to DOM
 | 
				
			||||||
		const els = flatten(ast.map(token => {
 | 
							const els = flatten(ast.map(token => {
 | 
				
			||||||
			switch (token.type) {
 | 
								switch (token.type) {
 | 
				
			||||||
				case 'text':
 | 
									case 'text': {
 | 
				
			||||||
					const text = token.content.replace(/(\r\n|\n|\r)/g, '\n');
 | 
										const text = token.content.replace(/(\r\n|\n|\r)/g, '\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (this.shouldBreak) {
 | 
										if (this.shouldBreak) {
 | 
				
			||||||
| 
						 | 
					@ -54,19 +58,52 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						return createElement('span', text.replace(/\n/g, ' '));
 | 
											return createElement('span', text.replace(/\n/g, ' '));
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'bold':
 | 
									case 'bold': {
 | 
				
			||||||
					return createElement('strong', token.bold);
 | 
										return createElement('b', token.bold);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'url':
 | 
									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: ${ isMany ? '100%' : '150%' };`
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											directives: [this.$store.state.settings.disableAnimatedMfm || isLong || isMany ? {} : {
 | 
				
			||||||
 | 
												name: 'animate-css',
 | 
				
			||||||
 | 
												value: { classes: 'tada', iteration: 'infinite' }
 | 
				
			||||||
 | 
											}]
 | 
				
			||||||
 | 
										}, token.big);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									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, {
 | 
										return createElement(MkUrl, {
 | 
				
			||||||
						props: {
 | 
											props: {
 | 
				
			||||||
							url: token.content,
 | 
												url: token.content,
 | 
				
			||||||
							target: '_blank'
 | 
												target: '_blank'
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'link':
 | 
									case 'link': {
 | 
				
			||||||
					return createElement('a', {
 | 
										return createElement('a', {
 | 
				
			||||||
						attrs: {
 | 
											attrs: {
 | 
				
			||||||
							class: 'link',
 | 
												class: 'link',
 | 
				
			||||||
| 
						 | 
					@ -75,8 +112,9 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			||||||
							title: token.url
 | 
												title: token.url
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}, token.title);
 | 
										}, token.title);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'mention':
 | 
									case 'mention': {
 | 
				
			||||||
					return (createElement as any)('a', {
 | 
										return (createElement as any)('a', {
 | 
				
			||||||
						attrs: {
 | 
											attrs: {
 | 
				
			||||||
							href: `${url}/@${getAcct(token)}`,
 | 
												href: `${url}/@${getAcct(token)}`,
 | 
				
			||||||
| 
						 | 
					@ -88,16 +126,18 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			||||||
							value: token.content
 | 
												value: token.content
 | 
				
			||||||
						}]
 | 
											}]
 | 
				
			||||||
					}, token.content);
 | 
										}, token.content);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'hashtag':
 | 
									case 'hashtag': {
 | 
				
			||||||
					return createElement('a', {
 | 
										return createElement('a', {
 | 
				
			||||||
						attrs: {
 | 
											attrs: {
 | 
				
			||||||
							href: `${url}/tags/${encodeURIComponent(token.hashtag)}`,
 | 
												href: `${url}/tags/${encodeURIComponent(token.hashtag)}`,
 | 
				
			||||||
							target: '_blank'
 | 
												target: '_blank'
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}, token.content);
 | 
										}, token.content);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'code':
 | 
									case 'code': {
 | 
				
			||||||
					return createElement('pre', {
 | 
										return createElement('pre', {
 | 
				
			||||||
						class: 'code'
 | 
											class: 'code'
 | 
				
			||||||
					}, [
 | 
										}, [
 | 
				
			||||||
| 
						 | 
					@ -107,15 +147,17 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
					]);
 | 
										]);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'inline-code':
 | 
									case 'inline-code': {
 | 
				
			||||||
					return createElement('code', {
 | 
										return createElement('code', {
 | 
				
			||||||
						domProps: {
 | 
											domProps: {
 | 
				
			||||||
							innerHTML: token.html
 | 
												innerHTML: token.html
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'quote':
 | 
									case 'quote': {
 | 
				
			||||||
					const text2 = token.quote.replace(/(\r\n|\n|\r)/g, '\n');
 | 
										const text2 = token.quote.replace(/(\r\n|\n|\r)/g, '\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (this.shouldBreak) {
 | 
										if (this.shouldBreak) {
 | 
				
			||||||
| 
						 | 
					@ -134,27 +176,32 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						}, text2.replace(/\n/g, ' '));
 | 
											}, text2.replace(/\n/g, ' '));
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'title':
 | 
									case 'title': {
 | 
				
			||||||
					return createElement('div', {
 | 
										return createElement('div', {
 | 
				
			||||||
						attrs: {
 | 
											attrs: {
 | 
				
			||||||
							class: 'title'
 | 
												class: 'title'
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}, token.title);
 | 
										}, token.title);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'emoji':
 | 
									case 'emoji': {
 | 
				
			||||||
					const emoji = emojilib.lib[token.emoji];
 | 
										const emoji = emojilib.lib[token.emoji];
 | 
				
			||||||
					return createElement('span', emoji ? emoji.char : token.content);
 | 
										return createElement('span', emoji ? emoji.char : token.content);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'search':
 | 
									case 'search': {
 | 
				
			||||||
					return createElement(MkGoogle, {
 | 
										return createElement(MkGoogle, {
 | 
				
			||||||
						props: {
 | 
											props: {
 | 
				
			||||||
							q: token.query
 | 
												q: token.query
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				default:
 | 
									default: {
 | 
				
			||||||
					console.log('unknown ast type:', token.type);
 | 
										console.log('unknown ast type:', token.type);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}));
 | 
							}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@
 | 
				
			||||||
	</ui-input>
 | 
						</ui-input>
 | 
				
			||||||
	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
 | 
						<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
 | 
				
			||||||
	<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
 | 
						<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
 | 
				
			||||||
	<p style="margin: 8px 0;">または<a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a></p>
 | 
						<p style="margin: 8px 0;">%i18n:@or%<a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
 | 
				
			||||||
</form>
 | 
					</form>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,6 +45,9 @@ root(isDark)
 | 
				
			||||||
		color isDark ? #fff : #606266
 | 
							color isDark ? #fff : #606266
 | 
				
			||||||
		transition 0.1s
 | 
							transition 0.1s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*
 | 
				
			||||||
 | 
								pointer-events none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&:hover
 | 
							&:hover
 | 
				
			||||||
		&:focus
 | 
							&:focus
 | 
				
			||||||
			color $theme-color
 | 
								color $theme-color
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,12 @@
 | 
				
			||||||
<iframe v-if="youtubeId" type="text/html" height="250"
 | 
					<iframe v-if="youtubeId" type="text/html" height="250"
 | 
				
			||||||
	:src="`https://www.youtube.com/embed/${youtubeId}?origin=${misskeyUrl}`"
 | 
						:src="`https://www.youtube.com/embed/${youtubeId}?origin=${misskeyUrl}`"
 | 
				
			||||||
	frameborder="0"/>
 | 
						frameborder="0"/>
 | 
				
			||||||
 | 
					<iframe v-else-if="spotifyId"
 | 
				
			||||||
 | 
						:src="`https://open.spotify.com/embed/track/${spotifyId}`"
 | 
				
			||||||
 | 
						frameborder="0" allowtransparency="true" allow="encrypted-media" />
 | 
				
			||||||
 | 
					<iframe v-else-if="nicovideoId"
 | 
				
			||||||
 | 
						:src="`https://embed.nicovideo.jp/watch/${nicovideoId}?oldScript=1&referer=${misskeyUrl}&from=${position || '0'}&allowProgrammaticFullScreen=1`"
 | 
				
			||||||
 | 
						frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
 | 
				
			||||||
<div v-else-if="tweetUrl && detail" class="twitter">
 | 
					<div v-else-if="tweetUrl && detail" class="twitter">
 | 
				
			||||||
	<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
 | 
						<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
 | 
				
			||||||
		<a :href="url"></a>
 | 
							<a :href="url"></a>
 | 
				
			||||||
| 
						 | 
					@ -49,6 +55,9 @@ export default Vue.extend({
 | 
				
			||||||
			icon: null,
 | 
								icon: null,
 | 
				
			||||||
			sitename: null,
 | 
								sitename: null,
 | 
				
			||||||
			youtubeId: null,
 | 
								youtubeId: null,
 | 
				
			||||||
 | 
								spotifyId: null,
 | 
				
			||||||
 | 
								nicovideoId: null,
 | 
				
			||||||
 | 
								position: null,
 | 
				
			||||||
			tweetUrl: null,
 | 
								tweetUrl: null,
 | 
				
			||||||
			misskeyUrl
 | 
								misskeyUrl
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
| 
						 | 
					@ -58,8 +67,20 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (url.hostname == 'www.youtube.com') {
 | 
							if (url.hostname == 'www.youtube.com') {
 | 
				
			||||||
			this.youtubeId = url.searchParams.get('v');
 | 
								this.youtubeId = url.searchParams.get('v');
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
		} else if (url.hostname == 'youtu.be') {
 | 
							} else if (url.hostname == 'youtu.be') {
 | 
				
			||||||
			this.youtubeId = url.pathname;
 | 
								this.youtubeId = url.pathname;
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							} else if (url.hostname == 'open.spotify.com') {
 | 
				
			||||||
 | 
								this.spotifyId = url.pathname.split('/').reverse().filter(x => x !== '')[0];
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							} else if (['nicovideo.jp', 'www.nicovideo.jp', 'nico.ms'].includes(url.hostname)) {
 | 
				
			||||||
 | 
								const id = url.pathname.split('/').reverse().filter(x => x !== '')[0];
 | 
				
			||||||
 | 
								if (['sm', 'nm', 'ax', 'ca', 'cd', 'cw', 'fx', 'ig', 'na', 'om', 'sd', 'sk', 'yk', 'yo', 'za', 'zb', 'zc', 'zd', 'ze', 'nl', 'so', ...Array(10).keys()].some(x => id.startsWith(x)) {
 | 
				
			||||||
 | 
									this.nicovideoId = id;
 | 
				
			||||||
 | 
									this.position = url.searchParams.get('from');
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} else if (this.detail && url.hostname == 'twitter.com' && /^\/.+\/status(es)?\/\d+/.test(url.pathname)) {
 | 
							} else if (this.detail && url.hostname == 'twitter.com' && /^\/.+\/status(es)?\/\d+/.test(url.pathname)) {
 | 
				
			||||||
			this.tweetUrl = url;
 | 
								this.tweetUrl = url;
 | 
				
			||||||
			const twttr = (window as any).twttr || {};
 | 
								const twttr = (window as any).twttr || {};
 | 
				
			||||||
| 
						 | 
					@ -79,19 +100,19 @@ export default Vue.extend({
 | 
				
			||||||
				twttr.ready = loadTweet;
 | 
									twttr.ready = loadTweet;
 | 
				
			||||||
				(window as any).twttr = twttr;
 | 
									(window as any).twttr = twttr;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
								return;
 | 
				
			||||||
			fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
 | 
					 | 
				
			||||||
				res.json().then(info => {
 | 
					 | 
				
			||||||
					this.title = info.title;
 | 
					 | 
				
			||||||
					this.description = info.description;
 | 
					 | 
				
			||||||
					this.thumbnail = info.thumbnail;
 | 
					 | 
				
			||||||
					this.icon = info.icon;
 | 
					 | 
				
			||||||
					this.sitename = info.sitename;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					this.fetching = false;
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
 | 
				
			||||||
 | 
								res.json().then(info => {
 | 
				
			||||||
 | 
									this.title = info.title;
 | 
				
			||||||
 | 
									this.description = info.description;
 | 
				
			||||||
 | 
									this.thumbnail = info.thumbnail;
 | 
				
			||||||
 | 
									this.icon = info.icon;
 | 
				
			||||||
 | 
									this.sitename = info.sitename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,6 @@ export default Vue.extend({
 | 
				
			||||||
				this.user = user;
 | 
									this.user = user;
 | 
				
			||||||
				this.fetching = false;
 | 
									this.fetching = false;
 | 
				
			||||||
				Progress.done();
 | 
									Progress.done();
 | 
				
			||||||
				document.title = getUserName(this.user) + ' | %i18n:common.name%';
 | 
					 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
		<p>%fa:R hdd%Storage</p>
 | 
							<p>%fa:R hdd%Storage</p>
 | 
				
			||||||
		<p>Total: {{ total | bytes(1) }}</p>
 | 
							<p>Total: {{ total | bytes(1) }}</p>
 | 
				
			||||||
		<p>Available: {{ available | bytes(1) }}</p>
 | 
							<p>Free: {{ available | bytes(1) }}</p>
 | 
				
			||||||
		<p>Used: {{ used | bytes(1) }}</p>
 | 
							<p>Used: {{ used | bytes(1) }}</p>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ export default (os: OS) => (cb, file = null) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const w = os.new(CropWindow, {
 | 
							const w = os.new(CropWindow, {
 | 
				
			||||||
			image: file,
 | 
								image: file,
 | 
				
			||||||
			title: 'アバターとして表示する部分を選択',
 | 
								title: '%i18n:desktop.avatar-crop-title%',
 | 
				
			||||||
			aspectRatio: 1 / 1
 | 
								aspectRatio: 1 / 1
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,11 +18,11 @@ export default (os: OS) => (cb, file = null) => {
 | 
				
			||||||
			data.append('file', blob, file.name + '.cropped.png');
 | 
								data.append('file', blob, file.name + '.cropped.png');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			os.api('drive/folders/find', {
 | 
								os.api('drive/folders/find', {
 | 
				
			||||||
				name: 'アイコン'
 | 
									name: '%i18n:desktop.avatar%'
 | 
				
			||||||
			}).then(iconFolder => {
 | 
								}).then(iconFolder => {
 | 
				
			||||||
				if (iconFolder.length === 0) {
 | 
									if (iconFolder.length === 0) {
 | 
				
			||||||
					os.api('drive/folders/create', {
 | 
										os.api('drive/folders/create', {
 | 
				
			||||||
						name: 'アイコン'
 | 
											name: '%i18n:desktop.avatar%'
 | 
				
			||||||
					}).then(iconFolder => {
 | 
										}).then(iconFolder => {
 | 
				
			||||||
						upload(data, iconFolder);
 | 
											upload(data, iconFolder);
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@ export default (os: OS) => (cb, file = null) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const upload = (data, folder) => {
 | 
						const upload = (data, folder) => {
 | 
				
			||||||
		const dialog = os.new(ProgressDialog, {
 | 
							const dialog = os.new(ProgressDialog, {
 | 
				
			||||||
			title: '新しいアバターをアップロードしています'
 | 
								title: '%i18n:desktop.uploading-avatar%'
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		document.body.appendChild(dialog.$el);
 | 
							document.body.appendChild(dialog.$el);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,10 +76,10 @@ export default (os: OS) => (cb, file = null) => {
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			os.apis.dialog({
 | 
								os.apis.dialog({
 | 
				
			||||||
				title: '%fa:info-circle%アバターを更新しました',
 | 
									title: '%fa:info-circle% %i18n:desktop.avatar-updated%',
 | 
				
			||||||
				text: '新しいアバターが反映されるまで時間がかかる場合があります。',
 | 
									text: null,
 | 
				
			||||||
				actions: [{
 | 
									actions: [{
 | 
				
			||||||
					text: 'わかった'
 | 
										text: '%i18n:common.got-it%'
 | 
				
			||||||
				}]
 | 
									}]
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,7 +92,7 @@ export default (os: OS) => (cb, file = null) => {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		os.apis.chooseDriveFile({
 | 
							os.apis.chooseDriveFile({
 | 
				
			||||||
			multiple: false,
 | 
								multiple: false,
 | 
				
			||||||
			title: '%fa:image%アバターにする画像を選択'
 | 
								title: '%fa:image% %i18n:desktop.choose-avatar%'
 | 
				
			||||||
		}).then(file => {
 | 
							}).then(file => {
 | 
				
			||||||
			fileSelected(file);
 | 
								fileSelected(file);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ export default (os: OS) => {
 | 
				
			||||||
	const cropImage = file => new Promise((resolve, reject) => {
 | 
						const cropImage = file => new Promise((resolve, reject) => {
 | 
				
			||||||
		const w = os.new(CropWindow, {
 | 
							const w = os.new(CropWindow, {
 | 
				
			||||||
			image: file,
 | 
								image: file,
 | 
				
			||||||
			title: 'バナーとして表示する部分を選択',
 | 
								title: '%i18n:desktop.banner-crop-title%',
 | 
				
			||||||
			aspectRatio: 16 / 9
 | 
								aspectRatio: 16 / 9
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,11 +18,11 @@ export default (os: OS) => {
 | 
				
			||||||
			data.append('file', blob, file.name + '.cropped.png');
 | 
								data.append('file', blob, file.name + '.cropped.png');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			os.api('drive/folders/find', {
 | 
								os.api('drive/folders/find', {
 | 
				
			||||||
				name: 'バナー'
 | 
									name: '%i18n:desktop.banner%'
 | 
				
			||||||
			}).then(bannerFolder => {
 | 
								}).then(bannerFolder => {
 | 
				
			||||||
				if (bannerFolder.length === 0) {
 | 
									if (bannerFolder.length === 0) {
 | 
				
			||||||
					os.api('drive/folders/create', {
 | 
										os.api('drive/folders/create', {
 | 
				
			||||||
						name: 'バナー'
 | 
											name: '%i18n:desktop.banner%'
 | 
				
			||||||
					}).then(iconFolder => {
 | 
										}).then(iconFolder => {
 | 
				
			||||||
						resolve(upload(data, iconFolder));
 | 
											resolve(upload(data, iconFolder));
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,7 @@ export default (os: OS) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const upload = (data, folder) => new Promise((resolve, reject) => {
 | 
						const upload = (data, folder) => new Promise((resolve, reject) => {
 | 
				
			||||||
		const dialog = os.new(ProgressDialog, {
 | 
							const dialog = os.new(ProgressDialog, {
 | 
				
			||||||
			title: '新しいバナーをアップロードしています'
 | 
								title: '%i18n:desktop.uploading-banner%'
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		document.body.appendChild(dialog.$el);
 | 
							document.body.appendChild(dialog.$el);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,10 +79,10 @@ export default (os: OS) => {
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			os.apis.dialog({
 | 
								os.apis.dialog({
 | 
				
			||||||
				title: '%fa:info-circle%バナーを更新しました',
 | 
									title: '%fa:info-circle% %i18n:desktop.banner-updated%',
 | 
				
			||||||
				text: '新しいバナーが反映されるまで時間がかかる場合があります。',
 | 
									text: null,
 | 
				
			||||||
				actions: [{
 | 
									actions: [{
 | 
				
			||||||
					text: 'わかった'
 | 
										text: '%i18n:common.got-it%'
 | 
				
			||||||
				}]
 | 
									}]
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ export default (os: OS) => {
 | 
				
			||||||
			? Promise.resolve(file)
 | 
								? Promise.resolve(file)
 | 
				
			||||||
			: os.apis.chooseDriveFile({
 | 
								: os.apis.chooseDriveFile({
 | 
				
			||||||
				multiple: false,
 | 
									multiple: false,
 | 
				
			||||||
				title: '%fa:image%バナーにする画像を選択'
 | 
									title: '%fa:image% %i18n:desktop.choose-banner%'
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return selectedFile
 | 
							return selectedFile
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import dateStringify from '../../../common/scripts/date-stringify';
 | 
					 | 
				
			||||||
import parse from '../../../../../mfm/parse';
 | 
					import parse from '../../../../../mfm/parse';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MkPostFormWindow from './post-form-window.vue';
 | 
					import MkPostFormWindow from './post-form-window.vue';
 | 
				
			||||||
| 
						 | 
					@ -129,7 +128,7 @@ export default Vue.extend({
 | 
				
			||||||
				: 0;
 | 
									: 0;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		title(): string {
 | 
							title(): string {
 | 
				
			||||||
			return dateStringify(this.p.createdAt);
 | 
								return new Date(this.p.createdAt).toLocaleString();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		urls(): string[] {
 | 
							urls(): string[] {
 | 
				
			||||||
			if (this.p.text) {
 | 
								if (this.p.text) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import dateStringify from '../../../common/scripts/date-stringify';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: {
 | 
						props: {
 | 
				
			||||||
| 
						 | 
					@ -28,7 +27,7 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		title(): string {
 | 
							title(): string {
 | 
				
			||||||
			return dateStringify(this.note.createdAt);
 | 
								return new Date(this.note.createdAt).toLocaleString();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,13 +12,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import dateStringify from '../../../common/scripts/date-stringify';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: ['note'],
 | 
						props: ['note'],
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		title(): string {
 | 
							title(): string {
 | 
				
			||||||
			return dateStringify(this.note.createdAt);
 | 
								return new Date(this.note.createdAt).toLocaleString();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@
 | 
				
			||||||
			<div class="body">
 | 
								<div class="body">
 | 
				
			||||||
				<p v-if="p.cw != null" class="cw">
 | 
									<p v-if="p.cw != null" class="cw">
 | 
				
			||||||
					<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
 | 
										<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
 | 
				
			||||||
					<span class="toggle" @click="showContent = !showContent">{{ showContent ? '隠す' : 'もっと見る' }}</span>
 | 
										<span class="toggle" @click="showContent = !showContent">{{ showContent ? '%i18n:@hide%' : '%i18n:@see-more%' }}</span>
 | 
				
			||||||
				</p>
 | 
									</p>
 | 
				
			||||||
				<div class="content" v-show="p.cw == null || showContent">
 | 
									<div class="content" v-show="p.cw == null || showContent">
 | 
				
			||||||
					<div class="text">
 | 
										<div class="text">
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import dateStringify from '../../../common/scripts/date-stringify';
 | 
					 | 
				
			||||||
import parse from '../../../../../mfm/parse';
 | 
					import parse from '../../../../../mfm/parse';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MkPostFormWindow from './post-form-window.vue';
 | 
					import MkPostFormWindow from './post-form-window.vue';
 | 
				
			||||||
| 
						 | 
					@ -128,7 +127,7 @@ export default Vue.extend({
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		title(): string {
 | 
							title(): string {
 | 
				
			||||||
			return dateStringify(this.p.createdAt);
 | 
								return new Date(this.p.createdAt).toLocaleString();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		urls(): string[] {
 | 
							urls(): string[] {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import { url } from '../../../config';
 | 
					import * as config from '../../../config';
 | 
				
			||||||
import getNoteSummary from '../../../../../misc/get-note-summary';
 | 
					import getNoteSummary from '../../../../../misc/get-note-summary';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import XNote from './notes.note.vue';
 | 
					import XNote from './notes.note.vue';
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(note.createdAt).getDate();
 | 
									const date = new Date(note.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(note.createdAt).getMonth() + 1;
 | 
									const month = new Date(note.createdAt).getMonth() + 1;
 | 
				
			||||||
				note._date = date;
 | 
									note._date = date;
 | 
				
			||||||
				note._datetext = `${month}月 ${date}日`;
 | 
									note._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return note;
 | 
									return note;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -149,7 +149,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// サウンドを再生する
 | 
									// サウンドを再生する
 | 
				
			||||||
				if (this.$store.state.device.enableSounds && !silent) {
 | 
									if (this.$store.state.device.enableSounds && !silent) {
 | 
				
			||||||
					const sound = new Audio(`${url}/assets/post.mp3`);
 | 
										const sound = new Audio(`${config.url}/assets/post.mp3`);
 | 
				
			||||||
					sound.volume = this.$store.state.device.soundVolume;
 | 
										sound.volume = this.$store.state.device.soundVolume;
 | 
				
			||||||
					sound.play();
 | 
										sound.play();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -187,7 +187,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clearNotification() {
 | 
							clearNotification() {
 | 
				
			||||||
			this.unreadCount = 0;
 | 
								this.unreadCount = 0;
 | 
				
			||||||
			document.title = '%i18n:common.name%';
 | 
								document.title = config.name;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onVisibilitychange() {
 | 
							onVisibilitychange() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,7 +130,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(notification.createdAt).getDate();
 | 
									const date = new Date(notification.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(notification.createdAt).getMonth() + 1;
 | 
									const month = new Date(notification.createdAt).getMonth() + 1;
 | 
				
			||||||
				notification._date = date;
 | 
									notification._date = date;
 | 
				
			||||||
				notification._datetext = `${month}月 ${date}日`;
 | 
									notification._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return notification;
 | 
									return notification;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
			<b>%i18n:@recent-tags%:</b>
 | 
								<b>%i18n:@recent-tags%:</b>
 | 
				
			||||||
			<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" title="%@click-to-tagging%">#{{ tag }}</a>
 | 
								<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" title="%@click-to-tagging%">#{{ tag }}</a>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<input v-show="useCw" v-model="cw" placeholder="内容への注釈 (オプション)">
 | 
							<input v-show="useCw" v-model="cw" placeholder="%i18n:@annotations%">
 | 
				
			||||||
		<textarea :class="{ with: (files.length != 0 || poll) }"
 | 
							<textarea :class="{ with: (files.length != 0 || poll) }"
 | 
				
			||||||
			ref="text" v-model="text" :disabled="posting"
 | 
								ref="text" v-model="text" :disabled="posting"
 | 
				
			||||||
			@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
 | 
								@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,7 @@
 | 
				
			||||||
				<span>%i18n:@show-maps-desc%</span>
 | 
									<span>%i18n:@show-maps-desc%</span>
 | 
				
			||||||
			</mk-switch>
 | 
								</mk-switch>
 | 
				
			||||||
			<mk-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
 | 
								<mk-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
 | 
				
			||||||
 | 
								<mk-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
 | 
				
			||||||
		</section>
 | 
							</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<section class="web" v-show="page == 'web'">
 | 
							<section class="web" v-show="page == 'web'">
 | 
				
			||||||
| 
						 | 
					@ -376,6 +377,12 @@ export default Vue.extend({
 | 
				
			||||||
				value: v
 | 
									value: v
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							onChangeDisableAnimatedMfm(v) {
 | 
				
			||||||
 | 
								this.$store.dispatch('settings/set', {
 | 
				
			||||||
 | 
									key: 'disableAnimatedMfm',
 | 
				
			||||||
 | 
									value: v
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		onChangeGradientWindowHeader(v) {
 | 
							onChangeGradientWindowHeader(v) {
 | 
				
			||||||
			this.$store.dispatch('settings/set', {
 | 
								this.$store.dispatch('settings/set', {
 | 
				
			||||||
				key: 'gradientWindowHeader',
 | 
									key: 'gradientWindowHeader',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
	<div class="main" ref="main">
 | 
						<div class="main" ref="main">
 | 
				
			||||||
		<div class="backdrop"></div>
 | 
							<div class="backdrop"></div>
 | 
				
			||||||
		<div class="main">
 | 
							<div class="main">
 | 
				
			||||||
			<p ref="welcomeback" v-if="$store.getters.isSignedIn">%i18n:@welcome-back%<b>{{ $store.state.i | userName }}</b>さん</p>
 | 
								<p ref="welcomeback" v-if="$store.getters.isSignedIn">%i18n:@welcome-back%<b>{{ $store.state.i | userName }}</b>%i18n:@adjective%</p>
 | 
				
			||||||
			<div class="container" ref="mainContainer">
 | 
								<div class="container" ref="mainContainer">
 | 
				
			||||||
				<div class="left">
 | 
									<div class="left">
 | 
				
			||||||
					<x-nav/>
 | 
										<x-nav/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ export default Vue.extend({
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		add() {
 | 
							add() {
 | 
				
			||||||
			(this as any).apis.input({
 | 
								(this as any).apis.input({
 | 
				
			||||||
				title: 'リスト名',
 | 
									title: '%i18n:@list-name%',
 | 
				
			||||||
			}).then(async title => {
 | 
								}).then(async title => {
 | 
				
			||||||
				const list = await (this as any).api('users/lists/create', {
 | 
									const list = await (this as any).api('users/lists/create', {
 | 
				
			||||||
					title
 | 
										title
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,7 +72,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(note.createdAt).getDate();
 | 
									const date = new Date(note.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(note.createdAt).getMonth() + 1;
 | 
									const month = new Date(note.createdAt).getMonth() + 1;
 | 
				
			||||||
				note._date = date;
 | 
									note._date = date;
 | 
				
			||||||
				note._datetext = `${month}月 ${date}日`;
 | 
									note._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return note;
 | 
									return note;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(notification.createdAt).getDate();
 | 
									const date = new Date(notification.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(notification.createdAt).getMonth() + 1;
 | 
									const month = new Date(notification.createdAt).getMonth() + 1;
 | 
				
			||||||
				notification._date = date;
 | 
									notification._date = date;
 | 
				
			||||||
				notification._datetext = `${month}月 ${date}日`;
 | 
									notification._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return notification;
 | 
									return notification;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<component :is="ui ? 'mk-ui' : 'div'">
 | 
					<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>
 | 
					</component>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,9 +14,14 @@ export default Vue.extend({
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		nav(game) {
 | 
							nav(game, actualNav) {
 | 
				
			||||||
			history.pushState(null, null, '/reversi/' + game.id);
 | 
								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>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,11 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% - %i18n:@title%';
 | 
							document.title = `${config.name} - %i18n:@title%`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: {
 | 
						props: {
 | 
				
			||||||
| 
						 | 
					@ -16,7 +17,7 @@ export default Vue.extend({
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name%';
 | 
							document.title = config.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Progress.start();
 | 
							Progress.start();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="pptjhabgjtt7kwskbfv4y3uml6fpuhmr">
 | 
					<div class="pptjhabgjtt7kwskbfv4y3uml6fpuhmr">
 | 
				
			||||||
	<h1>{{'%i18n:@share-with%'.split("{}")[0] + '%i18n:common.name%' + '%i18n:@share-with%'.split("{}")[1]}}</h1>
 | 
						<h1>{{ '%i18n:@share-with%'.replace('{}', name) }}</h1>
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
		<mk-signin v-if="!$store.getters.isSignedIn"/>
 | 
							<mk-signin v-if="!$store.getters.isSignedIn"/>
 | 
				
			||||||
		<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
 | 
							<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
 | 
				
			||||||
| 
						 | 
					@ -12,10 +12,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
 | 
								name: config.name,
 | 
				
			||||||
			posted: false,
 | 
								posted: false,
 | 
				
			||||||
			text: new URLSearchParams(location.search).get('text')
 | 
								text: new URLSearchParams(location.search).get('text')
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@
 | 
				
			||||||
			<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
 | 
								<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
 | 
				
			||||||
			<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
 | 
								<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
 | 
				
			||||||
		</button>
 | 
							</button>
 | 
				
			||||||
		<button class="mute ui" @click="list">%fa:list% リストに追加</button>
 | 
							<button class="mute ui" @click="list">%fa:list% %i18n:@push-to-a-list%</button>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ export default Vue.extend({
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
				(this as any).apis.dialog({
 | 
									(this as any).apis.dialog({
 | 
				
			||||||
					title: 'Done!',
 | 
										title: 'Done!',
 | 
				
			||||||
					text: `${this.user.name}を${list.title}に追加しました。`
 | 
										text: '%i18n:@list-pushed%'.replace('{user}', this.user.name).replace('{list}', list.title)
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,7 +68,6 @@ export default Vue.extend({
 | 
				
			||||||
				this.user = user;
 | 
									this.user = user;
 | 
				
			||||||
				this.fetching = false;
 | 
									this.fetching = false;
 | 
				
			||||||
				Progress.done();
 | 
									Progress.done();
 | 
				
			||||||
				document.title = getUserName(this.user) + ' | %i18n:common.name%';
 | 
					 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@
 | 
				
			||||||
	<div class="body" :style="{ backgroundImage: `url('${ welcomeBgUrl }')` }">
 | 
						<div class="body" :style="{ backgroundImage: `url('${ welcomeBgUrl }')` }">
 | 
				
			||||||
		<div class="container">
 | 
							<div class="container">
 | 
				
			||||||
			<div class="info">
 | 
								<div class="info">
 | 
				
			||||||
				<span>%i18n:common.name% <b>{{ host }}</b></span>
 | 
									<span><b>{{ host }}</b></span>
 | 
				
			||||||
				<span class="stats" v-if="stats">
 | 
									<span class="stats" v-if="stats">
 | 
				
			||||||
					<span>%fa:user% {{ stats.originalUsersCount | number }}</span>
 | 
										<span>%fa:user% {{ stats.originalUsersCount | number }}</span>
 | 
				
			||||||
					<span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
 | 
										<span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
 | 
				
			||||||
| 
						 | 
					@ -16,9 +16,9 @@
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<main>
 | 
								<main>
 | 
				
			||||||
				<div class="about">
 | 
									<div class="about">
 | 
				
			||||||
					<h1 v-if="name">{{ name }}</h1>
 | 
										<h1 v-if="name != 'Misskey'">{{ name }}</h1>
 | 
				
			||||||
					<h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" alt="%i18n:common.name%"></h1>
 | 
										<h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name"></h1>
 | 
				
			||||||
					<p class="powerd-by" v-if="name">%i18n:@powered-by-misskey%</p>
 | 
										<p class="powerd-by" v-if="name != 'Misskey'" v-html="'%i18n:@powered-by-misskey%'"></p>
 | 
				
			||||||
					<p class="desc" v-html="description || '%i18n:common.about%'"></p>
 | 
										<p class="desc" v-html="description || '%i18n:common.about%'"></p>
 | 
				
			||||||
					<a ref="signup" @click="signup">📦 %i18n:@signup%</a>
 | 
										<a ref="signup" @click="signup">📦 %i18n:@signup%</a>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@
 | 
				
			||||||
			<mk-nav class="nav"/>
 | 
								<mk-nav class="nav"/>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<mk-forkit class="forkit"/>
 | 
							<mk-forkit class="forkit"/>
 | 
				
			||||||
		<img src="assets/title.dark.svg" alt="%i18n:common.name%">
 | 
							<img src="assets/title.dark.svg" :alt="name">
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="tl">
 | 
						<div class="tl">
 | 
				
			||||||
		<mk-welcome-timeline :max="20"/>
 | 
							<mk-welcome-timeline :max="20"/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="mkw-polls--body" :data-darkmode="$store.state.device.darkmode">
 | 
							<div class="mkw-polls--body" :data-darkmode="$store.state.device.darkmode">
 | 
				
			||||||
			<div class="poll" v-if="!fetching && poll != null">
 | 
								<div class="poll" v-if="!fetching && poll != null">
 | 
				
			||||||
				<p v-if="poll.text"><router-link to="poll | notePage">{{ poll.text }}</router-link></p>
 | 
									<p v-if="poll.text"><router-link :to="poll | notePage">{{ poll.text }}</router-link></p>
 | 
				
			||||||
				<p v-if="!poll.text"><router-link to="poll | notePage">%fa:link%</router-link></p>
 | 
									<p v-if="!poll.text"><router-link :to="poll | notePage">%fa:link%</router-link></p>
 | 
				
			||||||
				<mk-poll :note="poll"/>
 | 
									<mk-poll :note="poll"/>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<p class="empty" v-if="!fetching && poll == null">%i18n:@nothing%</p>
 | 
								<p class="empty" v-if="!fetching && poll == null">%i18n:@nothing%</p>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@ export default define({
 | 
				
			||||||
			}).then(data => {
 | 
								}).then(data => {
 | 
				
			||||||
				this.clear();
 | 
									this.clear();
 | 
				
			||||||
			}).catch(err => {
 | 
								}).catch(err => {
 | 
				
			||||||
				alert('失敗した');
 | 
									alert('Something happened');
 | 
				
			||||||
			}).then(() => {
 | 
								}).then(() => {
 | 
				
			||||||
				this.posting = false;
 | 
									this.posting = false;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,11 @@
 | 
				
			||||||
	<div class="banner"
 | 
						<div class="banner"
 | 
				
			||||||
		:style="$store.state.i.bannerUrl ? `background-image: url(${$store.state.i.bannerUrl})` : ''"
 | 
							:style="$store.state.i.bannerUrl ? `background-image: url(${$store.state.i.bannerUrl})` : ''"
 | 
				
			||||||
		title="%i18n:@update-banner%"
 | 
							title="%i18n:@update-banner%"
 | 
				
			||||||
		@click="os.apis.updateBanner"
 | 
							@click="() => os.apis.updateBanner()"
 | 
				
			||||||
	></div>
 | 
						></div>
 | 
				
			||||||
	<mk-avatar class="avatar" :user="$store.state.i"
 | 
						<mk-avatar class="avatar" :user="$store.state.i"
 | 
				
			||||||
		@click="os.apis.updateAvatar"
 | 
							:disable-link="true"
 | 
				
			||||||
 | 
							@click="() => os.apis.updateAvatar()"
 | 
				
			||||||
		title="%i18n:@update-avatar%"
 | 
							title="%i18n:@update-avatar%"
 | 
				
			||||||
	/>
 | 
						/>
 | 
				
			||||||
	<router-link class="name" :to="$store.state.i | userPage">{{ $store.state.i | userName }}</router-link>
 | 
						<router-link class="name" :to="$store.state.i | userPage">{{ $store.state.i | userName }}</router-link>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<mk-ui>
 | 
					<mk-ui>
 | 
				
			||||||
	<p v-if="fetching">読み込み中</p>
 | 
						<p v-if="fetching">%i18n:common.loading%</p>
 | 
				
			||||||
	<b-card v-if="!fetching" :header="app.name">
 | 
						<b-card v-if="!fetching" :header="app.name">
 | 
				
			||||||
		<b-form-group label="App Secret">
 | 
							<b-form-group label="App Secret">
 | 
				
			||||||
			<b-input :value="app.secret" readonly/>
 | 
								<b-input :value="app.secret" readonly/>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<mk-ui>
 | 
					<mk-ui>
 | 
				
			||||||
	<b-button to="/apps" variant="primary">アプリの管理</b-button>
 | 
						<b-button to="/apps" variant="primary">%i18n:@manage-apps%</b-button>
 | 
				
			||||||
</mk-ui>
 | 
					</mk-ui>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<b-navbar toggleable="md" type="dark" variant="info">
 | 
						<b-navbar toggleable="md" type="dark" variant="info">
 | 
				
			||||||
		<b-navbar-brand>%i18n:common.name% Developers</b-navbar-brand>
 | 
							<b-navbar-brand>Developers</b-navbar-brand>
 | 
				
			||||||
		<b-navbar-nav>
 | 
							<b-navbar-nav>
 | 
				
			||||||
			<b-nav-item to="/">Home</b-nav-item>
 | 
								<b-nav-item to="/">Home</b-nav-item>
 | 
				
			||||||
			<b-nav-item to="/apps">Apps</b-nav-item>
 | 
								<b-nav-item to="/apps">Apps</b-nav-item>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@ Vue.mixin({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.info(`Misskey v${version} (${codename})`);
 | 
					console.info(`Misskey v${version} (${codename})`);
 | 
				
			||||||
console.info(
 | 
					console.info(
 | 
				
			||||||
	'%cここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。',
 | 
						'%c%i18n:common.do-not-copy-paste%',
 | 
				
			||||||
	'color: red; background: yellow; font-size: 16px; font-weight: bold;');
 | 
						'color: red; background: yellow; font-size: 16px; font-weight: bold;');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BootTimer解除
 | 
					// BootTimer解除
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import getNoteSummary from '../../../../../misc/get-note-summary';
 | 
					import getNoteSummary from '../../../../../misc/get-note-summary';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const displayLimit = 30;
 | 
					const displayLimit = 30;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,7 +67,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(note.createdAt).getDate();
 | 
									const date = new Date(note.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(note.createdAt).getMonth() + 1;
 | 
									const month = new Date(note.createdAt).getMonth() + 1;
 | 
				
			||||||
				note._date = date;
 | 
									note._date = date;
 | 
				
			||||||
				note._datetext = `${month}月 ${date}日`;
 | 
									note._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return note;
 | 
									return note;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -183,7 +184,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clearNotification() {
 | 
							clearNotification() {
 | 
				
			||||||
			this.unreadCount = 0;
 | 
								this.unreadCount = 0;
 | 
				
			||||||
			document.title = '%i18n:common.name%';
 | 
								document.title = config.name;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onVisibilitychange() {
 | 
							onVisibilitychange() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ export default Vue.extend({
 | 
				
			||||||
				const date = new Date(notification.createdAt).getDate();
 | 
									const date = new Date(notification.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(notification.createdAt).getMonth() + 1;
 | 
									const month = new Date(notification.createdAt).getMonth() + 1;
 | 
				
			||||||
				notification._date = date;
 | 
									notification._date = date;
 | 
				
			||||||
				notification._datetext = `${month}月 ${date}日`;
 | 
									notification._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return notification;
 | 
									return notification;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,12 +3,12 @@
 | 
				
			||||||
	<mk-special-message/>
 | 
						<mk-special-message/>
 | 
				
			||||||
	<div class="main" ref="main">
 | 
						<div class="main" ref="main">
 | 
				
			||||||
		<div class="backdrop"></div>
 | 
							<div class="backdrop"></div>
 | 
				
			||||||
		<p ref="welcomeback" v-if="$store.getters.isSignedIn">%i18n:@welcome-back%<b>{{ $store.state.i | userName }}</b>さん</p>
 | 
							<p ref="welcomeback" v-if="$store.getters.isSignedIn">%i18n:@welcome-back%<b>{{ $store.state.i | userName }}</b>%i18n:@adjective%</p>
 | 
				
			||||||
		<div class="content" ref="mainContainer">
 | 
							<div class="content" ref="mainContainer">
 | 
				
			||||||
			<button class="nav" @click="$parent.isDrawerOpening = true">%fa:bars%</button>
 | 
								<button class="nav" @click="$parent.isDrawerOpening = true">%fa:bars%</button>
 | 
				
			||||||
			<template v-if="hasUnreadNotification || hasUnreadMessagingMessage || hasGameInvitation">%fa:circle%</template>
 | 
								<template v-if="hasUnreadNotification || hasUnreadMessagingMessage || hasGameInvitation">%fa:circle%</template>
 | 
				
			||||||
			<h1>
 | 
								<h1>
 | 
				
			||||||
				<slot>%i18n:common.name%</slot>
 | 
									<slot>config.name</slot>
 | 
				
			||||||
			</h1>
 | 
								</h1>
 | 
				
			||||||
			<slot name="func"></slot>
 | 
								<slot name="func"></slot>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
| 
						 | 
					@ -20,11 +20,13 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import * as anime from 'animejs';
 | 
					import * as anime from 'animejs';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: ['func'],
 | 
						props: ['func'],
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
 | 
								config,
 | 
				
			||||||
			hasGameInvitation: false,
 | 
								hasGameInvitation: false,
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			connectionId: null
 | 
								connectionId: null
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					@ -43,7 +44,7 @@ export default Vue.extend({
 | 
				
			||||||
		window.addEventListener('popstate', this.onPopState);
 | 
							window.addEventListener('popstate', this.onPopState);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% Drive';
 | 
							document.title = `${config.name} Drive`;
 | 
				
			||||||
		document.documentElement.style.background = '#fff';
 | 
							document.documentElement.style.background = '#fff';
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	beforeDestroy() {
 | 
						beforeDestroy() {
 | 
				
			||||||
| 
						 | 
					@ -63,7 +64,7 @@ export default Vue.extend({
 | 
				
			||||||
			(this.$refs as any).browser.openContextMenu();
 | 
								(this.$refs as any).browser.openContextMenu();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		onMoveRoot(silent) {
 | 
							onMoveRoot(silent) {
 | 
				
			||||||
			const title = '%i18n:common.name% Drive';
 | 
								const title = `${config.name} Drive`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!silent) {
 | 
								if (!silent) {
 | 
				
			||||||
				// Rewrite URL
 | 
									// Rewrite URL
 | 
				
			||||||
| 
						 | 
					@ -76,7 +77,7 @@ export default Vue.extend({
 | 
				
			||||||
			this.folder = null;
 | 
								this.folder = null;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		onOpenFolder(folder, silent) {
 | 
							onOpenFolder(folder, silent) {
 | 
				
			||||||
			const title = folder.name + ' | %i18n:common.name% Drive';
 | 
								const title = `${folder.name} | ${config.name} Drive`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!silent) {
 | 
								if (!silent) {
 | 
				
			||||||
				// Rewrite URL
 | 
									// Rewrite URL
 | 
				
			||||||
| 
						 | 
					@ -89,7 +90,7 @@ export default Vue.extend({
 | 
				
			||||||
			this.folder = folder;
 | 
								this.folder = folder;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		onOpenFile(file, silent) {
 | 
							onOpenFile(file, silent) {
 | 
				
			||||||
			const title = file.name + ' | %i18n:common.name% Drive';
 | 
								const title = `${file.name} | ${config.name} Drive`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!silent) {
 | 
								if (!silent) {
 | 
				
			||||||
				// Rewrite URL
 | 
									// Rewrite URL
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					@ -28,7 +29,7 @@ export default Vue.extend({
 | 
				
			||||||
		this.fetch();
 | 
							this.fetch();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% | %i18n:@notifications%';
 | 
							document.title = `${config.name} | %i18n:@notifications%`;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		fetch() {
 | 
							fetch() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
					import parseAcct from '../../../../../misc/acct/parse';
 | 
				
			||||||
import getUserName from '../../../../../misc/get-user-name';
 | 
					import getUserName from '../../../../../misc/get-user-name';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					@ -49,7 +50,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.user = user;
 | 
									this.user = user;
 | 
				
			||||||
				this.fetching = false;
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | %i18n:common.name%';
 | 
									document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + config.name;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		onLoaded() {
 | 
							onLoaded() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
					import parseAcct from '../../../../../misc/acct/parse';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					@ -48,7 +49,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.user = user;
 | 
									this.user = user;
 | 
				
			||||||
				this.fetching = false;
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | %i18n:common.name%';
 | 
									document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + config.name;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		onLoaded() {
 | 
							onLoaded() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,27 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<mk-ui>
 | 
					<mk-ui>
 | 
				
			||||||
	<span slot="header">%fa:gamepad%%i18n:@reversi%</span>
 | 
						<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>
 | 
					</mk-ui>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as config from '../../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% %i18n:@reversi%';
 | 
							document.title = `${config.name} %i18n:@reversi%`;
 | 
				
			||||||
		document.documentElement.style.background = '#fff';
 | 
							document.documentElement.style.background = '#fff';
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		nav(game) {
 | 
							nav(game, actualNav) {
 | 
				
			||||||
			history.pushState(null, null, '/reversi/' + game.id);
 | 
								if (actualNav) {
 | 
				
			||||||
 | 
									this.$router.push('/reversi/' + game.id);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// TODO: https://github.com/vuejs/vue-router/issues/703
 | 
				
			||||||
 | 
									this.$router.push('/reversi/' + game.id);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,6 +49,7 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import XTl from './home.timeline.vue';
 | 
					import XTl from './home.timeline.vue';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
| 
						 | 
					@ -96,7 +97,7 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name%';
 | 
							document.title = config.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Progress.start();
 | 
							Progress.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
					import parseAcct from '../../../../../misc/acct/parse';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					@ -47,7 +48,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.user = user;
 | 
									this.user = user;
 | 
				
			||||||
				this.fetching = false;
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				document.title = `%i18n:@messaging%: ${Vue.filter('userName')(this.user)} | %i18n:common.name%`;
 | 
									document.title = `%i18n:@messaging%: ${Vue.filter('userName')(this.user)} | ${config.name}`;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,11 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import getAcct from '../../../../../misc/acct/render';
 | 
					import getAcct from '../../../../../misc/acct/render';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% %i18n:@messaging%';
 | 
							document.title = `${config.name} %i18n:@messaging%`;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		navigate(user) {
 | 
							navigate(user) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
| 
						 | 
					@ -31,7 +32,7 @@ export default Vue.extend({
 | 
				
			||||||
		this.fetch();
 | 
							this.fetch();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name%';
 | 
							document.title = config.name;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		fetch() {
 | 
							fetch() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% | %i18n:@notifications%';
 | 
							document.title = '%i18n:@notifications%';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Progress.start();
 | 
							Progress.start();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ export default Vue.extend({
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% | %i18n:@title%';
 | 
							document.title = '%i18n:@title%';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Progress.start();
 | 
							Progress.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
	<span slot="header">%fa:search% {{ q }}</span>
 | 
						<span slot="header">%fa:search% {{ q }}</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<main>
 | 
						<main>
 | 
				
			||||||
		<p v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
 | 
							<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:not-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:not-found%'.split('{}')[1] }}</p>
 | 
				
			||||||
		<mk-notes ref="timeline" :more="existMore ? more : null"/>
 | 
							<mk-notes ref="timeline" :more="existMore ? more : null"/>
 | 
				
			||||||
	</main>
 | 
						</main>
 | 
				
			||||||
</mk-ui>
 | 
					</mk-ui>
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const limit = 20;
 | 
					const limit = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +35,7 @@ export default Vue.extend({
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = `%i18n:@search%: ${this.q} | %i18n:common.name%`;
 | 
							document.title = `%i18n:@search%: ${this.q} | ${config.name}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.fetch();
 | 
							this.fetch();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
				<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
 | 
									<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
 | 
				
			||||||
				<ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch>
 | 
									<ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch>
 | 
				
			||||||
				<ui-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
 | 
									<ui-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
 | 
				
			||||||
 | 
									<ui-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div>
 | 
									<div>
 | 
				
			||||||
					<div>%i18n:@timeline%</div>
 | 
										<div>%i18n:@timeline%</div>
 | 
				
			||||||
| 
						 | 
					@ -142,7 +143,7 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% | %i18n:@settings%';
 | 
							document.title = '%i18n:@settings%';
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
| 
						 | 
					@ -192,6 +193,13 @@ export default Vue.extend({
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onChangeDisableAnimatedMfm(v) {
 | 
				
			||||||
 | 
								this.$store.dispatch('settings/set', {
 | 
				
			||||||
 | 
									key: 'disableAnimatedMfm',
 | 
				
			||||||
 | 
									value: v
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onChangeShowReplyTarget(v) {
 | 
							onChangeShowReplyTarget(v) {
 | 
				
			||||||
			this.$store.dispatch('settings/set', {
 | 
								this.$store.dispatch('settings/set', {
 | 
				
			||||||
				key: 'showReplyTarget',
 | 
									key: 'showReplyTarget',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,23 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="azibmfpleajagva420swmu4c3r7ni7iw">
 | 
					<div class="azibmfpleajagva420swmu4c3r7ni7iw">
 | 
				
			||||||
	<h1>{{'%i18n:@share-with%'.split("{}")[0] + '%i18n:common.name%' + '%i18n:@share-with%'.split("{}")[1]}}</h1>
 | 
						<h1>{{ '%i18n:@share-with%'.replace('{}', name) }}</h1>
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
		<mk-signin v-if="!$store.getters.isSignedIn"/>
 | 
							<mk-signin v-if="!$store.getters.isSignedIn"/>
 | 
				
			||||||
		<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
 | 
							<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
 | 
				
			||||||
		<p v-if="posted" class="posted">%fa:check%</p>
 | 
							<p v-if="posted" class="posted">%fa:check%</p>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<ui-button class="close" v-if="posted" @click="close">閉じる</ui-button>
 | 
						<ui-button class="close" v-if="posted" @click="close">%i18n:common.close%</ui-button>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
 | 
								name: config.name,
 | 
				
			||||||
			posted: false,
 | 
								posted: false,
 | 
				
			||||||
			text: new URLSearchParams(location.search).get('text')
 | 
								text: new URLSearchParams(location.search).get('text')
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ export default Vue.extend({
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name% | %i18n:@title%';
 | 
							document.title = '%i18n:@title%';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Progress.start();
 | 
							Progress.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,7 @@ import * as age from 's-age';
 | 
				
			||||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
					import parseAcct from '../../../../../misc/acct/parse';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import XHome from './user/home.vue';
 | 
					import XHome from './user/home.vue';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
| 
						 | 
					@ -106,7 +107,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.fetching = false;
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				Progress.done();
 | 
									Progress.done();
 | 
				
			||||||
				document.title = Vue.filter('userName')(this.user) + ' | %i18n:common.name%';
 | 
									document.title = Vue.filter('userName')(this.user) + ' | ' + config.name;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,10 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="welcome">
 | 
					<div class="welcome">
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
		<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" alt="%i18n:common.name%">
 | 
							<img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name">
 | 
				
			||||||
		<p class="host">{{ host }}</p>
 | 
							<p class="host">{{ host }}</p>
 | 
				
			||||||
		<div class="about">
 | 
							<div class="about">
 | 
				
			||||||
			<h2>{{ name || 'unidentified' }}</h2>
 | 
								<h2>{{ name }}</h2>
 | 
				
			||||||
			<p v-html="description || '%i18n:common.about%'"></p>
 | 
								<p v-html="description || '%i18n:common.about%'"></p>
 | 
				
			||||||
			<router-link class="signup" to="/signup">%i18n:@signup%</router-link>
 | 
								<router-link class="signup" to="/signup">%i18n:@signup%</router-link>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,8 +24,8 @@
 | 
				
			||||||
					<option value="nav">%i18n:common.widgets.nav%</option>
 | 
										<option value="nav">%i18n:common.widgets.nav%</option>
 | 
				
			||||||
					<option value="tips">%i18n:common.widgets.tips%</option>
 | 
										<option value="tips">%i18n:common.widgets.tips%</option>
 | 
				
			||||||
				</select>
 | 
									</select>
 | 
				
			||||||
				<button @click="addWidget">追加</button>
 | 
									<button @click="addWidget">%i18n:add-widget%</button>
 | 
				
			||||||
				<p><a @click="hint">カスタマイズのヒント</a></p>
 | 
									<p><a @click="hint">%i18n:customization-tips%</a></p>
 | 
				
			||||||
			</header>
 | 
								</header>
 | 
				
			||||||
			<x-draggable
 | 
								<x-draggable
 | 
				
			||||||
				:list="widgets"
 | 
									:list="widgets"
 | 
				
			||||||
| 
						 | 
					@ -53,6 +53,7 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import * as XDraggable from 'vuedraggable';
 | 
					import * as XDraggable from 'vuedraggable';
 | 
				
			||||||
import * as uuid from 'uuid';
 | 
					import * as uuid from 'uuid';
 | 
				
			||||||
 | 
					import * as config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
| 
						 | 
					@ -102,7 +103,7 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = '%i18n:common.name%';
 | 
							document.title = config.name;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,8 @@ export default function load() {
 | 
				
			||||||
	if (config.localDriveCapacityMb == null) config.localDriveCapacityMb = 256;
 | 
						if (config.localDriveCapacityMb == null) config.localDriveCapacityMb = 256;
 | 
				
			||||||
	if (config.remoteDriveCapacityMb == null) config.remoteDriveCapacityMb = 8;
 | 
						if (config.remoteDriveCapacityMb == null) config.remoteDriveCapacityMb = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (config.name == null) config.name = 'Misskey';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return Object.assign(config, mixin);
 | 
						return Object.assign(config, mixin);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/docs/api/entities/drive-folder.yaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/docs/api/entities/drive-folder.yaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					name: "DriveFolder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					desc:
 | 
				
			||||||
 | 
					  ja: "ドライブのフォルダを表します。"
 | 
				
			||||||
 | 
					  en: "A folder of Drive."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					props:
 | 
				
			||||||
 | 
					  id:
 | 
				
			||||||
 | 
					    type: "id"
 | 
				
			||||||
 | 
					    optional: false
 | 
				
			||||||
 | 
					    desc:
 | 
				
			||||||
 | 
					      ja: "フォルダID"
 | 
				
			||||||
 | 
					      en: "The ID of this folder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createdAt:
 | 
				
			||||||
 | 
					    type: "date"
 | 
				
			||||||
 | 
					    optional: false
 | 
				
			||||||
 | 
					    desc:
 | 
				
			||||||
 | 
					      ja: "作成日時"
 | 
				
			||||||
 | 
					      en: "The created date of this folder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  userId:
 | 
				
			||||||
 | 
					    type: "id(User)"
 | 
				
			||||||
 | 
					    optional: false
 | 
				
			||||||
 | 
					    desc:
 | 
				
			||||||
 | 
					      ja: "所有者ID"
 | 
				
			||||||
 | 
					      en: "The ID of the owner of this folder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  parentId:
 | 
				
			||||||
 | 
					    type: "entity(DriveFolder)"
 | 
				
			||||||
 | 
					    optional: false
 | 
				
			||||||
 | 
					    desc:
 | 
				
			||||||
 | 
					      ja: "親フォルダのID (ルートなら null)"
 | 
				
			||||||
 | 
					      en: "The ID of parent folder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  name:
 | 
				
			||||||
 | 
					    type: "string"
 | 
				
			||||||
 | 
					    optional: false
 | 
				
			||||||
 | 
					    desc:
 | 
				
			||||||
 | 
					      ja: "フォルダ名"
 | 
				
			||||||
 | 
					      en: "The name of this folder"
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,15 @@
 | 
				
			||||||
# MisskeyリバーシBotの開発
 | 
					# MisskeyリバーシBotの開発
 | 
				
			||||||
Misskeyのリバーシ機能に対応したBotの開発方法をここに記します。
 | 
					Misskeyのリバーシ機能に対応したBotの開発方法をここに記します。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. `reversi`ストリームに以下のパラメータを付けて接続する:
 | 
					1. `games/reversi`ストリームに以下のパラメータを付けて接続する:
 | 
				
			||||||
	* `i`: botアカウントのAPIキー
 | 
						* `i`: botアカウントのAPIキー
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2. 対局への招待が来たら、ストリームから`invited`イベントが流れてくる
 | 
					2. 対局への招待が来たら、ストリームから`invited`イベントが流れてくる
 | 
				
			||||||
	* イベントの中身に、`parent`という名前で対局へ誘ってきたユーザーの情報が含まれている
 | 
						* イベントの中身に、`parent`という名前で対局へ誘ってきたユーザーの情報が含まれている
 | 
				
			||||||
 | 
					
 | 
				
			||||||
3. `reversi/match`へ、`user_id`として`parent`の`id`が含まれたリクエストを送信する
 | 
					3. `games/reversi/match`へ、`user_id`として`parent`の`id`が含まれたリクエストを送信する
 | 
				
			||||||
 | 
					
 | 
				
			||||||
4. 上手くいくとゲーム情報が返ってくるので、`reversi-game`ストリームへ、以下のパラメータを付けて接続する:
 | 
					4. 上手くいくとゲーム情報が返ってくるので、`games/reversi-game`ストリームへ、以下のパラメータを付けて接続する:
 | 
				
			||||||
	* `i`: botアカウントのAPIキー
 | 
						* `i`: botアカウントのAPIキー
 | 
				
			||||||
	* `game`: `game`の`id`
 | 
						* `game`: `game`の`id`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,8 +96,8 @@ y = Math.floor(pos / mapWidth)
 | 
				
			||||||
フォームコントロールは、次のようなオブジェクトです:
 | 
					フォームコントロールは、次のようなオブジェクトです:
 | 
				
			||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  id: 'button1',
 | 
					  id: 'switch1',
 | 
				
			||||||
  type: 'button',
 | 
					  type: 'switch',
 | 
				
			||||||
  label: 'Enable hoge',
 | 
					  label: 'Enable hoge',
 | 
				
			||||||
  value: false
 | 
					  value: false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -110,21 +110,21 @@ y = Math.floor(pos / mapWidth)
 | 
				
			||||||
### フォームの操作を受け取る
 | 
					### フォームの操作を受け取る
 | 
				
			||||||
ユーザーがフォームを操作すると、ストリームから`update-form`イベントが流れてきます。
 | 
					ユーザーがフォームを操作すると、ストリームから`update-form`イベントが流れてきます。
 | 
				
			||||||
イベントの中身には、コントロールのIDと、ユーザーが設定した値が含まれています。
 | 
					イベントの中身には、コントロールのIDと、ユーザーが設定した値が含まれています。
 | 
				
			||||||
例えば、上で示したボタンをユーザーがオンにしたとすると、次のイベントが流れてきます:
 | 
					例えば、上で示したスイッチをユーザーがオンにしたとすると、次のイベントが流れてきます:
 | 
				
			||||||
```javascript
 | 
					```javascript
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  id: 'button1',
 | 
					  id: 'switch1',
 | 
				
			||||||
  value: true
 | 
					  value: true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### フォームコントロールの種類
 | 
					### フォームコントロールの種類
 | 
				
			||||||
#### ボタン
 | 
					#### スイッチ
 | 
				
			||||||
type: `button`
 | 
					type: `switch`
 | 
				
			||||||
ボタンを表示します。何かの機能をオン/オフさせたい場合に有用です。
 | 
					スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##### プロパティ
 | 
					##### プロパティ
 | 
				
			||||||
`desc` ... ボタンの詳細な説明。
 | 
					`desc` ... スイッチの詳細な説明。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### ラジオボタン
 | 
					#### ラジオボタン
 | 
				
			||||||
type: `radio`
 | 
					type: `radio`
 | 
				
			||||||
| 
						 | 
					@ -145,6 +145,15 @@ items: [{
 | 
				
			||||||
}]
 | 
					}]
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### スライダー
 | 
				
			||||||
 | 
					type: `slider`
 | 
				
			||||||
 | 
					スライダーを表示します。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##### プロパティ
 | 
				
			||||||
 | 
					`min` ... スライダーの下限。
 | 
				
			||||||
 | 
					`max` ... スライダーの上限。
 | 
				
			||||||
 | 
					`step` ... 入力欄で刻むステップ値。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### テキストボックス
 | 
					#### テキストボックス
 | 
				
			||||||
type: `textbox`
 | 
					type: `textbox`
 | 
				
			||||||
テキストボックスを表示します。ユーザーになにか入力させる一般的な用途に利用できます。
 | 
					テキストボックスを表示します。ユーザーになにか入力させる一般的な用途に利用できます。
 | 
				
			||||||
| 
						 | 
					@ -163,3 +172,6 @@ type: `textbox`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
メッセージの種類: `success`, `info`, `warning`, `error`。
 | 
					メッセージの種類: `success`, `info`, `warning`, `error`。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 投了する
 | 
				
			||||||
 | 
					投了をするには、<a href="./api/endpoints/games/reversi/games/surrender">このエンドポイント</a>にリクエストします。
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/games/reversi/package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/games/reversi/package.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "misskey-reversi",
 | 
				
			||||||
 | 
					  "version": "0.0.5",
 | 
				
			||||||
 | 
					  "description": "Misskey reversi engine",
 | 
				
			||||||
 | 
					  "keywords": [
 | 
				
			||||||
 | 
					    "misskey"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "author": "syuilo <i@syuilo.com>",
 | 
				
			||||||
 | 
					  "license": "MIT",
 | 
				
			||||||
 | 
					  "repository": "https://github.com/syuilo/misskey.git",
 | 
				
			||||||
 | 
					  "bugs": "https://github.com/syuilo/misskey/issues",
 | 
				
			||||||
 | 
					  "main": "./built/core.js",
 | 
				
			||||||
 | 
					  "types": "./built/core.d.ts",
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "build": "tsc"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/games/reversi/tsconfig.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/games/reversi/tsconfig.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"compilerOptions": {
 | 
				
			||||||
 | 
							"noEmitOnError": false,
 | 
				
			||||||
 | 
							"noImplicitAny": false,
 | 
				
			||||||
 | 
							"noImplicitReturns": true,
 | 
				
			||||||
 | 
							"noFallthroughCasesInSwitch": true,
 | 
				
			||||||
 | 
							"experimentalDecorators": true,
 | 
				
			||||||
 | 
							"declaration": true,
 | 
				
			||||||
 | 
							"sourceMap": false,
 | 
				
			||||||
 | 
							"target": "es2017",
 | 
				
			||||||
 | 
							"module": "commonjs",
 | 
				
			||||||
 | 
							"removeComments": false,
 | 
				
			||||||
 | 
							"noLib": false,
 | 
				
			||||||
 | 
							"outDir": "./built",
 | 
				
			||||||
 | 
							"rootDir": "./"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"compileOnSave": false,
 | 
				
			||||||
 | 
						"include": [
 | 
				
			||||||
 | 
							"./core.ts"
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Error.stackTraceLimit = Infinity;
 | 
					Error.stackTraceLimit = Infinity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require('events').EventEmitter.defaultMaxListeners = 128;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as os from 'os';
 | 
					import * as os from 'os';
 | 
				
			||||||
import * as cluster from 'cluster';
 | 
					import * as cluster from 'cluster';
 | 
				
			||||||
import * as debug from 'debug';
 | 
					import * as debug from 'debug';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
const { lib: emojilib } = require('emojilib');
 | 
					const { lib: emojilib } = require('emojilib');
 | 
				
			||||||
import { JSDOM } from 'jsdom';
 | 
					const jsdom = require('jsdom');
 | 
				
			||||||
 | 
					const { JSDOM } = jsdom;
 | 
				
			||||||
import config from '../config';
 | 
					import config from '../config';
 | 
				
			||||||
import { INote } from '../models/note';
 | 
					import { INote } from '../models/note';
 | 
				
			||||||
import { TextElement } from './parse';
 | 
					import { TextElement } from './parse';
 | 
				
			||||||
| 
						 | 
					@ -11,6 +12,18 @@ const handlers: { [key: string]: (window: any, token: any, mentionedRemoteUsers:
 | 
				
			||||||
		document.body.appendChild(b);
 | 
							document.body.appendChild(b);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						big({ document }, { big }) {
 | 
				
			||||||
 | 
							const b = document.createElement('strong');
 | 
				
			||||||
 | 
							b.textContent = big;
 | 
				
			||||||
 | 
							document.body.appendChild(b);
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						motion({ document }, { big }) {
 | 
				
			||||||
 | 
							const b = document.createElement('strong');
 | 
				
			||||||
 | 
							b.textContent = big;
 | 
				
			||||||
 | 
							document.body.appendChild(b);
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	code({ document }, { code }) {
 | 
						code({ document }, { code }) {
 | 
				
			||||||
		const pre = document.createElement('pre');
 | 
							const pre = document.createElement('pre');
 | 
				
			||||||
		const inner = document.createElement('code');
 | 
							const inner = document.createElement('code');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/mfm/parse/elements/big.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/mfm/parse/elements/big.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Big
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type TextElementBig = {
 | 
				
			||||||
 | 
						type: 'big'
 | 
				
			||||||
 | 
						content: string
 | 
				
			||||||
 | 
						big: string
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function(text: string) {
 | 
				
			||||||
 | 
						const match = text.match(/^\*\*\*(.+?)\*\*\*/);
 | 
				
			||||||
 | 
						if (!match) return null;
 | 
				
			||||||
 | 
						const big = match[0];
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							type: 'big',
 | 
				
			||||||
 | 
							content: big,
 | 
				
			||||||
 | 
							big: match[1]
 | 
				
			||||||
 | 
						} as TextElementBig;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/mfm/parse/elements/motion.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/mfm/parse/elements/motion.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TextElementBold } from './elements/bold';
 | 
					import { TextElementBold } from './elements/bold';
 | 
				
			||||||
 | 
					import { TextElementBig } from './elements/big';
 | 
				
			||||||
import { TextElementCode } from './elements/code';
 | 
					import { TextElementCode } from './elements/code';
 | 
				
			||||||
import { TextElementEmoji } from './elements/emoji';
 | 
					import { TextElementEmoji } from './elements/emoji';
 | 
				
			||||||
import { TextElementHashtag } from './elements/hashtag';
 | 
					import { TextElementHashtag } from './elements/hashtag';
 | 
				
			||||||
| 
						 | 
					@ -13,8 +14,10 @@ import { TextElementQuote } from './elements/quote';
 | 
				
			||||||
import { TextElementSearch } from './elements/search';
 | 
					import { TextElementSearch } from './elements/search';
 | 
				
			||||||
import { TextElementTitle } from './elements/title';
 | 
					import { TextElementTitle } from './elements/title';
 | 
				
			||||||
import { TextElementUrl } from './elements/url';
 | 
					import { TextElementUrl } from './elements/url';
 | 
				
			||||||
 | 
					import { TextElementMotion } from './elements/motion';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const elements = [
 | 
					const elements = [
 | 
				
			||||||
 | 
						require('./elements/big'),
 | 
				
			||||||
	require('./elements/bold'),
 | 
						require('./elements/bold'),
 | 
				
			||||||
	require('./elements/title'),
 | 
						require('./elements/title'),
 | 
				
			||||||
	require('./elements/url'),
 | 
						require('./elements/url'),
 | 
				
			||||||
| 
						 | 
					@ -25,11 +28,13 @@ const elements = [
 | 
				
			||||||
	require('./elements/inline-code'),
 | 
						require('./elements/inline-code'),
 | 
				
			||||||
	require('./elements/quote'),
 | 
						require('./elements/quote'),
 | 
				
			||||||
	require('./elements/emoji'),
 | 
						require('./elements/emoji'),
 | 
				
			||||||
	require('./elements/search')
 | 
						require('./elements/search'),
 | 
				
			||||||
 | 
						require('./elements/motion')
 | 
				
			||||||
].map(element => element.default as TextElementProcessor);
 | 
					].map(element => element.default as TextElementProcessor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElement = { type: 'text', content: string }
 | 
					export type TextElement = { type: 'text', content: string }
 | 
				
			||||||
	| TextElementBold
 | 
						| TextElementBold
 | 
				
			||||||
 | 
						| TextElementBig
 | 
				
			||||||
	| TextElementCode
 | 
						| TextElementCode
 | 
				
			||||||
	| TextElementEmoji
 | 
						| TextElementEmoji
 | 
				
			||||||
	| TextElementHashtag
 | 
						| TextElementHashtag
 | 
				
			||||||
| 
						 | 
					@ -39,7 +44,8 @@ export type TextElement = { type: 'text', content: string }
 | 
				
			||||||
	| TextElementQuote
 | 
						| TextElementQuote
 | 
				
			||||||
	| TextElementSearch
 | 
						| TextElementSearch
 | 
				
			||||||
	| TextElementTitle
 | 
						| TextElementTitle
 | 
				
			||||||
	| TextElementUrl;
 | 
						| TextElementUrl
 | 
				
			||||||
 | 
						| TextElementMotion;
 | 
				
			||||||
export type TextElementProcessor = (text: string, i: number) => TextElement | TextElement[];
 | 
					export type TextElementProcessor = (text: string, i: number) => TextElement | TextElement[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (source: string): TextElement[] => {
 | 
					export default (source: string): TextElement[] => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import DriveFileThumbnail, { deleteDriveFileThumbnail } from './drive-file-thumb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
 | 
					const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
 | 
				
			||||||
DriveFile.createIndex('md5');
 | 
					DriveFile.createIndex('md5');
 | 
				
			||||||
DriveFile.createIndex('metadata.uri', { sparse: true, unique: true });
 | 
					DriveFile.createIndex(['metadata.uri', 'metadata.userId'], { sparse: true, unique: true });
 | 
				
			||||||
export default DriveFile;
 | 
					export default DriveFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DriveFileChunk = monkDb.get('driveFiles.chunks');
 | 
					export const DriveFileChunk = monkDb.get('driveFiles.chunks');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ export interface IReversiGame {
 | 
				
			||||||
	isStarted: boolean;
 | 
						isStarted: boolean;
 | 
				
			||||||
	isEnded: boolean;
 | 
						isEnded: boolean;
 | 
				
			||||||
	winnerId: mongo.ObjectID;
 | 
						winnerId: mongo.ObjectID;
 | 
				
			||||||
 | 
						surrendered: mongo.ObjectID;
 | 
				
			||||||
	logs: Array<{
 | 
						logs: Array<{
 | 
				
			||||||
		at: Date;
 | 
							at: Date;
 | 
				
			||||||
		color: boolean;
 | 
							color: boolean;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,11 @@ export default async (job: bq.Job, done: any): Promise<void> => {
 | 
				
			||||||
		await request(job.data.user, job.data.to, job.data.content);
 | 
							await request(job.data.user, job.data.to, job.data.content);
 | 
				
			||||||
		done();
 | 
							done();
 | 
				
			||||||
	} catch (res) {
 | 
						} catch (res) {
 | 
				
			||||||
 | 
							if (res == null || !res.hasOwnProperty('statusCode')) {
 | 
				
			||||||
 | 
								console.warn(`deliver failed (unknown): ${res}`);
 | 
				
			||||||
 | 
								return done();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (res.statusCode == null) return done();
 | 
							if (res.statusCode == null) return done();
 | 
				
			||||||
		if (res.statusCode >= 400 && res.statusCode < 500) {
 | 
							if (res.statusCode >= 400 && res.statusCode < 500) {
 | 
				
			||||||
			// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
 | 
								// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										59
									
								
								src/server/api/endpoints/games/reversi/games/surrender.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/server/api/endpoints/games/reversi/games/surrender.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					import $ from 'cafy'; import ID from '../../../../../../misc/cafy-id';
 | 
				
			||||||
 | 
					import ReversiGame, { pack } from '../../../../../../models/games/reversi/game';
 | 
				
			||||||
 | 
					import { ILocalUser } from '../../../../../../models/user';
 | 
				
			||||||
 | 
					import getParams from '../../../../get-params';
 | 
				
			||||||
 | 
					import { publishReversiGameStream } from '../../../../../../stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						desc: {
 | 
				
			||||||
 | 
							ja: '指定したリバーシの対局で投了します。'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							gameId: $.type(ID).note({
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									ja: '投了したい対局'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const [ps, psErr] = getParams(meta, params);
 | 
				
			||||||
 | 
						if (psErr) return rej(psErr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const game = await ReversiGame.findOne({ _id: ps.gameId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (game == null) {
 | 
				
			||||||
 | 
							return rej('game not found');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (game.isEnded) {
 | 
				
			||||||
 | 
							return rej('this game is already ended');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) {
 | 
				
			||||||
 | 
							return rej('access denied');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const winnerId = game.user1Id.equals(user._id) ? game.user2Id : game.user1Id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						await ReversiGame.update({
 | 
				
			||||||
 | 
							_id: game._id
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							$set: {
 | 
				
			||||||
 | 
								surrendered: user._id,
 | 
				
			||||||
 | 
								isEnded: true,
 | 
				
			||||||
 | 
								winnerId: winnerId
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						publishReversiGameStream(game._id, 'ended', {
 | 
				
			||||||
 | 
							winnerId: winnerId,
 | 
				
			||||||
 | 
							game: await pack(game._id, user)
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -1,33 +1,156 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import User, { pack, ILocalUser } from '../../../../models/user';
 | 
					 | 
				
			||||||
const escapeRegexp = require('escape-regexp');
 | 
					const escapeRegexp = require('escape-regexp');
 | 
				
			||||||
 | 
					import User, { pack, ILocalUser, validateUsername, IUser } from '../../../../models/user';
 | 
				
			||||||
 | 
					import getParams from '../../get-params';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						desc: {
 | 
				
			||||||
 | 
							ja: 'ユーザーを検索します。'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							query: $.str.note({
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									ja: 'クエリ'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							offset: $.num.optional.min(0).note({
 | 
				
			||||||
 | 
								default: 0,
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									ja: 'オフセット'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							limit: $.num.optional.range(1, 100).note({
 | 
				
			||||||
 | 
								default: 10,
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									ja: '取得する数'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							localOnly: $.bool.optional.note({
 | 
				
			||||||
 | 
								default: false,
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									ja: 'ローカルユーザーのみ検索対象にするか否か'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Search a user
 | 
					 * Search a user
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
 | 
					export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
 | 
				
			||||||
	// Get 'query' parameter
 | 
						const [ps, psErr] = getParams(meta, params);
 | 
				
			||||||
	const [query, queryError] = $.str.pipe(x => x != '').get(params.query);
 | 
						if (psErr) return rej(psErr);
 | 
				
			||||||
	if (queryError) return rej('invalid query param');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get 'max' parameter
 | 
						const isUsername = validateUsername(ps.query.replace('@', ''));
 | 
				
			||||||
	const [max = 10, maxErr] = $.num.optional.range(1, 30).get(params.max);
 | 
					 | 
				
			||||||
	if (maxErr) return rej('invalid max param');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const escapedQuery = escapeRegexp(query);
 | 
						let users: IUser[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Search users
 | 
						if (isUsername) {
 | 
				
			||||||
	const users = await User
 | 
							users = await User
 | 
				
			||||||
		.find({
 | 
								.find({
 | 
				
			||||||
			host: null,
 | 
									host: null,
 | 
				
			||||||
			$or: [{
 | 
									usernameLower: new RegExp('^' + escapeRegexp(ps.query.replace('@', '').toLowerCase()))
 | 
				
			||||||
				usernameLower: new RegExp(escapedQuery.replace('@', '').toLowerCase())
 | 
					 | 
				
			||||||
			}, {
 | 
								}, {
 | 
				
			||||||
				name: new RegExp(escapedQuery)
 | 
									limit: ps.limit,
 | 
				
			||||||
			}]
 | 
									skip: ps.offset
 | 
				
			||||||
		}, {
 | 
								});
 | 
				
			||||||
			limit: max
 | 
					
 | 
				
			||||||
		});
 | 
							if (users.length < ps.limit && !ps.localOnly) {
 | 
				
			||||||
 | 
								const otherUsers = await User
 | 
				
			||||||
 | 
									.find({
 | 
				
			||||||
 | 
										host: { $ne: null },
 | 
				
			||||||
 | 
										usernameLower: new RegExp('^' + escapeRegexp(ps.query.replace('@', '').toLowerCase()))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										limit: ps.limit - users.length
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								users = users.concat(otherUsers);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (users.length < ps.limit) {
 | 
				
			||||||
 | 
								const otherUsers = await User
 | 
				
			||||||
 | 
									.find({
 | 
				
			||||||
 | 
										_id: { $nin: users.map(u => u._id) },
 | 
				
			||||||
 | 
										host: null,
 | 
				
			||||||
 | 
										usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase()))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										limit: ps.limit - users.length
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								users = users.concat(otherUsers);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (users.length < ps.limit && !ps.localOnly) {
 | 
				
			||||||
 | 
								const otherUsers = await User
 | 
				
			||||||
 | 
									.find({
 | 
				
			||||||
 | 
										_id: { $nin: users.map(u => u._id) },
 | 
				
			||||||
 | 
										host: { $ne: null },
 | 
				
			||||||
 | 
										usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase()))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										limit: ps.limit - users.length
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								users = users.concat(otherUsers);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (users.length < ps.limit) {
 | 
				
			||||||
 | 
							const otherUsers = await User
 | 
				
			||||||
 | 
								.find({
 | 
				
			||||||
 | 
									_id: { $nin: users.map(u => u._id) },
 | 
				
			||||||
 | 
									host: null,
 | 
				
			||||||
 | 
									name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									limit: ps.limit - users.length
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							users = users.concat(otherUsers);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (users.length < ps.limit && !ps.localOnly) {
 | 
				
			||||||
 | 
							const otherUsers = await User
 | 
				
			||||||
 | 
								.find({
 | 
				
			||||||
 | 
									_id: { $nin: users.map(u => u._id) },
 | 
				
			||||||
 | 
									host: { $ne: null },
 | 
				
			||||||
 | 
									name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									limit: ps.limit - users.length
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							users = users.concat(otherUsers);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (users.length < ps.limit) {
 | 
				
			||||||
 | 
							const otherUsers = await User
 | 
				
			||||||
 | 
								.find({
 | 
				
			||||||
 | 
									_id: { $nin: users.map(u => u._id) },
 | 
				
			||||||
 | 
									host: null,
 | 
				
			||||||
 | 
									name: new RegExp(escapeRegexp(ps.query.toLowerCase()))
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									limit: ps.limit - users.length
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							users = users.concat(otherUsers);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (users.length < ps.limit && !ps.localOnly) {
 | 
				
			||||||
 | 
							const otherUsers = await User
 | 
				
			||||||
 | 
								.find({
 | 
				
			||||||
 | 
									_id: { $nin: users.map(u => u._id) },
 | 
				
			||||||
 | 
									host: { $ne: null },
 | 
				
			||||||
 | 
									name: new RegExp(escapeRegexp(ps.query.toLowerCase()))
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									limit: ps.limit - users.length
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							users = users.concat(otherUsers);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Serialize
 | 
						// Serialize
 | 
				
			||||||
	res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
 | 
						res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,70 +0,0 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					 | 
				
			||||||
import User, { pack, ILocalUser } from '../../../../models/user';
 | 
					 | 
				
			||||||
const escapeRegexp = require('escape-regexp');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Search a user by username
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
 | 
					 | 
				
			||||||
	// Get 'query' parameter
 | 
					 | 
				
			||||||
	const [query, queryError] = $.str.get(params.query);
 | 
					 | 
				
			||||||
	if (queryError) return rej('invalid query param');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get 'offset' parameter
 | 
					 | 
				
			||||||
	const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
 | 
					 | 
				
			||||||
	if (offsetErr) return rej('invalid offset param');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get 'limit' parameter
 | 
					 | 
				
			||||||
	const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
 | 
					 | 
				
			||||||
	if (limitErr) return rej('invalid limit param');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let users = await User
 | 
					 | 
				
			||||||
		.find({
 | 
					 | 
				
			||||||
			host: null,
 | 
					 | 
				
			||||||
			usernameLower: new RegExp('^' + escapeRegexp(query.toLowerCase()))
 | 
					 | 
				
			||||||
		}, {
 | 
					 | 
				
			||||||
			limit: limit,
 | 
					 | 
				
			||||||
			skip: offset
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (users.length < limit) {
 | 
					 | 
				
			||||||
		const otherUsers = await User
 | 
					 | 
				
			||||||
			.find({
 | 
					 | 
				
			||||||
				host: { $ne: null },
 | 
					 | 
				
			||||||
				usernameLower: new RegExp('^' + escapeRegexp(query.toLowerCase()))
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				limit: limit - users.length
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		users = users.concat(otherUsers);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (users.length < limit) {
 | 
					 | 
				
			||||||
		const otherUsers = await User
 | 
					 | 
				
			||||||
			.find({
 | 
					 | 
				
			||||||
				_id: { $nin: users.map(u => u._id) },
 | 
					 | 
				
			||||||
				host: null,
 | 
					 | 
				
			||||||
				usernameLower: new RegExp(escapeRegexp(query.toLowerCase()))
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				limit: limit - users.length
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		users = users.concat(otherUsers);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (users.length < limit) {
 | 
					 | 
				
			||||||
		const otherUsers = await User
 | 
					 | 
				
			||||||
			.find({
 | 
					 | 
				
			||||||
				_id: { $nin: users.map(u => u._id) },
 | 
					 | 
				
			||||||
				host: { $ne: null },
 | 
					 | 
				
			||||||
				usernameLower: new RegExp(escapeRegexp(query.toLowerCase()))
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				limit: limit - users.length
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		users = users.concat(otherUsers);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Serialize
 | 
					 | 
				
			||||||
	res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ const handler = new EventEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let bot: IUser;
 | 
					let bot: IUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const post = async (text: string) => {
 | 
					const post = async (text: string, home = true) => {
 | 
				
			||||||
	if (bot == null) {
 | 
						if (bot == null) {
 | 
				
			||||||
		const account = await User.findOne({
 | 
							const account = await User.findOne({
 | 
				
			||||||
			usernameLower: config.github_bot.username.toLowerCase()
 | 
								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
 | 
					// Init router
 | 
				
			||||||
| 
						 | 
					@ -130,7 +130,7 @@ handler.on('issue_comment', event => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
handler.on('watch', event => {
 | 
					handler.on('watch', event => {
 | 
				
			||||||
	const sender = event.sender;
 | 
						const sender = event.sender;
 | 
				
			||||||
	post(`⭐️ Starred by **${sender.login}** ⭐️`);
 | 
						post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
handler.on('fork', event => {
 | 
					handler.on('fork', event => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,16 +294,35 @@ export default async function(
 | 
				
			||||||
		metadata.uri = uri;
 | 
							metadata.uri = uri;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const driveFile = isLink
 | 
						let driveFile: IDriveFile;
 | 
				
			||||||
		? await DriveFile.insert({
 | 
					
 | 
				
			||||||
			length: 0,
 | 
						if (isLink) {
 | 
				
			||||||
			uploadDate: new Date(),
 | 
							try {
 | 
				
			||||||
			md5: hash,
 | 
								driveFile = await DriveFile.insert({
 | 
				
			||||||
			filename: detectedName,
 | 
									length: 0,
 | 
				
			||||||
			metadata: metadata,
 | 
									uploadDate: new Date(),
 | 
				
			||||||
			contentType: mime
 | 
									md5: hash,
 | 
				
			||||||
		})
 | 
									filename: detectedName,
 | 
				
			||||||
		: await (save(fs.createReadStream(path), detectedName, mime, hash, size, metadata));
 | 
									metadata: metadata,
 | 
				
			||||||
 | 
									contentType: mime
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							} catch (e) {
 | 
				
			||||||
 | 
								// duplicate key error (when already registered)
 | 
				
			||||||
 | 
								if (e.code === 11000) {
 | 
				
			||||||
 | 
									log(`already registered ${metadata.uri}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									driveFile = await DriveFile.findOne({
 | 
				
			||||||
 | 
										'metadata.uri': metadata.uri,
 | 
				
			||||||
 | 
										'metadata.userId': user._id
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									console.error(e);
 | 
				
			||||||
 | 
									throw e;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							driveFile = await (save(fs.createReadStream(path), detectedName, mime, hash, size, metadata));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log(`drive file has been created ${driveFile._id}`);
 | 
						log(`drive file has been created ${driveFile._id}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +103,28 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 | 
				
			||||||
		data.visibleUsers = data.visibleUsers.filter(x => x != null);
 | 
							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();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Parse MFM
 | 
						// Parse MFM
 | 
				
			||||||
	const tokens = data.text ? parse(data.text) : [];
 | 
						const tokens = data.text ? parse(data.text) : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								test/mfm.ts
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								test/mfm.ts
									
										
									
									
									
								
							| 
						 | 
					@ -31,6 +31,28 @@ describe('Text', () => {
 | 
				
			||||||
			], tokens);
 | 
								], tokens);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							it('big', () => {
 | 
				
			||||||
 | 
								const tokens = analyze('***Strawberry*** Pasta');
 | 
				
			||||||
 | 
								assert.deepEqual([
 | 
				
			||||||
 | 
									{ 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', () => {
 | 
							it('mention', () => {
 | 
				
			||||||
			const tokens = analyze('@himawari お腹ペコい');
 | 
								const tokens = analyze('@himawari お腹ペコい');
 | 
				
			||||||
			assert.deepEqual([
 | 
								assert.deepEqual([
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue