diff --git a/.config/example.yml b/.config/example.yml index 4772a1f22f..427377a694 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -1,3 +1,9 @@ +# インスタンス名 +name: + +# インスタンスの紹介 +description: + # サーバーのメンテナ情報 maintainer: # メンテナの名前 @@ -55,3 +61,7 @@ twitter: # インテグレーション用アプリのコンシューマーシークレット consumer_secret: + +# true にすると、リモートのファイルをキャッシュしなくなります(直リンクします)。 +# ストレージ容量を節約することができますが、「リモートメディアを表示しない」設定をオンにしているユーザーは、リモートの画像などは見えなくなります。 +preventCache: false diff --git a/.gitattributes b/.gitattributes index 952d6cd0e9..9ea77a4b0e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ *.svg -diff -text *.psd -diff -text *.ai -diff -text +yarn.lock -diff -text diff --git a/.gitignore b/.gitignore index 41fef982c4..197c1ec4d2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ npm-debug.log run.bat api-docs.json package-lock.json -yarn.lock +*.log diff --git a/.npmrc b/.npmrc index c1ca392fea..2fcf1d76c2 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-lock = false +save-exact=true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..5c0214ff9f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +ChangeLog +========= + +破壊的変更のみ記載。 + +This document describes breaking changes only. + +4.0.0 +----- + +オセロがリバーシに変更されました。 + +Othello is now Reversi. + +### Migration + +MongoDBの、`othelloGames`と`othelloMatchings`コレクションをそれぞれ`reversiGames`と`reversiMatchings`にリネームしてください。 + +You need to rename `othelloGames` and `othelloMatchings` MongoDB collections to `reversiGames` and `reversiMatchings`. + +3.0.0 +----- + +### Migration + +起動する前に、`node cli/recount-stats`してください。 + +Please run `node cli/recount-stats` before launch. diff --git a/README.md b/README.md index a44c7e16ee..f8738421cf 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,24 @@ > Lead Maintainer: [syuilo][syuilo-link] **[Misskey](https://misskey.xyz)** is a completely open source, -ultimately sophisticated new type of mini-blog based SNS. +ultimately sophisticated professional microblogging software. Become a Patron! +![](https://c10.patreonusercontent.com/3/e30%3D/patreon-posts/RsKWEDEKf8D_wYDQWAbex9CSb-1DnXW1nfqfLvuys5ROj2k0VF6_luuzHMTyf95n.png?token-time=1529539200&token-hash=RmcSP0947mw5o2-B6g1L6aU_OoDXANe198kLU6HMO30%3D) + :sparkles: Features ---------------------------------------------------------------- * Reactions * User lists +* Customizable column view (known as MisskeyDeck) + * and widgets! * Private messages * Mute -* Real time contents +* Streaming * ActivityPub compatible -and more! You can touch with your own eyes at [misskey.xyz](https://misskey.xyz). +and more! You can see it with your own eyes at [misskey.xyz](https://misskey.xyz). :package: Create your instance ---------------------------------------------------------------- @@ -45,18 +49,9 @@ If you want to... [![Backers][backers-image]][support-url] [![Sponsors][sponsors-image]][support-url] -:mortar_board: Notable contributors ----------------------------------------------------------------- -| ![syuilo][syuilo-icon] | ![Morisawa Aya][ayamorisawa-icon] | ![otofune][otofune-icon] | ![akihikodaki][akihikodaki-icon] | ![tamaina][tamaina-icon] | ![rinsuki][rinsuki-icon] | -|:-:|:-:|:-:|:-:|:-:|:-:| -| [syuilo][syuilo-link]
Owner | [Aya Morisawa][ayamorisawa-link]
Collaborator | [otofune][otofune-link]
Collaborator | [akihikodaki][akihikodaki-link] | [tamaina][tamaina-link] | [rinsuki][rinsuki-link] | - -[List of all contributors](https://github.com/syuilo/misskey/graphs/contributors) - -### :earth_americas: Translators -| ![][mirro-san-icon] | ![][Conan-kun-icon] | ![][m4sk1n-icon] | -|:-:|:-:|:-:| -| [Mirro][mirro-san-link]
English, French | [Asriel][Conan-kun-link]
English, French | [Marcin Mikołajczak][m4sk1n-link]
Polish | +| ![][ooo-icon] | +|:-:| +| [ooo][ooo-link] | :four_leaf_clover: Copyright ---------------------------------------------------------------- @@ -84,23 +79,8 @@ Misskey is an open-source software licensed under [GNU AGPLv3](LICENSE). [sponsors-image]: https://opencollective.com/misskey/sponsors.svg [support-url]: https://opencollective.com/misskey#support - [syuilo-link]: https://syuilo.com [syuilo-icon]: https://avatars2.githubusercontent.com/u/4439005?v=3&s=70 -[ayamorisawa-link]: https://github.com/ayamorisawa -[ayamorisawa-icon]: https://avatars0.githubusercontent.com/u/10798641?v=3&s=70 -[otofune-link]: https://github.com/otofune -[otofune-icon]: https://avatars0.githubusercontent.com/u/15062473?v=3&s=70 -[akihikodaki-link]: https://github.com/akihikodaki -[akihikodaki-icon]: https://avatars2.githubusercontent.com/u/17036990?s=70&v=4 -[rinsuki-link]: https://github.com/rinsuki -[rinsuki-icon]: https://avatars0.githubusercontent.com/u/6533808?s=70&v=4 -[tamaina-link]: https://github.com/tamaina -[tamaina-icon]: https://avatars1.githubusercontent.com/u/7973572?s=70&v=4 -[mirro-san-link]: https://github.com/mirro-san -[mirro-san-icon]: https://avatars1.githubusercontent.com/u/17948612?s=70&v=4 -[Conan-kun-link]: https://github.com/Conan-kun -[Conan-kun-icon]: https://avatars3.githubusercontent.com/u/30003708?s=70&v=4 -[m4sk1n-link]: https://github.com/m4sk1n -[m4sk1n-icon]: https://avatars3.githubusercontent.com/u/21127288?s=70&v=4 +[ooo-link]: https://www.patreon.com/user/creators?u=11601413 +[ooo-icon]: https://c10.patreonusercontent.com/3/eyJ2IjoiMSIsInciOjIwMH0%3D/patreon-media/user/11601413/20cb15f209924302b399b99d3c98b850?token-time=2145916800&token-hash=IO31nK6VZCMWBWU2VAk2c824BX2QZ4DNPKyHHZXS0iw%3D diff --git a/assets/favicon.ico b/assets/favicon.ico index d63c68b016..8e2b3ff4ca 100644 Binary files a/assets/favicon.ico and b/assets/favicon.ico differ diff --git a/assets/favicon/128.png b/assets/favicon/128.png deleted file mode 100644 index 1ccaaeee1c..0000000000 Binary files a/assets/favicon/128.png and /dev/null differ diff --git a/assets/favicon/128.svg b/assets/favicon/128.svg deleted file mode 100644 index 34888557b9..0000000000 Binary files a/assets/favicon/128.svg and /dev/null differ diff --git a/assets/favicon/16.png b/assets/favicon/16.png deleted file mode 100644 index a1d3e1be72..0000000000 Binary files a/assets/favicon/16.png and /dev/null differ diff --git a/assets/favicon/256.png b/assets/favicon/256.png deleted file mode 100644 index b3c4be42af..0000000000 Binary files a/assets/favicon/256.png and /dev/null differ diff --git a/assets/favicon/256.svg b/assets/favicon/256.svg deleted file mode 100644 index 5ecee9e0be..0000000000 Binary files a/assets/favicon/256.svg and /dev/null differ diff --git a/assets/favicon/32.png b/assets/favicon/32.png deleted file mode 100644 index f0466cce91..0000000000 Binary files a/assets/favicon/32.png and /dev/null differ diff --git a/assets/favicon/64.png b/assets/favicon/64.png deleted file mode 100644 index 9710052ae7..0000000000 Binary files a/assets/favicon/64.png and /dev/null differ diff --git a/assets/favicon/favicon.png b/assets/favicon/favicon.png new file mode 100644 index 0000000000..2efa2cad61 Binary files /dev/null and b/assets/favicon/favicon.png differ diff --git a/assets/favicon/16.svg b/assets/favicon/favicon.svg similarity index 98% rename from assets/favicon/16.svg rename to assets/favicon/favicon.svg index 03aa8bc6bd..9532def728 100644 Binary files a/assets/favicon/16.svg and b/assets/favicon/favicon.svg differ diff --git a/assets/mi.svg b/assets/mi.svg index d4f7cf7e9e..44e5c74d0c 100644 Binary files a/assets/mi.svg and b/assets/mi.svg differ diff --git a/cli/recount-stats.js b/cli/recount-stats.js new file mode 100644 index 0000000000..84cb5d0f57 --- /dev/null +++ b/cli/recount-stats.js @@ -0,0 +1,42 @@ +const { default: Note } = require('../built/models/note'); +const { default: Meta } = require('../built/models/meta'); +const { default: User } = require('../built/models/user'); + +async function main() { + const meta = await Meta.findOne({}); + + const notesCount = await Note.count(); + + const usersCount = await User.count(); + + const originalNotesCount = await Note.count({ + '_user.host': null + }); + + const originalUsersCount = await User.count({ + host: null + }); + + const stats = { + notesCount, + usersCount, + originalNotesCount, + originalUsersCount + }; + + if (meta) { + await Meta.update({}, { + $set: { + stats + } + }); + } else { + await Meta.insert({ + stats + }); + } +} + +main().then(() => { + console.log('done'); +}).catch(console.error); diff --git a/cli/suspend.js b/cli/suspend.js index 0f22bba477..877b37dbca 100644 --- a/cli/suspend.js +++ b/cli/suspend.js @@ -3,16 +3,21 @@ const User = require('../built/models/user').default; const args = process.argv.slice(2); -const userId = new mongo.ObjectID(args[0]); +const user = args[0]; -console.log(`Suspending ${userId}...`); +const q = user.startsWith('@') ? { + username: user.split('@')[1], + host: user.split('@')[2] || null +} : { _id: new mongo.ObjectID(user) }; -User.update({ _id: userId }, { +console.log(`Suspending ${user}...`); + +User.update(q, { $set: { isSuspended: true } }).then(() => { - console.log(`Suspended ${userId}`); + console.log(`Suspended ${user}`); }, e => { console.error(e); }); diff --git a/cli/update-remote-user.js b/cli/update-remote-user.js new file mode 100644 index 0000000000..b50cddddbe --- /dev/null +++ b/cli/update-remote-user.js @@ -0,0 +1,12 @@ +const updatePerson = require('../built/remote/activitypub/models/person').updatePerson; + +const args = process.argv.slice(2); +const user = args[0]; + +console.log(`Updating ${user}...`); + +updatePerson(user).then(() => { + console.log(`Updated ${user}`); +}, e => { + console.error(e); +}); diff --git a/docs/setup.en.md b/docs/setup.en.md index 8dde4d00d6..28e025521b 100644 --- a/docs/setup.en.md +++ b/docs/setup.en.md @@ -47,7 +47,14 @@ You need to generate config file via `npm run config` command. *5.* Build Misskey ---------------------------------------------------------------- -We need to use `node-gyp` to build the `crypto` module. + +Build misskey with the following: + +`npm run build` + +If you're on Debian, you will need to install the `build-essential` package. + +If you're still encountering errors about some modules, use node-gyp: 1. `npm install -g node-gyp` 2. `node-gyp configure` diff --git a/gulpfile.ts b/gulpfile.ts index fa1155878c..49a80879d2 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -8,12 +8,12 @@ import * as gutil from 'gulp-util'; import * as ts from 'gulp-typescript'; const sourcemaps = require('gulp-sourcemaps'); import tslint from 'gulp-tslint'; -import cssnano = require('gulp-cssnano'); +const cssnano = require('gulp-cssnano'); import * as uglifyComposer from 'gulp-uglify/composer'; import pug = require('gulp-pug'); import * as rimraf from 'rimraf'; import chalk from 'chalk'; -import imagemin = require('gulp-imagemin'); +const imagemin = require('gulp-imagemin'); import * as rename from 'gulp-rename'; import * as mocha from 'gulp-mocha'; import * as replace from 'gulp-replace'; diff --git a/locales/de.yml b/locales/de.yml index 1bcd3bd541..01c8767994 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -334,7 +334,7 @@ desktop/views/components/friends-maker.vue: refresh: "Mehr" close: "Schließen" desktop/views/components/game-window.vue: - game: "リバーシ" + game: "Reversi" desktop/views/components/home.vue: done: "Verbunden" add-widget: "Widget hinzufügen:" diff --git a/locales/en.yml b/locales/en.yml index 52800eb459..8b54a6616e 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -334,7 +334,7 @@ desktop/views/components/friends-maker.vue: refresh: "More" close: "Close" desktop/views/components/game-window.vue: - game: "リバーシ" + game: "Reversi" desktop/views/components/home.vue: done: "Submit" add-widget: "Add widget:" @@ -550,7 +550,7 @@ desktop/views/components/ui.header.nav.vue: home: "Home" deck: "Deck" messaging: "Messages" - game: "Play Othello" + game: "Play Reversi" desktop/views/components/ui.header.notifications.vue: title: "Notifications" desktop/views/components/ui.header.post.vue: diff --git a/locales/fr.yml b/locales/fr.yml index a69695732b..88253108d6 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -334,7 +334,7 @@ desktop/views/components/friends-maker.vue: refresh: "Plus" close: "Fermer" desktop/views/components/game-window.vue: - game: "リバーシ" + game: "Reversi" desktop/views/components/home.vue: done: "Envoyer" add-widget: "Ajouter un widget" diff --git a/locales/index.ts b/locales/index.ts index 319d178e0a..2ae84f30ae 100644 --- a/locales/index.ts +++ b/locales/index.ts @@ -5,12 +5,15 @@ import * as fs from 'fs'; import * as yaml from 'js-yaml'; -const loadLang = lang => yaml.safeLoad( - fs.readFileSync(`./locales/${lang}.yml`, 'utf-8')); +export type LangKey = 'de' | 'en' | 'fr' | 'ja' | 'pl'; +export type LocaleObject = { [key: string]: any }; + +const loadLang = (lang: LangKey) => yaml.safeLoad( + fs.readFileSync(`./locales/${lang}.yml`, 'utf-8')) as LocaleObject; const native = loadLang('ja'); -const langs = { +const langs: { [key: string]: LocaleObject } = { 'de': loadLang('de'), 'en': loadLang('en'), 'fr': loadLang('fr'), @@ -23,4 +26,8 @@ Object.entries(langs).map(([, locale]) => { locale = Object.assign({}, native, locale); }); +export function isAvailableLanguage(lang: string): lang is LangKey { + return lang in langs; +} + export default langs; diff --git a/locales/ja.yml b/locales/ja.yml index 0fcbca5361..29decefe68 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -3,7 +3,9 @@ meta: divider: "" common: - misskey: "Misskeyで皆と共有しよう。" + misskey: "A ⭐ of fediverse" + about-title: "A ⭐ of fediverse." + about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた分散マイクロブログSNSです。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。" time: unknown: "なぞのじかん" @@ -37,12 +39,62 @@ common: confused: "こまこまのこまり" pudding: "Pudding" + note-placeholders: + a: "今どうしてる?" + b: "何かありましたか?" + c: "何をお考えですか?" + d: "言いたいことは?" + e: "ここに書いてください" + f: "あなたが書くのを待っています..." + delete: "削除" loading: "読み込み中" ok: "わかった" update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。" my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。" + widgets: + analog-clock: "アナログ時計" + profile: "プロフィール" + calendar: "カレンダー" + timemachine: "カレンダー(タイムマシン)" + activity: "アクティビティ" + rss: "RSSリーダー" + memo: "付箋" + trends: "トレンド" + photo-stream: "フォトストリーム" + posts-monitor: "投稿チャート" + slideshow: "スライドショー" + version: "バージョン" + broadcast: "ブロードキャスト" + notifications: "通知" + users: "おすすめユーザー" + polls: "アンケート" + post-form: "投稿フォーム" + messaging: "メッセージ" + server: "サーバー情報" + donation: "寄付のお願い" + nav: "ナビゲーション" + tips: "ヒント" + hashtags: "ハッシュタグ" + + deck: + widgets: "ウィジェット" + home: "ホーム" + local: "ローカル" + global: "グローバル" + notifications: "通知" + list: "リスト" + swap-left: "左に移動" + swap-right: "右に移動" + swap-up: "上に移動" + swap-down: "下に移動" + remove: "カラムを削除" + add-column: "カラムを追加" + rename: "名前を変更" + stack-left: "左に重ねる" + pop-right: "右に出す" + common/views/components/connect-failed.vue: title: "サーバーに接続できません" description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。" @@ -104,6 +156,8 @@ common/views/components/nav.vue: common/views/components/note-menu.vue: favorite: "お気に入り" pin: "ピン留め" + delete: "削除" + delete-confirm: "この投稿を削除しますか?" remote: "投稿元で見る" common/views/components/poll.vue: @@ -115,11 +169,11 @@ common/views/components/poll.vue: voted: "投票済み" common/views/components/poll-editor.vue: - no-only-one-choice: "投票には、選択肢が最低2つ必要です" + no-only-one-choice: "アンケートには、選択肢が最低2つ必要です" choice-n: "選択肢{}" remove: "この選択肢を削除" add: "+選択肢を追加" - destroy: "投票を破棄" + destroy: "アンケートを破棄" common/views/components/reaction-picker.vue: choose-reaction: "リアクションを選択" @@ -197,10 +251,24 @@ common/views/widgets/photo-stream.vue: title: "フォトストリーム" no-photos: "写真はありません" +common/views/widgets/posts-monitor.vue: + title: "投稿チャート" + toggle: "表示を切り替え" + +common/views/widgets/hashtags.vue: + title: "ハッシュタグ" + count: "{}人が投稿" + empty: "トレンドなし" + common/views/widgets/server.vue: title: "サーバー情報" toggle: "表示を切り替え" +common/views/widgets/memo.vue: + title: "付箋" + memo: "ここに書いて!" + save: "保存" + desktop/views/components/activity.chart.vue: total: "Black ... Total" notes: "Blue ... Notes" @@ -291,8 +359,10 @@ desktop/views/components/drive.vue: url-upload: "URLからアップロード" desktop/views/components/follow-button.vue: - unfollow: "フォロー解除" - follow: "フォローする" + following: "フォロー中" + follow: "フォロー" + request-pending: "フォロー許可待ち" + follow-request: "フォロー申請" desktop/views/components/followers-window.vue: followers: "{} のフォロワー" @@ -314,30 +384,11 @@ desktop/views/components/friends-maker.vue: close: "閉じる" desktop/views/components/game-window.vue: - game: "オセロ" + game: "リバーシ" desktop/views/components/home.vue: done: "完了" add-widget: "ウィジェットを追加:" - profile: "プロフィール" - calendar: "カレンダー" - timemachine: "カレンダー(タイムマシン)" - activity: "アクティビティ" - rss: "RSSリーダー" - trends: "トレンド" - photostream: "フォトストリーム" - slideshow: "スライドショー" - version: "バージョン" - broadcast: "ブロードキャスト" - notifications: "通知" - users: "おすすめユーザー" - polls: "投票" - post-form: "投稿フォーム" - messaging: "メッセージ" - server: "サーバー情報" - donation: "寄付のお願い" - nav: "ナビゲーション" - tips: "ヒント" add: "追加" desktop/views/input-dialog.vue: @@ -352,21 +403,21 @@ desktop/views/components/messaging-window.vue: desktop/views/components/note-detail.vue: more: "会話をもっと読み込む" - private: "(この投稿は非公開です)" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" reposted-by: "{}がRenote" location: "位置情報" renote: "Renote" add-reaction: "リアクション" -desktop/views/components/note-detail.sub.vue: - private: "(この投稿は非公開です)" - desktop/views/components/notes.note.vue: reposted-by: "{}がRenote" reply: "返信" renote: "Renote" add-reaction: "リアクション" detail: "詳細" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" desktop/views/components/notes.vue: error: "読み込みに失敗しました。" @@ -377,10 +428,9 @@ desktop/views/components/notifications.vue: empty: "ありません!" desktop/views/components/post-form.vue: - note-placeholder: "いまどうしてる?" reply-placeholder: "この投稿への返信..." quote-placeholder: "この投稿を引用..." - note: "投稿" + submit: "投稿" reply: "返信" renote: "Renote" posted: "投稿しました!" @@ -394,7 +444,7 @@ desktop/views/components/post-form.vue: attach-media-from-drive: "ドライブからメディアを添付" attach-cancel: "添付取り消し" insert-a-kao: "v(‘ω’)v" - create-poll: "投票を作成" + create-poll: "アンケートを作成" text-remain: "残り{}文字" desktop/views/components/post-form-window.vue: @@ -531,7 +581,7 @@ desktop/views/components/settings.api.vue: token: "Token:" enter-password: "パスワードを入力してください" -desktop/views/components/settings.app.vue: +desktop/views/components/settings.apps.vue: no-apps: "連携しているアプリケーションはありません" desktop/views/components/settings.mute.vue: @@ -557,9 +607,10 @@ desktop/views/components/settings.profile.vue: is-cat: "このアカウントはCatです" desktop/views/components/sub-note-content.vue: - hidden: "(この投稿は非公開です)" - media: "つのメディア" - poll: "投票" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" + media-count: "{}つのメディア" + poll: "アンケート" desktop/views/components/taskmanager.vue: title: "タスクマネージャ" @@ -575,6 +626,7 @@ desktop/views/components/ui.header.account.vue: drive: "ドライブ" favorites: "お気に入り" lists: "リスト" + follow-requests: "フォロー申請" customize: "カスタマイズ" settings: "設定" signout: "サインアウト" @@ -582,6 +634,7 @@ desktop/views/components/ui.header.account.vue: desktop/views/components/ui.header.nav.vue: home: "ホーム" + deck: "デッキ" messaging: "メッセージ" game: "ゲーム" @@ -594,7 +647,13 @@ desktop/views/components/ui.header.post.vue: desktop/views/components/ui.header.search.vue: placeholder: "検索" +desktop/views/components/received-follow-requests-window.vue: + title: "フォロー申請" + accept: "承認" + reject: "拒否" + desktop/views/components/user-lists-window.vue: + title: "リスト" create-list: "リストを作成" desktop/views/components/user-preview.vue: @@ -615,7 +674,18 @@ desktop/views/components/window.vue: popout: "ポップアウト" close: "閉じる" +desktop/views/pages/deck/deck.tl-column.vue: + is-media-only: "メディア投稿のみ" + is-media-view: "メディアビュー" + +desktop/views/pages/deck/deck.note.vue: + reposted-by: "{}がRenote" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" + desktop/views/pages/welcome.vue: + about: "詳しく..." + gotit: "わかった" signin: "ログイン" signup: "新規登録" signin-button: "やってる" @@ -692,14 +762,13 @@ desktop/views/widgets/notifications.vue: settings: "通知の設定" desktop/views/widgets/polls.vue: - title: "投票" + title: "アンケート" refresh: "他を見る" nothing: "ありません!" desktop/views/widgets/post-form.vue: title: "投稿" note: "投稿" - placeholder: "いまどうしてる?" desktop/views/widgets/profile.vue: update-banner: "クリックでバナー編集" @@ -724,6 +793,16 @@ mobile/views/components/drive.vue: load-more: "もっと読み込む" nothing-in-drive: "ドライブには何もありません" folder-is-empty: "このフォルダは空です" + prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>" + deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。" + folder-name: "フォルダー名" + root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。" + root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。" + url-prompt: "アップロードしたいファイルのURL" + uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。" + +mobile/views/components/drive-file-detail.vue: + rename: "名前を変更" mobile/views/components/drive-file-chooser.vue: select-file: "ファイルを選択" @@ -739,42 +818,86 @@ mobile/views/components/drive.file-detail.vue: exif: "EXIF" mobile/views/components/follow-button.vue: + following: "フォロー中" follow: "フォロー" - unfollow: "フォロー解除" + request-pending: "フォロー許可待ち" + follow-request: "フォロー申請" + +mobile/views/components/friends-maker.vue: + title: "気になるユーザーをフォロー" + empty: "おすすめのユーザーは見つかりませんでした。" + fetching: "読み込んでいます" + refresh: "もっと見る" + close: "閉じる" mobile/views/components/note.vue: reposted-by: "{}がRenote" + more: "もっと見る" + less: "隠す" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" + location: "位置情報" mobile/views/components/note-detail.vue: reply: "返信" reaction: "リアクション" + reposted-by: "{}がRenote" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" + location: "位置情報" + +mobile/views/components/note-preview.vue: + admin: "admin" + bot: "bot" + cat: "cat" + +mobile/views/components/note-sub.vue: + admin: "admin" + bot: "bot" + cat: "cat" + +mobile/views/components/notes.vue: + failed: "読み込みに失敗しました。" + retry: "リトライ" mobile/views/components/notifications.vue: more: "もっと見る" empty: "ありません!" mobile/views/components/post-form.vue: + add-visible-user: "ユーザーを追加" submit: "投稿" reply: "返信" renote: "Renote" - renote-placeholder: "この投稿を引用... (オプション)" + quote-placeholder: "この投稿を引用... (オプション)" reply-placeholder: "この投稿への返信..." - note-placeholder: "いまどうしてる?" + cw-placeholder: "内容への注釈 (オプション)" + location-alert: "お使いの端末は位置情報に対応していません" + error: "エラー" + username-prompt: "ユーザー名を入力してください" mobile/views/components/sub-note-content.vue: - media-count: "{}個のメディア" - poll: "投票" + private: "この投稿は非公開です" + deleted: "この投稿は削除されました" + media-count: "{}つのメディア" + poll: "アンケート" mobile/views/components/timeline.vue: empty: "投稿がありません" load-more: "もっと" mobile/views/components/ui.nav.vue: - home: "ホーム" + timeline: "タイムライン" notifications: "通知" messaging: "メッセージ" + follow-requests: "フォロー申請" search: "検索" drive: "ドライブ" + favorites: "お気に入り" + user-lists: "リスト" + widgets: "ウィジェット" + game: "ゲーム" + darkmode: "ダークモード" settings: "設定" about: "Misskeyについて" @@ -788,8 +911,16 @@ mobile/views/components/users-list.vue: known: "知り合い" load-more: "もっと" +mobile/views/pages/favorites.vue: + title: "お気に入り" + +mobile/views/pages/user-lists.vue: + title: "リスト" + enter-list-name: "リスト名を入力してください" + mobile/views/pages/drive.vue: drive: "ドライブ" + more: "もっと見る" mobile/views/pages/followers.vue: followers-of: "{}のフォロワー" @@ -808,6 +939,11 @@ mobile/views/pages/messaging.vue: mobile/views/pages/messaging-room.vue: messaging: "メッセージ" +mobile/views/pages/received-follow-requests.vue: + title: "フォロー申請" + accept: "承認" + reject: "拒否" + mobile/views/pages/note.vue: title: "投稿" prev: "前の投稿" diff --git a/locales/pl.yml b/locales/pl.yml index d3bafff06d..edb2466c82 100644 --- a/locales/pl.yml +++ b/locales/pl.yml @@ -334,7 +334,7 @@ desktop/views/components/friends-maker.vue: refresh: "Więcej" close: "Zamknij" desktop/views/components/game-window.vue: - game: "リバーシ" + game: "Reversi" desktop/views/components/home.vue: done: "Wyślij" add-widget: "Dodaj widżet:" diff --git a/package.json b/package.json index 534b0c296d..6691723386 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "misskey", "author": "syuilo ", - "version": "2.17.0", - "clientVersion": "1.0.5731", + "version": "4.1.1", + "clientVersion": "1.0.6542", "codename": "nighthike", "main": "./built/index.js", "private": true, @@ -23,10 +23,10 @@ "format": "gulp format" }, "dependencies": { - "@fortawesome/fontawesome": "1.0.1", - "@fortawesome/fontawesome-free-brands": "5.0.2", - "@fortawesome/fontawesome-free-regular": "5.0.2", - "@fortawesome/fontawesome-free-solid": "5.0.2", + "@fortawesome/fontawesome": "1.1.8", + "@fortawesome/fontawesome-free-brands": "5.0.13", + "@fortawesome/fontawesome-free-regular": "5.0.13", + "@fortawesome/fontawesome-free-solid": "5.0.13", "@koa/cors": "2.2.1", "@prezzemolo/rap": "0.1.2", "@prezzemolo/zip": "0.0.3", @@ -34,7 +34,6 @@ "@types/debug": "0.0.30", "@types/deep-equal": "1.0.1", "@types/elasticsearch": "5.0.23", - "@types/eventemitter3": "2.0.2", "@types/gm": "1.18.0", "@types/gulp": "3.8.36", "@types/gulp-htmlmin": "1.3.32", @@ -63,7 +62,6 @@ "@types/mkdirp": "0.5.2", "@types/mocha": "5.2.0", "@types/mongodb": "3.0.18", - "@types/monk": "6.0.0", "@types/ms": "0.7.30", "@types/node": "10.1.2", "@types/nopt": "3.0.29", @@ -114,7 +112,7 @@ "gulp-cssnano": "2.1.3", "gulp-htmlmin": "4.0.0", "gulp-imagemin": "4.1.0", - "gulp-mocha": "5.0.0", + "gulp-mocha": "6.0.0", "gulp-pug": "4.0.1", "gulp-rename": "1.2.3", "gulp-replace": "1.0.0", @@ -124,17 +122,17 @@ "gulp-typescript": "4.0.2", "gulp-uglify": "3.0.0", "gulp-util": "3.0.8", - "hard-source-webpack-plugin": "0.6.9", + "hard-source-webpack-plugin": "0.6.10", "highlight.js": "9.12.0", - "html-minifier": "3.5.15", + "html-minifier": "3.5.16", "http-signature": "1.2.0", "inquirer": "5.2.0", "is-root": "2.0.0", "is-url": "1.2.4", "js-yaml": "3.11.0", - "jsdom": "11.10.0", + "jsdom": "11.11.0", "koa": "2.5.1", - "koa-bodyparser": "4.2.0", + "koa-bodyparser": "4.2.1", "koa-compress": "3.0.0", "koa-favicon": "2.0.1", "koa-json-body": "5.3.0", @@ -152,7 +150,7 @@ "mkdirp": "0.5.1", "mocha": "5.2.0", "moji": "0.5.1", - "mongodb": "3.0.8", + "mongodb": "3.0.10", "monk": "6.0.6", "ms": "2.1.1", "nan": "2.10.0", @@ -163,18 +161,18 @@ "object-assign-deep": "0.4.0", "on-build-webpack": "0.1.0", "os-utils": "0.0.14", - "parse5": "4.0.0", + "parse5": "5.0.0", "progress-bar-webpack-plugin": "1.11.0", "prominence": "0.2.0", "promise-sequential": "1.1.1", "pug": "2.0.3", - "punycode": "2.1.0", + "punycode": "2.1.1", "qrcode": "1.2.0", "ratelimiter": "3.0.3", "recaptcha-promise": "0.1.3", "reconnecting-websocket": "3.2.2", "redis": "2.8.0", - "request": "2.86.0", + "request": "2.87.0", "request-promise-native": "1.0.5", "rimraf": "2.6.2", "rndstr": "1.0.0", @@ -193,7 +191,7 @@ "textarea-caret": "3.1.0", "tmp": "0.0.33", "ts-loader": "4.3.0", - "ts-node": "6.0.3", + "ts-node": "6.0.4", "tslint": "5.10.0", "typescript": "2.8.3", "typescript-eslint-parser": "15.0.0", @@ -205,8 +203,7 @@ "vue-cropperjs": "2.2.0", "vue-js-modal": "1.3.13", "vue-json-tree-view": "2.1.4", - "vue-loader": "15.1.0", - "vue-material": "^1.0.0-beta-10.2", + "vue-loader": "15.2.1", "vue-router": "3.0.1", "vue-template-compiler": "2.5.16", "vuedraggable": "2.16.0", @@ -214,10 +211,14 @@ "vuex-persistedstate": "^2.5.4", "web-push": "3.3.1", "webfinger.js": "2.6.6", - "webpack": "4.8.3", - "webpack-cli": "2.1.3", + "webpack": "4.9.1", + "webpack-cli": "2.1.4", "websocket": "1.0.26", - "ws": "5.1.1", - "xev": "2.0.0" + "ws": "5.2.0", + "xev": "2.0.1" + }, + "devDependencies": { + "@types/file-type": "5.2.1", + "@types/jsdom": "11.0.5" } } diff --git a/src/acct/parse.ts b/src/acct/parse.ts index ef1f55405d..0c00fccef6 100644 --- a/src/acct/parse.ts +++ b/src/acct/parse.ts @@ -1,4 +1,4 @@ -export default acct => { +export default (acct: string) => { const splitted = acct.split('@', 2); return { username: splitted[0], host: splitted[1] || null }; }; diff --git a/src/acct/render.ts b/src/acct/render.ts index 9afb03d88b..c29bfb4764 100644 --- a/src/acct/render.ts +++ b/src/acct/render.ts @@ -1,3 +1,5 @@ -export default user => { +import { IUser } from '../models/user'; + +export default (user: IUser) => { return user.host === null ? user.username : `${user.username}@${user.host}`; }; diff --git a/src/build/fa.ts b/src/build/fa.ts index 111c19ae66..077bb51e6d 100644 --- a/src/build/fa.ts +++ b/src/build/fa.ts @@ -3,18 +3,18 @@ */ import * as fontawesome from '@fortawesome/fontawesome'; -import * as regular from '@fortawesome/fontawesome-free-regular'; -import * as solid from '@fortawesome/fontawesome-free-solid'; -import * as brands from '@fortawesome/fontawesome-free-brands'; +import regular from '@fortawesome/fontawesome-free-regular'; +import solid from '@fortawesome/fontawesome-free-solid'; +import brands from '@fortawesome/fontawesome-free-brands'; fontawesome.library.add(regular, solid, brands); export const pattern = /%fa:(.+?)%/g; -export const replacement = (match, key) => { +export const replacement = (match: string, key: string) => { const args = key.split(' '); let prefix = 'fas'; - const classes = []; + const classes: string[] = []; let transform = ''; let name; @@ -34,12 +34,12 @@ export const replacement = (match, key) => { } }); - const icon = fontawesome.icon({ prefix, iconName: name }, { - classes: classes + const icon = fontawesome.icon({ prefix, iconName: name } as fontawesome.IconLookup, { + classes: classes, + transform: fontawesome.parse.transform(transform) }); if (icon) { - icon.transform = fontawesome.parse.transform(transform); return `${icon.html[0]}`; } else { console.warn(`'${name}' not found in fa`); diff --git a/src/build/i18n.ts b/src/build/i18n.ts index 35854055d0..dc48251c95 100644 --- a/src/build/i18n.ts +++ b/src/build/i18n.ts @@ -2,7 +2,7 @@ * Replace i18n texts */ -import locale from '../../locales'; +import locale, { isAvailableLanguage, LocaleObject } from '../../locales'; export default class Replacer { private lang: string; @@ -16,19 +16,19 @@ export default class Replacer { this.replacement = this.replacement.bind(this); } - private get(path: string, key: string) { - const texts = locale[this.lang]; - - if (texts == null) { + private get(path: string, key: string): string { + if (!isAvailableLanguage(this.lang)) { console.warn(`lang '${this.lang}' is not supported`); return key; // Fallback } + const texts = locale[this.lang]; + let text = texts; if (path) { if (text.hasOwnProperty(path)) { - text = text[path]; + text = text[path] as LocaleObject; } else { console.warn(`path '${path}' not found in '${this.lang}'`); return key; // Fallback @@ -38,7 +38,7 @@ export default class Replacer { // Check the key existance const error = key.split('.').some(k => { if (text.hasOwnProperty(k)) { - text = text[k]; + text = (text as LocaleObject)[k]; return false; } else { return true; @@ -48,12 +48,15 @@ export default class Replacer { if (error) { console.warn(`key '${key}' not found in '${path}' of '${this.lang}'`); return key; // Fallback + } else if (typeof text !== 'string') { + console.warn(`key '${key}' is not string in '${path}' of '${this.lang}'`); + return key; // Fallback } else { return text; } } - public replacement(match, key) { + public replacement(match: string, key: string) { let path = null; if (key.indexOf('|') != -1) { diff --git a/src/cafy-id.ts b/src/cafy-id.ts index 310b1eb20b..dac0f97bd2 100644 --- a/src/cafy-id.ts +++ b/src/cafy-id.ts @@ -1,8 +1,8 @@ import * as mongo from 'mongodb'; import { Query } from 'cafy'; -export const isAnId = x => mongo.ObjectID.isValid(x); -export const isNotAnId = x => !isAnId(x); +export const isAnId = (x: any) => mongo.ObjectID.isValid(x); +export const isNotAnId = (x: any) => !isAnId(x); /** * ID diff --git a/src/client/app/app.styl b/src/client/app/app.styl index ba694b73ae..431b9daa65 100644 --- a/src/client/app/app.styl +++ b/src/client/app/app.styl @@ -7,11 +7,6 @@ html cursor progress !important body - // for md - font-size 16px !important - line-height initial !important - letter-spacing initial !important - overflow-wrap break-word #error diff --git a/src/client/app/auth/assets/icon.svg b/src/client/app/auth/assets/icon.svg new file mode 100644 index 0000000000..36f5d3e404 Binary files /dev/null and b/src/client/app/auth/assets/icon.svg differ diff --git a/src/client/app/auth/assets/logo.svg b/src/client/app/auth/assets/logo.svg deleted file mode 100644 index 19b8a2737e..0000000000 Binary files a/src/client/app/auth/assets/logo.svg and /dev/null differ diff --git a/src/client/app/auth/script.ts b/src/client/app/auth/script.ts index 20f59bf033..fd985c46ad 100644 --- a/src/client/app/auth/script.ts +++ b/src/client/app/auth/script.ts @@ -20,6 +20,7 @@ init(launch => { // Init router const router = new VueRouter({ mode: 'history', + base: '/auth/', routes: [ { path: '/:token', component: Index }, ] diff --git a/src/client/app/auth/views/index.vue b/src/client/app/auth/views/index.vue index 0fcd9bfe53..6d0ba3cda3 100644 --- a/src/client/app/auth/views/index.vue +++ b/src/client/app/auth/views/index.vue @@ -1,8 +1,9 @@ @@ -51,7 +52,7 @@ export default Vue.extend({ } }, mounted() { - if (!this.$root.$data.os.isSignedIn) return; + if (!this.$store.getters.isSignedIn) return; // Fetch session (this as any).api('auth/session/show', { @@ -62,7 +63,7 @@ export default Vue.extend({ // 既に連携していた場合 if (this.session.app.isAuthorized) { - this.$root.$data.os.api('auth/accept', { + (this as any).api('auth/accept', { token: this.session.token }).then(() => { this.accepted(); @@ -72,6 +73,7 @@ export default Vue.extend({ } }).catch(error => { this.state = 'fetch-session-error'; + this.fetching = false; }); }, methods: { @@ -101,7 +103,7 @@ export default Vue.extend({ padding 32px color #555 - > div + > div:not(.form) padding 64px > h1 @@ -142,8 +144,8 @@ export default Vue.extend({ > footer > img display block - width 64px - height 64px - margin 0 auto + width 32px + height 32px + margin 16px auto diff --git a/src/client/app/base.pug b/src/client/app/base.pug index c182fd6f64..11b150bc67 100644 --- a/src/client/app/base.pug +++ b/src/client/app/base.pug @@ -19,7 +19,7 @@ html | Misskey block desc - meta(name='description' content='A SNS') + meta(name='description' content='A planet of fediverse') block meta @@ -42,7 +42,7 @@ html | JavaScriptを有効にしてください br | Please turn on your JavaScript - div#ini: p - span . - span . - span . + div#ini. + + + diff --git a/src/client/app/boot.js b/src/client/app/boot.js index 7b884c8a54..08c3fdeaee 100644 --- a/src/client/app/boot.js +++ b/src/client/app/boot.js @@ -32,9 +32,9 @@ //#region Detect app name let app = null; - if (url.pathname == '/docs') app = 'docs'; - if (url.pathname == '/dev') app = 'dev'; - if (url.pathname == '/auth') app = 'auth'; + if (url.pathname == '/docs' || url.pathname.startsWith('/docs/')) app = 'docs'; + if (url.pathname == '/dev' || url.pathname.startsWith('/dev/')) app = 'dev'; + if (url.pathname == '/auth' || url.pathname.startsWith('/auth/')) app = 'auth'; //#endregion //#region Detect the user language diff --git a/src/client/app/common/define-widget.ts b/src/client/app/common/define-widget.ts index 0b2bc36566..2fae28be72 100644 --- a/src/client/app/common/define-widget.ts +++ b/src/client/app/common/define-widget.ts @@ -9,9 +9,9 @@ export default function(data: { widget: { type: Object }, - isMobile: { - type: Boolean, - default: false + platform: { + type: String, + required: true }, isCustomizeMode: { type: Boolean, @@ -66,17 +66,10 @@ export default function(data: { this.bakeProps(); - if (this.isMobile) { - (this as any).api('i/update_mobile_home', { - id: this.id, - data: this.props - }); - } else { - (this as any).api('i/update_home', { - id: this.id, - data: this.props - }); - } + (this as any).api('i/update_widget', { + id: this.id, + data: this.props + }); } } }); diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts index c19b1c5ad0..cc28f75998 100644 --- a/src/client/app/common/scripts/compose-notification.ts +++ b/src/client/app/common/scripts/compose-notification.ts @@ -55,7 +55,7 @@ export default function(type, data): Notification { icon: data.user.avatarUrl + '?thumbnail&size=64' }; - case 'othello_invited': + case 'reversi_invited': return { title: '対局への招待があります', body: `${getUserName(data.parent)}さんから`, diff --git a/src/client/app/common/scripts/streaming/home.ts b/src/client/app/common/scripts/streaming/home.ts index 44d07e331a..dd18c70d70 100644 --- a/src/client/app/common/scripts/streaming/home.ts +++ b/src/client/app/common/scripts/streaming/home.ts @@ -1,5 +1,3 @@ -import * as merge from 'object-assign-deep'; - import Stream from './stream'; import StreamManager from './stream-manager'; import MiOS from '../../../mios'; @@ -20,14 +18,36 @@ export class HomeStream extends Stream { }, 1000 * 60); // 自分の情報が更新されたとき - this.on('i_updated', i => { + this.on('meUpdated', i => { if (os.debug) { console.log('I updated:', i); } - merge(me, i); - // キャッシュ更新 - os.bakeMe(); + os.store.dispatch('mergeMe', i); + }); + + this.on('read_all_notifications', () => { + os.store.dispatch('mergeMe', { + hasUnreadNotification: false + }); + }); + + this.on('unread_notification', () => { + os.store.dispatch('mergeMe', { + hasUnreadNotification: true + }); + }); + + this.on('read_all_messaging_messages', () => { + os.store.dispatch('mergeMe', { + hasUnreadMessagingMessage: false + }); + }); + + this.on('unread_messaging_message', () => { + os.store.dispatch('mergeMe', { + hasUnreadMessagingMessage: true + }); }); this.on('clientSettingUpdated', x => { @@ -38,25 +58,18 @@ export class HomeStream extends Stream { }); this.on('home_updated', x => { - if (x.home) { - os.store.commit('settings/setHome', x.home); - } else { - os.store.commit('settings/setHomeWidget', { - id: x.id, - data: x.data - }); - } + os.store.commit('settings/setHome', x); }); this.on('mobile_home_updated', x => { - if (x.home) { - os.store.commit('settings/setMobileHome', x.home); - } else { - os.store.commit('settings/setMobileHomeWidget', { - id: x.id, - data: x.data - }); - } + os.store.commit('settings/setMobileHome', x); + }); + + this.on('widgetUpdated', x => { + os.store.commit('settings/setWidget', { + id: x.id, + data: x.data + }); }); // トークンが再生成されたとき diff --git a/src/client/app/common/scripts/streaming/server.ts b/src/client/app/common/scripts/streaming/notes-stats.ts similarity index 57% rename from src/client/app/common/scripts/streaming/server.ts rename to src/client/app/common/scripts/streaming/notes-stats.ts index 2ea4239288..9e3e78a709 100644 --- a/src/client/app/common/scripts/streaming/server.ts +++ b/src/client/app/common/scripts/streaming/notes-stats.ts @@ -3,15 +3,15 @@ import StreamManager from './stream-manager'; import MiOS from '../../../mios'; /** - * Server stream connection + * Notes stats stream connection */ -export class ServerStream extends Stream { +export class NotesStatsStream extends Stream { constructor(os: MiOS) { - super(os, 'server'); + super(os, 'notes-stats'); } } -export class ServerStreamManager extends StreamManager { +export class NotesStatsStreamManager extends StreamManager { private os: MiOS; constructor(os: MiOS) { @@ -22,7 +22,7 @@ export class ServerStreamManager extends StreamManager { public getConnection() { if (this.connection == null) { - this.connection = new ServerStream(this.os); + this.connection = new NotesStatsStream(this.os); } return this.connection; diff --git a/src/client/app/common/scripts/streaming/othello-game.ts b/src/client/app/common/scripts/streaming/reversi-game.ts similarity index 66% rename from src/client/app/common/scripts/streaming/othello-game.ts rename to src/client/app/common/scripts/streaming/reversi-game.ts index 9e36f647bb..5638b3013f 100644 --- a/src/client/app/common/scripts/streaming/othello-game.ts +++ b/src/client/app/common/scripts/streaming/reversi-game.ts @@ -1,9 +1,9 @@ import Stream from './stream'; import MiOS from '../../../mios'; -export class OthelloGameStream extends Stream { +export class ReversiGameStream extends Stream { constructor(os: MiOS, me, game) { - super(os, 'othello-game', { + super(os, 'reversi-game', { i: me ? me.token : null, game: game.id }); diff --git a/src/client/app/common/scripts/streaming/othello.ts b/src/client/app/common/scripts/streaming/reversi.ts similarity index 66% rename from src/client/app/common/scripts/streaming/othello.ts rename to src/client/app/common/scripts/streaming/reversi.ts index 8f4f217e39..2e4395f0f1 100644 --- a/src/client/app/common/scripts/streaming/othello.ts +++ b/src/client/app/common/scripts/streaming/reversi.ts @@ -2,15 +2,15 @@ import StreamManager from './stream-manager'; import Stream from './stream'; import MiOS from '../../../mios'; -export class OthelloStream extends Stream { +export class ReversiStream extends Stream { constructor(os: MiOS, me) { - super(os, 'othello', { + super(os, 'reversi', { i: me.token }); } } -export class OthelloStreamManager extends StreamManager { +export class ReversiStreamManager extends StreamManager { private me; private os: MiOS; @@ -23,7 +23,7 @@ export class OthelloStreamManager extends StreamManager { public getConnection() { if (this.connection == null) { - this.connection = new OthelloStream(this.os, this.me); + this.connection = new ReversiStream(this.os, this.me); } return this.connection; diff --git a/src/client/app/common/scripts/streaming/server-stats.ts b/src/client/app/common/scripts/streaming/server-stats.ts new file mode 100644 index 0000000000..9983dfcaf0 --- /dev/null +++ b/src/client/app/common/scripts/streaming/server-stats.ts @@ -0,0 +1,30 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../../mios'; + +/** + * Server stats stream connection + */ +export class ServerStatsStream extends Stream { + constructor(os: MiOS) { + super(os, 'server-stats'); + } +} + +export class ServerStatsStreamManager extends StreamManager { + private os: MiOS; + + constructor(os: MiOS) { + super(); + + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new ServerStatsStream(this.os); + } + + return this.connection; + } +} diff --git a/src/client/app/common/views/components/analog-clock.vue b/src/client/app/common/views/components/analog-clock.vue new file mode 100644 index 0000000000..53fb2a8dad --- /dev/null +++ b/src/client/app/common/views/components/analog-clock.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue index 3e1b17635f..a65b62882f 100644 --- a/src/client/app/common/views/components/avatar.vue +++ b/src/client/app/common/views/components/avatar.vue @@ -32,7 +32,7 @@ export default Vue.extend({ ? `rgb(${ this.user.avatarColor.join(',') })` : null, backgroundImage: this.lightmode ? null : `url(${ this.user.avatarUrl }?thumbnail)`, - borderRadius: (this as any).clientSettings.circleIcons ? '100%' : null + borderRadius: this.$store.state.settings.circleIcons ? '100%' : null }; } } diff --git a/src/client/app/common/views/components/forkit.vue b/src/client/app/common/views/components/forkit.vue index 2a463ebfd7..de627181ef 100644 --- a/src/client/app/common/views/components/forkit.vue +++ b/src/client/app/common/views/components/forkit.vue @@ -13,9 +13,6 @@ .a display block - position absolute - top 0 - right 0 > svg display block diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index c1a7bc61d7..5b2fa084fb 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -1,5 +1,8 @@ import Vue from 'vue'; +import analogClock from './analog-clock.vue'; +import menu from './menu.vue'; +import noteHeader from './note-header.vue'; import signin from './signin.vue'; import signup from './signup.vue'; import forkit from './forkit.vue'; @@ -24,9 +27,20 @@ import urlPreview from './url-preview.vue'; import twitterSetting from './twitter-setting.vue'; import fileTypeIcon from './file-type-icon.vue'; import Switch from './switch.vue'; -import Othello from './othello.vue'; +import Reversi from './reversi.vue'; import welcomeTimeline from './welcome-timeline.vue'; +import uiInput from './ui/input.vue'; +import uiButton from './ui/button.vue'; +import uiCard from './ui/card.vue'; +import uiForm from './ui/form.vue'; +import uiTextarea from './ui/textarea.vue'; +import uiSwitch from './ui/switch.vue'; +import uiRadio from './ui/radio.vue'; +import uiSelect from './ui/select.vue'; +Vue.component('mk-analog-clock', analogClock); +Vue.component('mk-menu', menu); +Vue.component('mk-note-header', noteHeader); Vue.component('mk-signin', signin); Vue.component('mk-signup', signup); Vue.component('mk-forkit', forkit); @@ -51,5 +65,13 @@ Vue.component('mk-url-preview', urlPreview); Vue.component('mk-twitter-setting', twitterSetting); Vue.component('mk-file-type-icon', fileTypeIcon); Vue.component('mk-switch', Switch); -Vue.component('mk-othello', Othello); +Vue.component('mk-reversi', Reversi); Vue.component('mk-welcome-timeline', welcomeTimeline); +Vue.component('ui-input', uiInput); +Vue.component('ui-button', uiButton); +Vue.component('ui-card', uiCard); +Vue.component('ui-form', uiForm); +Vue.component('ui-textarea', uiTextarea); +Vue.component('ui-switch', uiSwitch); +Vue.component('ui-radio', uiRadio); +Vue.component('ui-select', uiSelect); diff --git a/src/client/app/common/views/components/media-list.vue b/src/client/app/common/views/components/media-list.vue index ff9d5e1022..2f8a1943ad 100644 --- a/src/client/app/common/views/components/media-list.vue +++ b/src/client/app/common/views/components/media-list.vue @@ -1,9 +1,11 @@ @@ -18,47 +20,60 @@ export default Vue.extend({ raw: { default: false } + }, + mounted() { + // for Safari bug + this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px` : '128px'; } }); diff --git a/src/client/app/common/views/components/menu.vue b/src/client/app/common/views/components/menu.vue new file mode 100644 index 0000000000..9b16732b9a --- /dev/null +++ b/src/client/app/common/views/components/menu.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/src/client/app/common/views/components/messaging-room.message.vue b/src/client/app/common/views/components/messaging-room.message.vue index ef39199dc4..a77b5f3658 100644 --- a/src/client/app/common/views/components/messaging-room.message.vue +++ b/src/client/app/common/views/components/messaging-room.message.vue @@ -8,7 +8,7 @@ Delete
- +
@@ -42,7 +42,7 @@ export default Vue.extend({ }, computed: { isMe(): boolean { - return this.message.userId == (this as any).os.i.id; + return this.message.userId == this.$store.state.i.id; }, urls(): string[] { if (this.message.text) { diff --git a/src/client/app/common/views/components/messaging-room.vue b/src/client/app/common/views/components/messaging-room.vue index 79756b22eb..b2831d6928 100644 --- a/src/client/app/common/views/components/messaging-room.vue +++ b/src/client/app/common/views/components/messaging-room.vue @@ -72,7 +72,7 @@ export default Vue.extend({ }, mounted() { - this.connection = new MessagingStream((this as any).os, (this as any).os.i, this.user.id); + this.connection = new MessagingStream((this as any).os, this.$store.state.i, this.user.id); this.connection.on('message', this.onMessage); this.connection.on('read', this.onRead); @@ -164,7 +164,7 @@ export default Vue.extend({ const isBottom = this.isBottom(); this.messages.push(message); - if (message.userId != (this as any).os.i.id && !document.hidden) { + if (message.userId != this.$store.state.i.id && !document.hidden) { this.connection.send({ type: 'read', id: message.id @@ -176,7 +176,7 @@ export default Vue.extend({ this.$nextTick(() => { this.scrollToBottom(); }); - } else if (message.userId != (this as any).os.i.id) { + } else if (message.userId != this.$store.state.i.id) { // Notify this.notifyNewMessage(); } @@ -229,7 +229,7 @@ export default Vue.extend({ onVisibilitychange() { if (document.hidden) return; this.messages.forEach(message => { - if (message.userId !== (this as any).os.i.id && !message.isRead) { + if (message.userId !== this.$store.state.i.id && !message.isRead) { this.connection.send({ type: 'read', id: message.id diff --git a/src/client/app/common/views/components/messaging.vue b/src/client/app/common/views/components/messaging.vue index 11f9c366d4..2ddec29984 100644 --- a/src/client/app/common/views/components/messaging.vue +++ b/src/client/app/common/views/components/messaging.vue @@ -95,7 +95,7 @@ export default Vue.extend({ methods: { getAcct, isMe(message) { - return message.userId == (this as any).os.i.id; + return message.userId == this.$store.state.i.id; }, onMessage(message) { this.messages = this.messages.filter(m => !( diff --git a/src/client/app/common/views/components/note-header.vue b/src/client/app/common/views/components/note-header.vue new file mode 100644 index 0000000000..6e64a6a6d3 --- /dev/null +++ b/src/client/app/common/views/components/note-header.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/src/client/app/common/views/components/note-html.ts b/src/client/app/common/views/components/note-html.ts index f86b50659e..8fa5f380dd 100644 --- a/src/client/app/common/views/components/note-html.ts +++ b/src/client/app/common/views/components/note-html.ts @@ -40,6 +40,17 @@ export default Vue.component('mk-note-html', { ast = this.ast; } + if (ast.filter(x => x.type != 'hashtag').length == 0) { + return; + } + + while (ast[ast.length - 1] && ( + ast[ast.length - 1].type == 'hashtag' || + (ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == ' ') || + (ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == '\n'))) { + ast.pop(); + } + // Parse ast to DOM const els = flatten(ast.map(token => { switch (token.type) { @@ -92,7 +103,7 @@ export default Vue.component('mk-note-html', { case 'hashtag': return createElement('a', { attrs: { - href: `${url}/search?q=${token.content}`, + href: `${url}/tags/${token.hashtag}`, target: '_blank' } }, token.content); diff --git a/src/client/app/common/views/components/note-menu.vue b/src/client/app/common/views/components/note-menu.vue index 88dc22aaf4..27a49a6536 100644 --- a/src/client/app/common/views/components/note-menu.vue +++ b/src/client/app/common/views/components/note-menu.vue @@ -1,54 +1,45 @@ - - diff --git a/src/client/app/common/views/components/othello.game.vue b/src/client/app/common/views/components/reversi.game.vue similarity index 91% rename from src/client/app/common/views/components/othello.game.vue rename to src/client/app/common/views/components/reversi.game.vue index ea75558d10..dc79c95bb8 100644 --- a/src/client/app/common/views/components/othello.game.vue +++ b/src/client/app/common/views/components/reversi.game.vue @@ -43,7 +43,7 @@ + + diff --git a/src/client/app/common/views/components/ui/card.vue b/src/client/app/common/views/components/ui/card.vue new file mode 100644 index 0000000000..05c51bca6b --- /dev/null +++ b/src/client/app/common/views/components/ui/card.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/form.vue b/src/client/app/common/views/components/ui/form.vue new file mode 100644 index 0000000000..fc8fdad9c4 --- /dev/null +++ b/src/client/app/common/views/components/ui/form.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue new file mode 100644 index 0000000000..ce28bfb12a --- /dev/null +++ b/src/client/app/common/views/components/ui/input.vue @@ -0,0 +1,350 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/radio.vue b/src/client/app/common/views/components/ui/radio.vue new file mode 100644 index 0000000000..04a46c5a96 --- /dev/null +++ b/src/client/app/common/views/components/ui/radio.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/select.vue b/src/client/app/common/views/components/ui/select.vue new file mode 100644 index 0000000000..4273a4a0de --- /dev/null +++ b/src/client/app/common/views/components/ui/select.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/switch.vue b/src/client/app/common/views/components/ui/switch.vue new file mode 100644 index 0000000000..a9e00d73d2 --- /dev/null +++ b/src/client/app/common/views/components/ui/switch.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/textarea.vue b/src/client/app/common/views/components/ui/textarea.vue new file mode 100644 index 0000000000..60fe1cdd82 --- /dev/null +++ b/src/client/app/common/views/components/ui/textarea.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/src/client/app/common/views/components/uploader.vue b/src/client/app/common/views/components/uploader.vue index a6caa80f3c..f4797d89f7 100644 --- a/src/client/app/common/views/components/uploader.vue +++ b/src/client/app/common/views/components/uploader.vue @@ -50,7 +50,7 @@ export default Vue.extend({ reader.readAsDataURL(file); const data = new FormData(); - data.append('i', (this as any).os.i.token); + data.append('i', this.$store.state.i.token); data.append('file', file); if (folder) data.append('folderId', folder); diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue index 028b911e24..38979871c1 100644 --- a/src/client/app/common/views/components/url-preview.vue +++ b/src/client/app/common/views/components/url-preview.vue @@ -68,7 +68,7 @@ iframe root(isDark) > a display block - font-size 16px + font-size 14px border solid 1px isDark ? #191b1f : #eee border-radius 4px overflow hidden @@ -136,8 +136,17 @@ root(isDark) left 0 width 100% + @media (max-width 550px) + font-size 12px + + > .thumbnail + height 80px + + > article + padding 12px + @media (max-width 500px) - font-size 8px + font-size 10px > .thumbnail height 70px @@ -145,6 +154,16 @@ root(isDark) > article padding 8px + > header + margin-bottom 4px + + > footer + margin-top 4px + + > img + width 12px + height 12px + .mk-url-preview[data-darkmode] root(true) diff --git a/src/client/app/common/views/components/visibility-chooser.vue b/src/client/app/common/views/components/visibility-chooser.vue index 592367cd6d..cc9c75095e 100644 --- a/src/client/app/common/views/components/visibility-chooser.vue +++ b/src/client/app/common/views/components/visibility-chooser.vue @@ -203,6 +203,7 @@ root(isDark) justify-content center align-items center margin-right 10px + width 16px > *:last-child flex 1 1 auto diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue index cad59d24f0..f3372bf062 100644 --- a/src/client/app/common/views/components/welcome-timeline.vue +++ b/src/client/app/common/views/components/welcome-timeline.vue @@ -13,7 +13,7 @@
- +
@@ -109,6 +109,9 @@ root(isDark) > .created-at color isDark ? #606984 : #c0c0c0 + > .text + text-align left + .mk-welcome-timeline[data-darkmode] root(true) diff --git a/src/client/app/common/views/widgets/analog-clock.vue b/src/client/app/common/views/widgets/analog-clock.vue new file mode 100644 index 0000000000..b1177d4ddf --- /dev/null +++ b/src/client/app/common/views/widgets/analog-clock.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/client/app/common/views/widgets/broadcast.vue b/src/client/app/common/views/widgets/broadcast.vue index f337cec853..69b2a54fe9 100644 --- a/src/client/app/common/views/widgets/broadcast.vue +++ b/src/client/app/common/views/widgets/broadcast.vue @@ -2,7 +2,7 @@
diff --git a/src/client/app/common/views/widgets/calendar.vue b/src/client/app/common/views/widgets/calendar.vue index 0e9714960a..333b56f629 100644 --- a/src/client/app/common/views/widgets/calendar.vue +++ b/src/client/app/common/views/widgets/calendar.vue @@ -1,5 +1,5 @@
+
@@ -67,7 +67,7 @@ export default define({ }, methods: { func() { - if (this.isMobile) return; + if (this.platform == 'mobile') return; if (this.props.design == 2) { this.props.design = 0; } else { diff --git a/src/client/app/common/views/widgets/donation.vue b/src/client/app/common/views/widgets/donation.vue index 75f5db808a..470576d5e6 100644 --- a/src/client/app/common/views/widgets/donation.vue +++ b/src/client/app/common/views/widgets/donation.vue @@ -1,5 +1,5 @@