diff --git a/.eslintrc b/.eslintrc index 0943cb4b6..dc1426eb1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -22,6 +22,7 @@ "globals": { "ENV": true, "VERSION": true, - "API": true + "API": true, + "LANGS": true } } diff --git a/.gitignore b/.gitignore index 53808240b..41fef982c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /build /built /data +/.cache-loader npm-debug.log *.pem run.bat diff --git a/.travis.yml b/.travis.yml index c86b737d2..f52fe7e3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,10 @@ notifications: email: false +branches: + except: + - l10n_master + language: node_js node_js: diff --git a/assets/title-dark.svg b/assets/title-dark.svg new file mode 100644 index 000000000..194741e67 --- /dev/null +++ b/assets/title-dark.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75266a683d673ae75ae34b7f02de128960b29f21f768c95f0079011f181d45f5 +size 13034 diff --git a/cli/clean-cached-remote-files.js b/cli/clean-cached-remote-files.js index e4db37ef9..a9c38a4cd 100644 --- a/cli/clean-cached-remote-files.js +++ b/cli/clean-cached-remote-files.js @@ -8,7 +8,8 @@ const { default: User } = require('../built/models/user'); const q = { 'metadata._user.host': { $ne: null - } + }, + 'metadata.isMetaOnly': false }; async function main() { @@ -56,8 +57,7 @@ async function main() { DriveFile.update({ _id: file._id }, { $set: { - 'metadata.deletedAt': new Date(), - 'metadata.isExpired': true + 'metadata.isMetaOnly': true } }) ]).then(async () => { diff --git a/docs/setup.en.md b/docs/setup.en.md index b858a4a2a..8dde4d00d 100644 --- a/docs/setup.en.md +++ b/docs/setup.en.md @@ -43,13 +43,7 @@ Please install and setup these softwares: *4.* Prepare configuration ---------------------------------------------------------------- -1. Copy `example.yml` of `.config` directory -2. Rename it to `default.yml` -3. Edit it - ---- - -Or you can generate config file via `npm run config` command. +You need to generate config file via `npm run config` command. *5.* Build Misskey ---------------------------------------------------------------- diff --git a/docs/setup.ja.md b/docs/setup.ja.md index c45ebcdca..0f1e46761 100644 --- a/docs/setup.ja.md +++ b/docs/setup.ja.md @@ -43,18 +43,14 @@ web-push generate-vapid-keys *4.* 設定ファイルを用意する ---------------------------------------------------------------- -1. `.config`ディレクトリ内の`example.yml`をコピー -2. `default.yml`にリネーム -3. 編集する - ---- - -または、`npm run config`コマンドを利用して、ガイドに従って情報を -入力して設定ファイルを生成することもできます。 +`npm run config`コマンドを利用して、ガイドに従って情報を入力してください。 *5.* Misskeyのビルド ---------------------------------------------------------------- -1. `npm run build` +1. `npm install -g node-gyp` +2. `node-gyp configure` +3. `node-gyp build` +4. `npm run build` *6.* 以上です! ---------------------------------------------------------------- @@ -78,4 +74,4 @@ VPSなどでビルドする時は、もしかしたらメモリが足りなく 3. npm run webpack 4. built/client をサーバーにアップロードする 5. サーバー上で、npm run gulp -6. 完了 \ No newline at end of file +6. 完了 diff --git a/locales/de.yml b/locales/de.yml index 7d0ffe408..5395de73a 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -1,7 +1,7 @@ --- meta: lang: "Deutsch" - divider: "" + divider: " " common: misskey: "Teile alles mit anderen mithilfe von Misskey" time: diff --git a/locales/en.yml b/locales/en.yml index adaf433dd..04f54957e 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -359,7 +359,7 @@ desktop/views/components/renote-form.vue: desktop/views/components/renote-form-window.vue: title: "Are you sure you want to renote this note?" desktop/views/components/settings-window.vue: - settings: "設定" + settings: "Settings" desktop/views/components/settings.vue: profile: "Profile" notification: "Notification" diff --git a/locales/fr.yml b/locales/fr.yml index 9ed78f6ec..4a9ddd380 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -1,7 +1,7 @@ --- meta: - lang: "日本語" - divider: "" + lang: "Français" + divider: " " common: misskey: "Partagez avec les autres en utilisant Misskey" time: diff --git a/locales/ja.yml b/locales/ja.yml index c3ee3e6c9..0fcbca536 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -173,6 +173,16 @@ common/views/components/twitter-setting.vue: common/views/components/uploader.vue: waiting: "待機中" +common/views/components/visibility-chooser.vue: + public: "公開" + home: "ホーム" + home-desc: "ホームタイムラインにのみ公開" + followers: "フォロワー" + followers-desc: "自分のフォロワーにのみ公開" + specified: "ダイレクト" + specified-desc: "指定したユーザーにのみ公開" + private: "非公開" + common/views/widgets/broadcast.vue: fetching: "確認中" no-broadcasts: "お知らせはありません" @@ -340,6 +350,14 @@ desktop/views/components/messaging-room-window.vue: desktop/views/components/messaging-window.vue: title: "メッセージ" +desktop/views/components/note-detail.vue: + more: "会話をもっと読み込む" + private: "(この投稿は非公開です)" + reposted-by: "{}がRenote" + location: "位置情報" + renote: "Renote" + add-reaction: "リアクション" + desktop/views/components/note-detail.sub.vue: private: "(この投稿は非公開です)" @@ -399,6 +417,9 @@ desktop/views/components/renote-form.vue: desktop/views/components/renote-form-window.vue: title: "この投稿をRenoteしますか?" +desktop/views/components/settings-window.vue: + settings: "設定" + desktop/views/components/settings.vue: profile: "プロフィール" notification: "通知" @@ -477,9 +498,6 @@ desktop/views/components/settings.vue: advanced-settings: "高度な設定" debug-mode: "デバッグモードを有効にする" debug-mode-desc: "この設定はブラウザに記憶されます。" - use-raw-script: "生のスクリプトを読み込む" - use-raw-script-desc: "圧縮されていない「生の」スクリプトを使用します。サイズが大きいため、読み込みに時間がかかる場合があります。この設定はブラウザに記憶されます。" - source-info: "Misskeyはソースマップも提供しています。" experimental: "実験的機能を有効にする" experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。" tools: "ツール" @@ -535,6 +553,13 @@ desktop/views/components/settings.profile.vue: description: "自己紹介" birthday: "誕生日" save: "保存" + is-bot: "このアカウントはBotです" + is-cat: "このアカウントはCatです" + +desktop/views/components/sub-note-content.vue: + hidden: "(この投稿は非公開です)" + media: "つのメディア" + poll: "投票" desktop/views/components/taskmanager.vue: title: "タスクマネージャ" @@ -583,6 +608,29 @@ desktop/views/components/users-list.vue: load-more: "もっと" fetching: "読み込んでいます" +desktop/views/components/users-list-item.vue: + followed: "フォローされています" + +desktop/views/components/window.vue: + popout: "ポップアウト" + close: "閉じる" + +desktop/views/pages/welcome.vue: + signin: "ログイン" + signup: "新規登録" + signin-button: "やってる" + signup-button: "やる" + timeline: "タイムライン" + +desktop/views/pages/drive.vue: + title: "Misskey Drive" + +desktop/views/pages/favorites.vue: + more: "さらに読み込む" + +desktop/views/pages/home-customize.vue: + title: "ホームのカスタマイズ" + desktop/views/pages/note.vue: prev: "前の投稿" next: "次の投稿" @@ -593,6 +641,11 @@ desktop/views/pages/selectdrive.vue: cancel: "キャンセル" upload: "PCからドライブにファイルをアップロード" +desktop/views/pages/user-list.users.vue: + users: "ユーザー" + add-user: "ユーザーを追加" + username: "ユーザー名" + desktop/views/pages/user/user.followers-you-know.vue: title: "知り合いのフォロワー" loading: "読み込み中" @@ -625,6 +678,12 @@ desktop/views/pages/user/user.profile.vue: muted: "ミュートしています" unmute: "ミュート解除" +desktop/views/pages/user/user.timeline.vue: + default: "投稿" + with-replies: "投稿と返信" + with-media: "メディア" + empty: "このユーザーはまだ何も投稿していないようです。" + desktop/views/widgets/messaging.vue: title: "メッセージ" @@ -642,6 +701,10 @@ desktop/views/widgets/post-form.vue: note: "投稿" placeholder: "いまどうしてる?" +desktop/views/widgets/profile.vue: + update-banner: "クリックでバナー編集" + update-avatar: "クリックでアバター編集" + desktop/views/widgets/trends.vue: title: "トレンド" refresh: "他を見る" @@ -735,7 +798,9 @@ mobile/views/pages/following.vue: following-of: "{}のフォロー" mobile/views/pages/home.vue: - timeline: "タイムライン" + home: "ホーム" + local: "ローカル" + global: "グローバル" mobile/views/pages/messaging.vue: messaging: "メッセージ" @@ -753,20 +818,19 @@ mobile/views/pages/notifications.vue: read-all: "すべての通知を既読にしますか?" mobile/views/pages/settings/settings.profile.vue: - title: "プロフィール設定" - will-be-published: "これらのプロフィールは公開されます。" + title: "プロフィール" name: "名前" + account: "アカウント" location: "場所" description: "自己紹介" birthday: "誕生日" avatar: "アイコン" banner: "バナー" - avatar-saved: "アイコンを保存しました" - banner-saved: "バナーを保存しました" - set-avatar: "アイコンを選択する" - set-banner: "バナーを選択する" + is-cat: "このアカウントはCatです" save: "保存" saved: "プロフィールを保存しました" + uploading: "アップロード中" + upload-failed: "アップロードに失敗しました" mobile/views/pages/search.vue: search: "検索" @@ -777,9 +841,40 @@ mobile/views/pages/selectdrive.vue: mobile/views/pages/settings.vue: signed-in-as: "{}としてサインイン中" - profile: "プロフィール" + lang: "言語" + lang-tip: "変更はページの再読み込み後に反映されます。" + recommended: "推奨" + auto: "自動" + specify-language: "言語を指定" + design: "デザインと表示" + dark-mode: "ダークモード" + i-am-under-limited-internet: "私は通信を制限されている" + circle-icons: "円形のアイコンを使用" + timeline: "タイムライン" + show-reply-target: "リプライ先を表示する" + show-my-renotes: "自分の行ったRenoteを表示する" + show-renoted-my-notes: "Renoteされた自分の投稿を表示する" + post-style: "投稿の表示スタイル" + post-style-standard: "標準" + post-style-smart: "スマート" + behavior: "動作" + fetch-on-scroll: "スクロールで自動読み込み" + disable-via-mobile: "「モバイルからの投稿」フラグを付けない" + load-raw-images: "添付された画像を高画質で表示する" + load-remote-media: "リモートサーバーのメディアを表示する" twitter: "Twitter連携" - signin-history: "サインイン履歴" + twitter-connect: "Twitterアカウントに接続する" + twitter-reconnect: "再接続する" + twitter-disconnect: "切断する" + update: "Misskey Update" + version: "バージョン:" + latest-version: "最新のバージョン:" + update-checking: "アップデートを確認中" + check-for-updates: "アップデートを確認" + no-updates: "利用可能な更新はありません" + no-updates-desc: "お使いのMisskeyは最新です。" + update-available: "新しいバージョンが利用可能です" + update-available-desc: "ページを再度読み込みすると更新が適用されます。" settings: "設定" signout: "サインアウト" diff --git a/locales/pl.yml b/locales/pl.yml index b6427bdf0..9324704bb 100644 --- a/locales/pl.yml +++ b/locales/pl.yml @@ -1,7 +1,7 @@ --- meta: - lang: "japoński" - divider: "" + lang: "język polski" + divider: " " common: misskey: "Dziel się zawartością z innymi korzystając z Misskey." time: diff --git a/locales/ru.yml b/locales/ru.yml index 08551f2db..290c660ff 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -1,7 +1,7 @@ --- meta: - lang: "日本語" - divider: "" + lang: "Русский язык" + divider: " " common: misskey: "Misskeyで皆と共有しよう。" time: diff --git a/package.json b/package.json index 3fa261b8a..534b0c296 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "misskey", "author": "syuilo ", - "version": "2.10.1", - "clientVersion": "1.0.5407", + "version": "2.17.0", + "clientVersion": "1.0.5731", "codename": "nighthike", "main": "./built/index.js", "private": true, @@ -65,7 +65,7 @@ "@types/mongodb": "3.0.18", "@types/monk": "6.0.0", "@types/ms": "0.7.30", - "@types/node": "10.1.0", + "@types/node": "10.1.2", "@types/nopt": "3.0.29", "@types/parse5": "3.0.0", "@types/pug": "2.0.4", @@ -80,7 +80,7 @@ "@types/speakeasy": "2.0.2", "@types/tmp": "0.0.33", "@types/uuid": "3.4.3", - "@types/webpack": "4.1.7", + "@types/webpack": "4.4.0", "@types/webpack-stream": "3.2.10", "@types/websocket": "0.0.39", "@types/ws": "5.1.1", @@ -98,8 +98,8 @@ "deepcopy": "0.6.3", "diskusage": "0.2.4", "dompurify": "1.0.4", - "elasticsearch": "14.2.2", - "element-ui": "2.3.8", + "elasticsearch": "15.0.0", + "element-ui": "2.3.9", "emojilib": "2.2.12", "escape-regexp": "0.0.1", "eslint": "4.19.1", @@ -124,7 +124,7 @@ "gulp-typescript": "4.0.2", "gulp-uglify": "3.0.0", "gulp-util": "3.0.8", - "hard-source-webpack-plugin": "0.6.7", + "hard-source-webpack-plugin": "0.6.9", "highlight.js": "9.12.0", "html-minifier": "3.5.15", "http-signature": "1.2.0", @@ -146,11 +146,11 @@ "koa-slow": "2.1.0", "koa-views": "6.1.4", "kue": "0.11.6", - "license-checker": "19.0.0", + "license-checker": "20.0.0", "loader-utils": "1.1.0", "mecab-async": "0.1.2", "mkdirp": "0.5.1", - "mocha": "5.1.1", + "mocha": "5.2.0", "moji": "0.5.1", "mongodb": "3.0.8", "monk": "6.0.6", @@ -205,12 +205,13 @@ "vue-cropperjs": "2.2.0", "vue-js-modal": "1.3.13", "vue-json-tree-view": "2.1.4", - "vue-loader": "15.0.11", + "vue-loader": "15.1.0", "vue-material": "^1.0.0-beta-10.2", "vue-router": "3.0.1", "vue-template-compiler": "2.5.16", "vuedraggable": "2.16.0", "vuex": "3.0.1", + "vuex-persistedstate": "^2.5.4", "web-push": "3.3.1", "webfinger.js": "2.6.6", "webpack": "4.8.3", diff --git a/src/build/i18n.ts b/src/build/i18n.ts index addc35ce5..35854055d 100644 --- a/src/build/i18n.ts +++ b/src/build/i18n.ts @@ -7,7 +7,7 @@ import locale from '../../locales'; export default class Replacer { private lang: string; - public pattern = /%i18n:([a-z0-9_\-\.\/\|\!]+?)%/g; + public pattern = /%i18n:([a-z0-9_\-\.\/\|]+?)%/g; constructor(lang: string) { this.lang = lang; @@ -56,11 +56,6 @@ export default class Replacer { public replacement(match, key) { let path = null; - const shouldEscape = key[0] == '!'; - if (shouldEscape) { - key = key.substr(1); - } - if (key.indexOf('|') != -1) { path = key.split('|')[0]; key = key.split('|')[1]; @@ -68,8 +63,6 @@ export default class Replacer { const txt = this.get(path, key); - return shouldEscape - ? txt.replace(/'/g, '\\x27').replace(/"/g, '\\x22') - : txt.replace(/"/g, '"'); + return txt.replace(/'/g, '\\x27').replace(/"/g, '\\x22'); } } diff --git a/src/client/app/app.styl b/src/client/app/app.styl index 431b9daa6..ba694b73a 100644 --- a/src/client/app/app.styl +++ b/src/client/app/app.styl @@ -7,6 +7,11 @@ 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/boot.js b/src/client/app/boot.js index 9338bc501..7b884c8a5 100644 --- a/src/client/app/boot.js +++ b/src/client/app/boot.js @@ -18,6 +18,14 @@ return; } + //#region Load settings + let settings = null; + const vuex = localStorage.getItem('vuex'); + if (vuex) { + settings = JSON.parse(vuex); + } + //#endregion + // Get the current url information const url = new URL(location.href); @@ -29,11 +37,16 @@ if (url.pathname == '/auth') app = 'auth'; //#endregion - // Detect the user language - // Note: The default language is Japanese + //#region Detect the user language let lang = navigator.language.split('-')[0]; + + // The default language is English if (!LANGS.includes(lang)) lang = 'en'; - if (localStorage.getItem('lang')) lang = localStorage.getItem('lang'); + + if (settings) { + if (settings.device.lang) lang = settings.device.lang; + } + //#endregion // Detect the user agent const ua = navigator.userAgent.toLowerCase(); @@ -61,20 +74,15 @@ } // Dark/Light - if (localStorage.getItem('darkmode') == 'true') { - document.documentElement.setAttribute('data-darkmode', 'true'); + if (settings) { + if (settings.device.darkmode) { + document.documentElement.setAttribute('data-darkmode', 'true'); + } } // Script version const ver = localStorage.getItem('v') || VERSION; - // Whether in debug mode - const isDebug = localStorage.getItem('debug') == 'true'; - - // Whether use raw version script - const raw = (localStorage.getItem('useRawScript') == 'true' && isDebug) - || ENV != 'production'; - // Get salt query const salt = localStorage.getItem('salt') ? '?salt=' + localStorage.getItem('salt') @@ -84,7 +92,7 @@ // Note: 'async' make it possible to load the script asyncly. // 'defer' make it possible to run the script when the dom loaded. const script = document.createElement('script'); - script.setAttribute('src', `/assets/${app}.${ver}.${lang}.${raw ? 'raw' : 'min'}.js${salt}`); + script.setAttribute('src', `/assets/${app}.${ver}.${lang}.js${salt}`); script.setAttribute('async', 'true'); script.setAttribute('defer', 'true'); head.appendChild(script); diff --git a/src/client/app/common/scripts/check-for-update.ts b/src/client/app/common/scripts/check-for-update.ts index 1e303017e..b5ba6916d 100644 --- a/src/client/app/common/scripts/check-for-update.ts +++ b/src/client/app/common/scripts/check-for-update.ts @@ -23,7 +23,7 @@ export default async function(mios: MiOS, force = false, silent = false) { } if (!silent) { - alert('%i18n:!common.update-available%'.replace('{newer}', newer).replace('{current}', current)); + alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)); } return newer; diff --git a/src/client/app/common/scripts/streaming/home.ts b/src/client/app/common/scripts/streaming/home.ts index 09d830bec..44d07e331 100644 --- a/src/client/app/common/scripts/streaming/home.ts +++ b/src/client/app/common/scripts/streaming/home.ts @@ -62,7 +62,7 @@ export class HomeStream extends Stream { // トークンが再生成されたとき // このままではMisskeyが利用できないので強制的にサインアウトさせる this.on('my_token_regenerated', () => { - alert('%i18n:!common.my-token-regenerated%'); + alert('%i18n:common.my-token-regenerated%'); os.signout(); }); } diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue index 8ec359e83..3e1b17635 100644 --- a/src/client/app/common/views/components/avatar.vue +++ b/src/client/app/common/views/components/avatar.vue @@ -21,10 +21,17 @@ export default Vue.extend({ } }, computed: { + lightmode(): boolean { + return this.$store.state.device.lightmode; + }, style(): any { return { - backgroundColor: this.user.avatarColor && this.user.avatarColor.length == 3 ? `rgb(${ this.user.avatarColor.join(',') })` : null, - backgroundImage: `url(${ this.user.avatarUrl }?thumbnail)`, + backgroundColor: this.lightmode + ? `rgb(${ this.user.avatarColor.slice(0, 3).join(',') })` + : this.user.avatarColor && this.user.avatarColor.length == 3 + ? `rgb(${ this.user.avatarColor.join(',') })` + : null, + backgroundImage: this.lightmode ? null : `url(${ this.user.avatarUrl }?thumbnail)`, borderRadius: (this as any).clientSettings.circleIcons ? '100%' : null }; } diff --git a/src/client/app/common/views/components/connect-failed.troubleshooter.vue b/src/client/app/common/views/components/connect-failed.troubleshooter.vue index 6a922676b..6c23cc796 100644 --- a/src/client/app/common/views/components/connect-failed.troubleshooter.vue +++ b/src/client/app/common/views/components/connect-failed.troubleshooter.vue @@ -8,21 +8,21 @@ - {{ network == null ? '%i18n:!@checking-network%' : '%i18n:!@network%' }} + {{ network == null ? '%i18n:@checking-network%' : '%i18n:@network%' }}

- {{ internet == null ? '%i18n:!@checking-internet%' : '%i18n:!@internet%' }} + {{ internet == null ? '%i18n:@checking-internet%' : '%i18n:@internet%' }}

- {{ server == null ? '%i18n:!@checking-server%' : '%i18n:!@server%' }} + {{ server == null ? '%i18n:@checking-server%' : '%i18n:@server%' }}

%i18n:@finding%

diff --git a/src/client/app/common/views/components/connect-failed.vue b/src/client/app/common/views/components/connect-failed.vue index 6c194ff98..0f686926b 100644 --- a/src/client/app/common/views/components/connect-failed.vue +++ b/src/client/app/common/views/components/connect-failed.vue @@ -3,9 +3,9 @@

%i18n:@title%

- {{ '%i18n:!@description%'.substr(0, '%i18n:!@description%'.indexOf('{')) }} - {{ '%i18n:!@description%'.match(/\{(.+?)\}/)[1] }} - {{ '%i18n:!@description%'.substr('%i18n:!@description%'.indexOf('}') + 1) }} + {{ '%i18n:@description%'.substr(0, '%i18n:@description%'.indexOf('{')) }} + {{ '%i18n:@description%'.match(/\{(.+?)\}/)[1] }} + {{ '%i18n:@description%'.substr('%i18n:@description%'.indexOf('}') + 1) }}

@@ -28,7 +28,7 @@ export default Vue.extend({ }, mounted() { document.title = 'Oops!'; - document.documentElement.style.background = '#f8f8f8'; + document.documentElement.style.setProperty('background', '#f8f8f8', 'important'); }, methods: { reload() { diff --git a/src/client/app/common/views/components/messaging-room.form.vue b/src/client/app/common/views/components/messaging-room.form.vue index 32a43ace5..050906cf4 100644 --- a/src/client/app/common/views/components/messaging-room.form.vue +++ b/src/client/app/common/views/components/messaging-room.form.vue @@ -197,7 +197,7 @@ export default Vue.extend({ 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 ba0ab3209..ef39199dc 100644 --- a/src/client/app/common/views/components/messaging-room.message.vue +++ b/src/client/app/common/views/components/messaging-room.message.vue @@ -59,8 +59,10 @@ export default Vue.extend({ diff --git a/src/client/app/common/views/components/messaging-room.vue b/src/client/app/common/views/components/messaging-room.vue index a45114e6b..79756b22e 100644 --- a/src/client/app/common/views/components/messaging-room.vue +++ b/src/client/app/common/views/components/messaging-room.vue @@ -8,7 +8,7 @@

%fa:info-circle%%i18n:@empty%

%fa:flag%%i18n:@no-history%

-
+ +
+ +
+
@@ -45,7 +49,9 @@ export default Vue.extend({ fetchingMoreMessages: false, messages: [], existMoreMessages: false, - connection: null + connection: null, + showIndicator: false, + timer: null }; }, @@ -149,9 +155,9 @@ export default Vue.extend({ onMessage(message) { // サウンドを再生する - if ((this as any).os.isEnableSounds) { + if (this.$store.state.device.enableSounds) { const sound = new Audio(`${url}/assets/message.mp3`); - sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 0.5; + sound.volume = this.$store.state.device.soundVolume; sound.play(); } @@ -172,7 +178,7 @@ export default Vue.extend({ }); } else if (message.userId != (this as any).os.i.id) { // Notify - this.notify('%i18n:!@new-message%'); + this.notifyNewMessage(); } }, @@ -205,18 +211,18 @@ export default Vue.extend({ } }, - notify(message) { - const n = document.createElement('p') as any; - n.innerHTML = '%fa:arrow-circle-down%' + message; - n.onclick = () => { - this.scrollToBottom(); - n.parentNode.removeChild(n); - }; - (this.$refs.notifications as any).appendChild(n); + onIndicatorClick() { + this.showIndicator = false; + this.scrollToBottom(); + }, - setTimeout(() => { - n.style.opacity = 0; - setTimeout(() => n.parentNode.removeChild(n), 1000); + notifyNewMessage() { + this.showIndicator = true; + + if (this.timer) clearTimeout(this.timer); + + this.timer = setTimeout(() => { + this.showIndicator = false; }, 4000); }, @@ -238,11 +244,12 @@ export default Vue.extend({ diff --git a/src/client/app/common/views/components/othello.game.vue b/src/client/app/common/views/components/othello.game.vue index 8c646cce0..ea75558d1 100644 --- a/src/client/app/common/views/components/othello.game.vue +++ b/src/client/app/common/views/components/othello.game.vue @@ -162,9 +162,9 @@ export default Vue.extend({ this.o.put(this.myColor, pos); // サウンドを再生する - if ((this as any).os.isEnableSounds) { + if (this.$store.state.device.enableSounds) { const sound = new Audio(`${url}/assets/othello-put-me.mp3`); - sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 0.5; + sound.volume = this.$store.state.device.soundVolume; sound.play(); } @@ -186,9 +186,9 @@ export default Vue.extend({ this.$forceUpdate(); // サウンドを再生する - if ((this as any).os.isEnableSounds && x.color != this.myColor) { + if (this.$store.state.device.enableSounds && x.color != this.myColor) { const sound = new Audio(`${url}/assets/othello-put-you.mp3`); - sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 0.5; + sound.volume = this.$store.state.device.soundVolume; sound.play(); } }, diff --git a/src/client/app/common/views/components/poll-editor.vue b/src/client/app/common/views/components/poll-editor.vue index 95bcba996..115c934c8 100644 --- a/src/client/app/common/views/components/poll-editor.vue +++ b/src/client/app/common/views/components/poll-editor.vue @@ -5,7 +5,7 @@

  • - + diff --git a/src/client/app/common/views/components/poll.vue b/src/client/app/common/views/components/poll.vue index 46e41cbcd..660247edb 100644 --- a/src/client/app/common/views/components/poll.vue +++ b/src/client/app/common/views/components/poll.vue @@ -1,19 +1,19 @@ diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue index 516979acd..f8bf7dd79 100644 --- a/src/client/app/common/views/components/signup.vue +++ b/src/client/app/common/views/components/signup.vue @@ -127,7 +127,7 @@ export default Vue.extend({ location.href = '/'; }); }).catch(() => { - alert('%i18n:!@some-error%'); + alert('%i18n:@some-error%'); (window as any).grecaptcha.reset(); this.recaptchaed = false; diff --git a/src/client/app/common/views/components/time.vue b/src/client/app/common/views/components/time.vue index 533958697..6e0d2b0dc 100644 --- a/src/client/app/common/views/components/time.vue +++ b/src/client/app/common/views/components/time.vue @@ -44,16 +44,16 @@ export default Vue.extend({ const time = this._time; const ago = (this.now.getTime() - time.getTime()) / 1000/*ms*/; return ( - ago >= 31536000 ? '%i18n:!common.time.years_ago%' .replace('{}', (~~(ago / 31536000)).toString()) : - ago >= 2592000 ? '%i18n:!common.time.months_ago%' .replace('{}', (~~(ago / 2592000)).toString()) : - ago >= 604800 ? '%i18n:!common.time.weeks_ago%' .replace('{}', (~~(ago / 604800)).toString()) : - ago >= 86400 ? '%i18n:!common.time.days_ago%' .replace('{}', (~~(ago / 86400)).toString()) : - ago >= 3600 ? '%i18n:!common.time.hours_ago%' .replace('{}', (~~(ago / 3600)).toString()) : - ago >= 60 ? '%i18n:!common.time.minutes_ago%'.replace('{}', (~~(ago / 60)).toString()) : - ago >= 10 ? '%i18n:!common.time.seconds_ago%'.replace('{}', (~~(ago % 60)).toString()) : - ago >= 0 ? '%i18n:!common.time.just_now%' : - ago < 0 ? '%i18n:!common.time.future%' : - '%i18n:!common.time.unknown%'); + ago >= 31536000 ? '%i18n:common.time.years_ago%' .replace('{}', (~~(ago / 31536000)).toString()) : + ago >= 2592000 ? '%i18n:common.time.months_ago%' .replace('{}', (~~(ago / 2592000)).toString()) : + ago >= 604800 ? '%i18n:common.time.weeks_ago%' .replace('{}', (~~(ago / 604800)).toString()) : + ago >= 86400 ? '%i18n:common.time.days_ago%' .replace('{}', (~~(ago / 86400)).toString()) : + ago >= 3600 ? '%i18n:common.time.hours_ago%' .replace('{}', (~~(ago / 3600)).toString()) : + ago >= 60 ? '%i18n:common.time.minutes_ago%'.replace('{}', (~~(ago / 60)).toString()) : + ago >= 10 ? '%i18n:common.time.seconds_ago%'.replace('{}', (~~(ago % 60)).toString()) : + ago >= 0 ? '%i18n:common.time.just_now%' : + ago < 0 ? '%i18n:common.time.future%' : + '%i18n:common.time.unknown%'); } }, created() { diff --git a/src/client/app/common/views/components/twitter-setting.vue b/src/client/app/common/views/components/twitter-setting.vue index ab07e6d09..9a2a1c3d4 100644 --- a/src/client/app/common/views/components/twitter-setting.vue +++ b/src/client/app/common/views/components/twitter-setting.vue @@ -3,7 +3,7 @@

    %i18n:@description%%i18n:@detail%

    - {{ os.i.twitter ? '%i18n:!@reconnect%' : '%i18n:!@connect%' }} + {{ os.i.twitter ? '%i18n:@reconnect%' : '%i18n:@connect%' }} or %i18n:@disconnect%

    diff --git a/src/client/app/common/views/components/visibility-chooser.vue b/src/client/app/common/views/components/visibility-chooser.vue index 50f0877ae..592367cd6 100644 --- a/src/client/app/common/views/components/visibility-chooser.vue +++ b/src/client/app/common/views/components/visibility-chooser.vue @@ -5,34 +5,34 @@
    %fa:globe%
    - 公開 + %i18n:@public%
    %fa:home%
    - ホーム - ホームタイムラインにのみ公開 + %i18n:@home% + %i18n:@home-desc%
    %fa:unlock%
    - フォロワー - 自分のフォロワーにのみ公開 + %i18n:@followers% + %i18n:@followers-desc%
    %fa:envelope%
    - ダイレクト - 指定したユーザーにのみ公開 + %i18n:@specified% + %i18n:@specified-desc%
    %fa:lock%
    - 非公開 + %i18n:@private%
    diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue index 6fadb030c..cad59d24f 100644 --- a/src/client/app/common/views/components/welcome-timeline.vue +++ b/src/client/app/common/views/components/welcome-timeline.vue @@ -37,6 +37,7 @@ export default Vue.extend({ fetch(cb?) { this.fetching = true; (this as any).api('notes', { + local: true, reply: false, renote: false, media: false, @@ -52,15 +53,15 @@ export default Vue.extend({ diff --git a/src/client/app/common/views/widgets/broadcast.vue b/src/client/app/common/views/widgets/broadcast.vue index 75b1d6052..f337cec85 100644 --- a/src/client/app/common/views/widgets/broadcast.vue +++ b/src/client/app/common/views/widgets/broadcast.vue @@ -14,7 +14,7 @@

    %i18n:@fetching%

    -

    {{ broadcasts.length == 0 ? '%i18n:!@no-broadcasts%' : broadcasts[i].title }}

    +

    {{ broadcasts.length == 0 ? '%i18n:@no-broadcasts%' : broadcasts[i].title }}

    diff --git a/src/client/app/common/views/widgets/donation.vue b/src/client/app/common/views/widgets/donation.vue index e35462611..75f5db808 100644 --- a/src/client/app/common/views/widgets/donation.vue +++ b/src/client/app/common/views/widgets/donation.vue @@ -3,9 +3,9 @@

    %fa:heart%%i18n:@title%

    - {{ '%i18n:!@text%'.substr(0, '%i18n:!@text%'.indexOf('{')) }} + {{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }} @syuilo - {{ '%i18n:!@text%'.substr('%i18n:!@text%'.indexOf('}') + 1) }} + {{ '%i18n:@text%'.substr('%i18n:@text%'.indexOf('}') + 1) }}

    diff --git a/src/client/app/config.ts b/src/client/app/config.ts index 522d7ff05..70c085de1 100644 --- a/src/client/app/config.ts +++ b/src/client/app/config.ts @@ -8,6 +8,7 @@ declare const _STATS_URL_: string; declare const _STATUS_URL_: string; declare const _DEV_URL_: string; declare const _LANG_: string; +declare const _LANGS_: string; declare const _RECAPTCHA_SITEKEY_: string; declare const _SW_PUBLICKEY_: string; declare const _THEME_COLOR_: string; @@ -27,6 +28,7 @@ export const statsUrl = _STATS_URL_; export const statusUrl = _STATUS_URL_; export const devUrl = _DEV_URL_; export const lang = _LANG_; +export const langs = _LANGS_; export const recaptchaSitekey = _RECAPTCHA_SITEKEY_; export const swPublickey = _SW_PUBLICKEY_; export const themeColor = _THEME_COLOR_; diff --git a/src/client/app/desktop/views/components/calendar.vue b/src/client/app/desktop/views/components/calendar.vue index 757eefac7..9a93841e5 100644 --- a/src/client/app/desktop/views/components/calendar.vue +++ b/src/client/app/desktop/views/components/calendar.vue @@ -2,7 +2,7 @@
    @@ -21,7 +21,7 @@ :data-is-out-of-range="isOutOfRange(i + 1)" :data-is-donichi="isDonichi(i + 1)" @click="go(i + 1)" - :title="isOutOfRange(i + 1) ? null : '%i18n:!@go%'" + :title="isOutOfRange(i + 1) ? null : '%i18n:@go%'" >
    {{ i + 1 }}
    @@ -58,13 +58,13 @@ export default Vue.extend({ month: new Date().getMonth() + 1, selected: new Date(), weekdayText: [ - '%i18n:!common.weekday-short.sunday%', - '%i18n:!common.weekday-short.monday%', - '%i18n:!common.weekday-short.tuesday%', - '%i18n:!common.weekday-short.wednesday%', - '%i18n:!common.weekday-short.thursday%', - '%i18n:!common.weekday-short.friday%', - '%i18n:!common.weekday-short.saturday%' + '%i18n:common.weekday-short.sunday%', + '%i18n:common.weekday-short.monday%', + '%i18n:common.weekday-short.tuesday%', + '%i18n:common.weekday-short.wednesday%', + '%i18n:common.weekday-short.thursday%', + '%i18n:common.weekday-short.friday%', + '%i18n:common.weekday-short.saturday%' ] }; }, diff --git a/src/client/app/desktop/views/components/drive.file.vue b/src/client/app/desktop/views/components/drive.file.vue index d8b8420ec..fb553e1ae 100644 --- a/src/client/app/desktop/views/components/drive.file.vue +++ b/src/client/app/desktop/views/components/drive.file.vue @@ -64,46 +64,46 @@ export default Vue.extend({ this.isContextmenuShowing = true; contextmenu(e, [{ type: 'item', - text: '%i18n:!@contextmenu.rename%', + text: '%i18n:@contextmenu.rename%', icon: '%fa:i-cursor%', onClick: this.rename }, { type: 'item', - text: '%i18n:!@contextmenu.copy-url%', + text: '%i18n:@contextmenu.copy-url%', icon: '%fa:link%', onClick: this.copyUrl }, { type: 'link', href: `${this.file.url}?download`, - text: '%i18n:!@contextmenu.download%', + text: '%i18n:@contextmenu.download%', icon: '%fa:download%', }, { type: 'divider', }, { type: 'item', - text: '%i18n:!common.delete%', + text: '%i18n:common.delete%', icon: '%fa:R trash-alt%', onClick: this.deleteFile }, { type: 'divider', }, { type: 'nest', - text: '%i18n:!@contextmenu.else-files%', + text: '%i18n:@contextmenu.else-files%', menu: [{ type: 'item', - text: '%i18n:!@contextmenu.set-as-avatar%', + text: '%i18n:@contextmenu.set-as-avatar%', onClick: this.setAsAvatar }, { type: 'item', - text: '%i18n:!@contextmenu.set-as-banner%', + text: '%i18n:@contextmenu.set-as-banner%', onClick: this.setAsBanner }] }, { type: 'nest', - text: '%i18n:!@contextmenu.open-in-app%', + text: '%i18n:@contextmenu.open-in-app%', menu: [{ type: 'item', - text: '%i18n:!@contextmenu.add-app%...', + text: '%i18n:@contextmenu.add-app%...', onClick: this.addApp }] }], { @@ -141,8 +141,8 @@ export default Vue.extend({ rename() { (this as any).apis.input({ - title: '%i18n:!@contextmenu.rename-file%', - placeholder: '%i18n:!@contextmenu.input-new-file-name%', + title: '%i18n:@contextmenu.rename-file%', + placeholder: '%i18n:@contextmenu.input-new-file-name%', default: this.file.name, allowEmpty: false }).then(name => { @@ -157,9 +157,9 @@ export default Vue.extend({ copyToClipboard(this.file.url); (this as any).apis.dialog({ title: '%fa:check%%i18n:@contextmenu.copied%', - text: '%i18n:!@contextmenu.copied-url-to-clipboard%', + text: '%i18n:@contextmenu.copied-url-to-clipboard%', actions: [{ - text: '%i18n:!common.ok%' + text: '%i18n:common.ok%' }] }); }, diff --git a/src/client/app/desktop/views/components/drive.folder.vue b/src/client/app/desktop/views/components/drive.folder.vue index 0761ffb1a..16f474f4e 100644 --- a/src/client/app/desktop/views/components/drive.folder.vue +++ b/src/client/app/desktop/views/components/drive.folder.vue @@ -54,26 +54,26 @@ export default Vue.extend({ this.isContextmenuShowing = true; contextmenu(e, [{ type: 'item', - text: '%i18n:!@contextmenu.move-to-this-folder%', + text: '%i18n:@contextmenu.move-to-this-folder%', icon: '%fa:arrow-right%', onClick: this.go }, { type: 'item', - text: '%i18n:!@contextmenu.show-in-new-window%', + text: '%i18n:@contextmenu.show-in-new-window%', icon: '%fa:R window-restore%', onClick: this.newWindow }, { type: 'divider', }, { type: 'item', - text: '%i18n:!@contextmenu.rename%', + text: '%i18n:@contextmenu.rename%', icon: '%fa:i-cursor%', onClick: this.rename }, { type: 'divider', }, { type: 'item', - text: '%i18n:!common.delete%', + text: '%i18n:common.delete%', icon: '%fa:R trash-alt%', onClick: this.deleteFolder }], { @@ -159,15 +159,15 @@ export default Vue.extend({ switch (err) { case 'detected-circular-definition': (this as any).apis.dialog({ - title: '%fa:exclamation-triangle%%i18n:!@unable-to-process%', - text: '%i18n:!@circular-reference-detected%', + title: '%fa:exclamation-triangle%%i18n:@unable-to-process%', + text: '%i18n:@circular-reference-detected%', actions: [{ - text: '%i18n:!common.ok%' + text: '%i18n:common.ok%' }] }); break; default: - alert('%i18n:!@unhandled-error% ' + err); + alert('%i18n:@unhandled-error% ' + err); } }); } @@ -199,8 +199,8 @@ export default Vue.extend({ rename() { (this as any).apis.input({ - title: '%i18n:!@contextmenu.rename-folder%', - placeholder: '%i18n:!@contextmenu.input-new-folder-name%', + title: '%i18n:@contextmenu.rename-folder%', + placeholder: '%i18n:@contextmenu.input-new-folder-name%', default: this.folder.name }).then(name => { (this as any).api('drive/folders/update', { diff --git a/src/client/app/desktop/views/components/drive.nav-folder.vue b/src/client/app/desktop/views/components/drive.nav-folder.vue index 71b2e419d..40f620875 100644 --- a/src/client/app/desktop/views/components/drive.nav-folder.vue +++ b/src/client/app/desktop/views/components/drive.nav-folder.vue @@ -8,7 +8,7 @@ @drop.stop="onDrop" > - {{ folder == null ? '%i18n:!@drive%' : folder.name }} + {{ folder == null ? '%i18n:@drive%' : folder.name }} diff --git a/src/client/app/desktop/views/components/drive.vue b/src/client/app/desktop/views/components/drive.vue index 973df1014..cae40f306 100644 --- a/src/client/app/desktop/views/components/drive.vue +++ b/src/client/app/desktop/views/components/drive.vue @@ -138,17 +138,17 @@ export default Vue.extend({ onContextmenu(e) { contextmenu(e, [{ type: 'item', - text: '%i18n:!@contextmenu.create-folder%', + text: '%i18n:@contextmenu.create-folder%', icon: '%fa:R folder%', onClick: this.createFolder }, { type: 'item', - text: '%i18n:!@contextmenu.upload%', + text: '%i18n:@contextmenu.upload%', icon: '%fa:upload%', onClick: this.selectLocalFile }, { type: 'item', - text: '%i18n:!@contextmenu.url-upload%', + text: '%i18n:@contextmenu.url-upload%', icon: '%fa:cloud-upload-alt%', onClick: this.urlUpload }]); @@ -306,15 +306,15 @@ export default Vue.extend({ switch (err) { case 'detected-circular-definition': (this as any).apis.dialog({ - title: '%fa:exclamation-triangle%%i18n:!@unable-to-process%', - text: '%i18n:!@circular-reference-detected%', + title: '%fa:exclamation-triangle%%i18n:@unable-to-process%', + text: '%i18n:@circular-reference-detected%', actions: [{ - text: '%i18n:!common.ok%' + text: '%i18n:common.ok%' }] }); break; default: - alert('%i18n:!@unhandled-error% ' + err); + alert('%i18n:@unhandled-error% ' + err); } }); } @@ -327,8 +327,8 @@ export default Vue.extend({ urlUpload() { (this as any).apis.input({ - title: '%i18n:!@url-upload%', - placeholder: '%i18n:!@url-of-file%' + title: '%i18n:@url-upload%', + placeholder: '%i18n:@url-of-file%' }).then(url => { (this as any).api('drive/files/upload_from_url', { url: url, @@ -337,9 +337,9 @@ export default Vue.extend({ (this as any).apis.dialog({ title: '%fa:check%%i18n:@url-upload-requested%', - text: '%i18n:!@may-take-time%', + text: '%i18n:@may-take-time%', actions: [{ - text: '%i18n:!common.ok%' + text: '%i18n:common.ok%' }] }); }); @@ -347,8 +347,8 @@ export default Vue.extend({ createFolder() { (this as any).apis.input({ - title: '%i18n:!@create-folder%', - placeholder: '%i18n:!@folder-name%' + title: '%i18n:@create-folder%', + placeholder: '%i18n:@folder-name%' }).then(name => { (this as any).api('drive/folders/create', { name: name, diff --git a/src/client/app/desktop/views/components/followers-window.vue b/src/client/app/desktop/views/components/followers-window.vue index f3eec13e0..7ed31315f 100644 --- a/src/client/app/desktop/views/components/followers-window.vue +++ b/src/client/app/desktop/views/components/followers-window.vue @@ -1,7 +1,7 @@