diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd6e095bf..e7800ab72c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,21 @@ npm i -g ts-node npm run migrate ``` +11.23.0 (2019/06/23) +-------------------- +### ✨Improvements +* ホーム/デッキのカスタマイズ情報を複数のデバイスで同期できるように +* ホーム/デッキのカスタマイズ情報の複数プリセット切り替え +* サーバー情報にRedisのバージョンを追加 +* ServiceWorker有効化 +* MisskeyPagesでリストの要素数を取得する関数を追加 +* URLプレビューでハッシュだけ違うプレビューカードは表示しないように +* URLプレビューをユーザーロケールで出し分けるように +* リモートの凍結されたアカウントからのアクティビティはすべて無視するように + +### 🐛Fixes +* フォロー解除してもフォローボタンがフォロー中のままになる問題を修正 + 11.22.0 (2019/06/18) -------------------- ### ✨Improvements diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 29395ecfda..ff7b730c20 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -1298,5 +1298,7 @@ pages: arg2: "Seznamy" _pick: arg1: "Seznamy" + _listLen: + arg1: "Seznamy" types: array: "Seznamy" diff --git a/locales/da-DK.yml b/locales/da-DK.yml index 08ca17592c..3b667258f7 100644 --- a/locales/da-DK.yml +++ b/locales/da-DK.yml @@ -1844,6 +1844,8 @@ pages: _pick: arg1: "Lister" arg2: "Position" + _listLen: + arg1: "Lister" number: "Tal" stringToNumber: "Tekst til tal" _stringToNumber: diff --git a/locales/de-DE.yml b/locales/de-DE.yml index bc6515cd57..2541e1ca63 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -929,5 +929,7 @@ pages: arg2: "Listen" _pick: arg1: "Listen" + _listLen: + arg1: "Listen" types: array: "Listen" diff --git a/locales/en-US.yml b/locales/en-US.yml index 5ac2561d16..fd8e965887 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -270,6 +270,9 @@ common: disable-via-mobile: "Don't mark the post as 'from mobile'" load-raw-images: "Show attached images in original quality" load-remote-media: "Show media from a remote server" + sync: "Sync" + home-profile: "Home profile" + deck-profile: "Deck profile" search: "Search" delete: "Delete" loading: "Loading" @@ -1103,7 +1106,10 @@ admin/views/index.vue: abuse: "Abuse" queue: "Job Queue" logs: "Logs" + db: "Database" back-to-misskey: "Back to Misskey" +admin/views/db.vue: + tables: "Tables" admin/views/dashboard.vue: dashboard: "Dashboard" accounts: "Accounts" @@ -1941,6 +1947,8 @@ pages: _pick: arg1: "Lists" arg2: "Position" + _listLen: + arg1: "Lists" number: "Number" stringToNumber: "Text to number" _stringToNumber: diff --git a/locales/es-ES.yml b/locales/es-ES.yml index bbfa7f10e9..174b69110e 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1109,5 +1109,7 @@ pages: arg2: "Listas" _pick: arg1: "Listas" + _listLen: + arg1: "Listas" types: array: "Listas" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 100b2f9124..fe593c24f5 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -265,6 +265,7 @@ common: disable-via-mobile: "Enlever la mention publié via 'mobile'" load-raw-images: "Afficher les photos jointes dans leur qualité originale" load-remote-media: "Afficher les médias depuis le serveur distant" + sync: "Synchroniser" search: "Recherche" delete: "Supprimer" loading: "Chargement en cours …" @@ -724,6 +725,7 @@ common/views/components/user-group-editor.vue: transfer: "Transférer de groupe" transferred: "Groupe transféré" remove-user: "Enlever un utilisateur de ce groupe" + delete-are-you-sure: "Désirez-vous vraiment supprimer le groupe $1 ?" deleted: "Supprimé" invite: "Inviter" invited: "Invitation envoyée avec succès" @@ -1088,7 +1090,10 @@ admin/views/index.vue: abuse: "Abus" queue: "File d’attente" logs: "Journaux" + db: "Base de données" back-to-misskey: "Retour vers Misskey" +admin/views/db.vue: + tables: "Tables" admin/views/dashboard.vue: dashboard: "Tableau de bord" accounts: "Comptes" @@ -1875,6 +1880,8 @@ pages: _pick: arg1: "Listes" arg2: "Position" + _listLen: + arg1: "Listes" number: "Numérique" stringToNumber: "Chaîne en chiffres" _stringToNumber: diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index d9223b1c4b..88673e804f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -282,6 +282,9 @@ common: disable-via-mobile: "「モバイルからの投稿」フラグを付けない" load-raw-images: "添付された画像を高画質で表示する" load-remote-media: "リモートサーバーのメディアを表示する" + sync: "同期" + home-profile: "ホームのプロファイル" + deck-profile: "デッキのプロファイル" search: "検索" delete: "削除" @@ -2148,6 +2151,9 @@ pages: _pick: arg1: "リスト" arg2: "位置" + listLen: "リストの長さを取得" + _listLen: + arg1: "リスト" number: "数値" stringToNumber: "テキストを数値に" _stringToNumber: diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 8f6fc18966..85ad415d71 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -1254,5 +1254,7 @@ pages: arg2: "リスト" _pick: arg1: "リスト" + _listLen: + arg1: "リスト" types: array: "リスト" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 72070e4d42..5272973ecc 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -116,7 +116,7 @@ common: local-only-message: "이 글은 로컬에만 공개되어 있습니다" click-to-tagging: "클릭하여 태그 넣기" visibility: "공개 범위" - geolocation-alert: "사용 중인 장치에서는 위치 정보를 사용할 수 없습니다" + geolocation-alert: "사용 중이신 장치에서는 위치 정보를 사용할 수 없습니다" error: "오류" enter-username: "사용자명을 입력해주세요" add-visible-user: "사용자 추가" @@ -270,6 +270,9 @@ common: disable-via-mobile: "작성하는 글에 \"모바일에서 작성함\" 을 붙이지 않음" load-raw-images: "첨부 이미지를 고품질로 표시" load-remote-media: "원격 서버의 미디어를 표시" + sync: "동기화" + home-profile: "홈 프로필" + deck-profile: "덱 프로필" search: "검색" delete: "삭제" loading: "로드 중" @@ -286,7 +289,7 @@ common: is-remote-user: "이 사용자 정보는 정확하지 않을 수 있습니다." is-remote-post: "이 글 정보는 복사본입니다." view-on-remote: "정확한 정보 보기" - renoted-by: "{user}이(가) 리노트" + renoted-by: "{user}님이 리노트" no-notes: "글이 없습니다" turn-on-darkmode: "어둠에 삼켜져라" turn-off-darkmode: "빛이 있으라" @@ -1103,7 +1106,10 @@ admin/views/index.vue: abuse: "스팸 신고" queue: "작업 대기열" logs: "로그" + db: "데이터베이스" back-to-misskey: "Misskey로 돌아가기" +admin/views/db.vue: + tables: "테이블" admin/views/dashboard.vue: dashboard: "대시보드" accounts: "계정" @@ -1126,7 +1132,7 @@ admin/views/queue.vue: states: active: "처리중" delayed: "지연됨" - waiting: "대기열에 들어감" + waiting: "대기열에 있음" result-is-truncated: "결과는 생략되었습니다" other-queues: "기타 큐" admin/views/logs.vue: @@ -1941,6 +1947,8 @@ pages: _pick: arg1: "리스트" arg2: "위치" + _listLen: + arg1: "리스트" number: "수치" stringToNumber: "텍스트를 수치로" _stringToNumber: diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 857a00ca97..355b3cb682 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -623,5 +623,7 @@ pages: arg2: "Lijsten" _pick: arg1: "Lijsten" + _listLen: + arg1: "Lijsten" types: array: "Lijsten" diff --git a/locales/no-NO.yml b/locales/no-NO.yml index c42b54baa9..296c0cccab 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -503,5 +503,7 @@ pages: arg2: "Lister" _pick: arg1: "Lister" + _listLen: + arg1: "Lister" types: array: "Lister" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index eecde5c735..e7a34c4acc 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1219,5 +1219,7 @@ pages: arg2: "Listy" _pick: arg1: "Listy" + _listLen: + arg1: "Listy" types: array: "Listy" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 002e3bfab9..01450b956b 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -26,6 +26,13 @@ common: do-not-copy-paste: "Пожалуйста, не вводите и не вставляйте сюда код. Аккаунту может угрожать опасность." load-more: "Загрузить больше" enter-password: "Пожалуйста, введите ваш пароль" + 2fa: "Двухфакторная аутентификация" + customize-home: "Настройка домашней страницы" + dark-mode: "Тёмная тема" + signin: "Войти" + signup: "Регистрация" + signout: "Выйти" + reload-to-apply-the-setting: "Вам необходимо перезагрузить страницу, чтобы применить настройки. Вы хотите перезагрузить сейчас?" customization-tips: title: "Советы по настройке" gotit: "Понятно!" @@ -51,7 +58,14 @@ common: month-and-day: "{day}.{month}" trash: "Мусорное ведро" drive: "Drive" + pages: "Страницы" messaging: "Чат" + timeline: "Лента" + followers: "Подписчики" + favorites: "Избранное" + post-form: + reply: "Ответить" + create-poll: "Создать опрос" weekday-short: sunday: "Вс" monday: "Пн" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 39d9920a44..5b4e6799ef 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -270,6 +270,9 @@ common: disable-via-mobile: "不要将帖子标记为“来自手机”" load-raw-images: "以原始质量显示附加图像" load-remote-media: "显示来自远程服务器的媒体" + sync: "同步" + home-profile: "定制首页数据" + deck-profile: "定制Deck数据" search: "搜索" delete: "删除" loading: "正在加载中" @@ -859,7 +862,7 @@ desktop/views/components/crop-window.vue: cancel: "取消" ok: "确定" desktop/views/components/drive-window.vue: - used: "使用中" + used: "已使用" desktop/views/components/drive.file.vue: avatar: "头像" banner: "背景" @@ -1016,7 +1019,7 @@ desktop/views/components/settings.apps.vue: no-apps: "没有已连接的应用程序" common/views/components/drive-settings.vue: max: "容量" - in-use: "正在使用" + in-use: "已使用" stats: "统计" common/views/components/mute-and-block.vue: mute-and-block: "屏蔽/拉黑" @@ -1103,7 +1106,10 @@ admin/views/index.vue: abuse: "举报垃圾信息" queue: "作业队列" logs: "登录" + db: "数据库" back-to-misskey: "返回 Misskey" +admin/views/db.vue: + tables: "表格" admin/views/dashboard.vue: dashboard: "Dashboard" accounts: "账户" @@ -1518,7 +1524,7 @@ desktop/views/widgets/users.vue: refresh: "更多" no-one: "不是!" mobile/views/components/drive.vue: - used: "使用中" + used: "已使用" folder-count: "文件夹" count-separator: "," file-count: "文件" @@ -1941,6 +1947,8 @@ pages: _pick: arg1: "列表" arg2: "位置" + _listLen: + arg1: "列表" number: "数值" stringToNumber: "文本到数字" _stringToNumber: diff --git a/package.json b/package.json index e5946f04f5..9d609db159 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "11.22.0", + "version": "11.23.0", "codename": "daybreak", "repository": { "type": "git", diff --git a/src/client/app/common/define-widget.ts b/src/client/app/common/define-widget.ts index 632ddf2ed6..ba4deafe3a 100644 --- a/src/client/app/common/define-widget.ts +++ b/src/client/app/common/define-widget.ts @@ -60,9 +60,9 @@ export default function (data: { save() { if (this.platform == 'deck') { - this.$store.commit('device/updateDeckColumn', this.column); + this.$store.commit('updateDeckColumn', this.column); } else { - this.$store.commit('device/updateWidget', this.widget); + this.$store.commit('updateWidget', this.widget); } } } diff --git a/src/client/app/common/scripts/note-mixin.ts b/src/client/app/common/scripts/note-mixin.ts index 4b454f8800..e649680070 100644 --- a/src/client/app/common/scripts/note-mixin.ts +++ b/src/client/app/common/scripts/note-mixin.ts @@ -83,9 +83,19 @@ export default (opts: Opts = {}) => ({ if (this.appearNote.text) { const ast = parse(this.appearNote.text); // TODO: 再帰的にURL要素がないか調べる - return unique(ast + const urls = unique(ast .filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent)) .map(t => t.node.props.url)); + + // unique without hash + // [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ] + const removeHash = x => x.replace(/#[^#]*$/, ''); + + return urls.reduce((array, url) => { + const removed = removeHash(url); + if (!array.map(x => removeHash(x)).includes(removed)) array.push(url); + return array; + }, []); } else { return null; } diff --git a/src/client/app/common/views/components/settings/settings.vue b/src/client/app/common/views/components/settings/settings.vue index 0cbc40da57..1254eb5e5e 100644 --- a/src/client/app/common/views/components/settings/settings.vue +++ b/src/client/app/common/views/components/settings/settings.vue @@ -131,6 +131,13 @@ +
+
{{ $t('@._settings.sync') }}
+ {{ $t('@._settings.home-profile') }} + {{ $t('@._settings.home-profile') }} + {{ $t('@._settings.deck-profile') }} +
+
{{ $t('@._settings.web-search-engine') }}
{{ $t('@._settings.web-search-engine') }} @@ -500,6 +507,21 @@ export default Vue.extend({ get() { return this.$store.state.device.mobileNotificationPosition; }, set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); } }, + + homeProfile: { + get() { return this.$store.state.device.homeProfile; }, + set(value) { this.$store.commit('device/set', { key: 'homeProfile', value }); } + }, + + mobileHomeProfile: { + get() { return this.$store.state.device.mobileHomeProfile; }, + set(value) { this.$store.commit('device/set', { key: 'mobileHomeProfile', value }); } + }, + + deckProfile: { + get() { return this.$store.state.device.deckProfile; }, + set(value) { this.$store.commit('device/set', { key: 'deckProfile', value }); } + }, }, created() { this.$root.getMeta().then(meta => { diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue index 9c61e46059..20fbcbb046 100644 --- a/src/client/app/common/views/components/url-preview.vue +++ b/src/client/app/common/views/components/url-preview.vue @@ -30,7 +30,7 @@ diff --git a/src/client/app/mios.ts b/src/client/app/mios.ts index ae1446b934..a73ef45c38 100644 --- a/src/client/app/mios.ts +++ b/src/client/app/mios.ts @@ -173,10 +173,9 @@ export default class MiOS extends EventEmitter { // Init service worker if (this.shouldRegisterSw) { - // #4813 - //this.getMeta().then(data => { - // this.registerSw(data.swPublickey); - //}); + this.getMeta().then(data => { + this.registerSw(data.swPublickey); + }); } }; diff --git a/src/client/app/mobile/views/pages/widgets.vue b/src/client/app/mobile/views/pages/widgets.vue index 7130fddb34..c012de1314 100644 --- a/src/client/app/mobile/views/pages/widgets.vue +++ b/src/client/app/mobile/views/pages/widgets.vue @@ -72,13 +72,13 @@ export default Vue.extend({ computed: { widgets(): any[] { - return this.$store.state.device.mobileHome; + return this.$store.getters.mobileHome || []; } }, created() { if (this.widgets.length == 0) { - this.$store.commit('device/setMobileHome', [{ + this.$store.commit('setMobileHome', [{ name: 'calendar', id: 'a', data: {} }, { @@ -98,6 +98,12 @@ export default Vue.extend({ id: 'g', data: {} }]); } + + this.$watch('$store.getters.mobileHome', () => { + this.$store.dispatch('settings/updateMobileHomeProfile'); + }, { + deep: true + }); }, mounted() { @@ -122,7 +128,7 @@ export default Vue.extend({ }, addWidget() { - this.$store.commit('device/addMobileHomeWidget', { + this.$store.commit('addMobileHomeWidget', { name: this.widgetAdderSelected, id: uuid(), data: {} @@ -130,11 +136,11 @@ export default Vue.extend({ }, removeWidget(widget) { - this.$store.commit('device/removeMobileHomeWidget', widget); + this.$store.commit('removeMobileHomeWidget', widget); }, saveHome() { - this.$store.commit('device/setMobileHome', this.widgets); + this.$store.commit('setMobileHome', this.widgets); } } }); diff --git a/src/client/app/store.ts b/src/client/app/store.ts index 6f545eb09b..852d2c393d 100644 --- a/src/client/app/store.ts +++ b/src/client/app/store.ts @@ -1,3 +1,4 @@ +import Vue from 'vue'; import Vuex from 'vuex'; import createPersistedState from 'vuex-persistedstate'; import * as nestedProperty from 'nested-property'; @@ -34,12 +35,15 @@ const defaultSettings = { gamesReversiShowBoardLabels: false, gamesReversiUseAvatarStones: true, disableAnimatedMfm: false, + homeProfiles: {}, + mobileHomeProfiles: {}, + deckProfiles: {}, }; const defaultDeviceSettings = { - home: null, - mobileHome: [], - deck: null, + homeProfile: 'Default', + mobileHomeProfile: 'Default', + deckProfile: 'Default', deckMode: false, deckColumnAlign: 'center', deckColumnWidth: 'normal', @@ -82,7 +86,13 @@ export default (os: MiOS) => new Vuex.Store({ }, getters: { - isSignedIn: state => state.i != null + isSignedIn: state => state.i != null, + + home: state => state.settings.homeProfiles[state.device.homeProfile], + + mobileHome: state => state.settings.mobileHomeProfiles[state.device.mobileHomeProfile], + + deck: state => state.settings.deckProfiles[state.device.deckProfile], }, mutations: { @@ -112,6 +122,216 @@ export default (os: MiOS) => new Vuex.Store({ clearBehindNotes(state) { state.behindNotes = []; document.title = os.instanceName; + }, + + setHome(state, data) { + Vue.set(state.settings.homeProfiles, state.device.homeProfile, data); + os.store.dispatch('settings/updateHomeProfile'); + }, + + setDeck(state, data) { + Vue.set(state.settings.deckProfiles, state.device.deckProfile, data); + os.store.dispatch('settings/updateDeckProfile'); + }, + + addHomeWidget(state, widget) { + state.settings.homeProfiles[state.device.homeProfile].unshift(widget); + os.store.dispatch('settings/updateHomeProfile'); + }, + + setMobileHome(state, data) { + Vue.set(state.settings.mobileHomeProfiles, state.device.mobileHomeProfile, data); + os.store.dispatch('settings/updateMobileHomeProfile'); + }, + + updateWidget(state, x) { + let w; + + //#region Desktop home + const home = state.settings.homeProfiles[state.device.homeProfile]; + if (home) { + w = home.find(w => w.id == x.id); + if (w) { + w.data = x.data; + os.store.dispatch('settings/updateHomeProfile'); + } + } + //#endregion + + //#region Mobile home + const mobileHome = state.settings.mobileHomeProfiles[state.device.mobileHomeProfile]; + if (mobileHome) { + w = mobileHome.find(w => w.id == x.id); + if (w) { + w.data = x.data; + os.store.dispatch('settings/updateMobileHomeProfile'); + } + } + //#endregion + }, + + addMobileHomeWidget(state, widget) { + state.settings.mobileHomeProfiles[state.device.mobileHomeProfile].unshift(widget); + os.store.dispatch('settings/updateMobileHomeProfile'); + }, + + removeMobileHomeWidget(state, widget) { + Vue.set('state.settings.mobileHomeProfiles', state.device.mobileHomeProfile, state.settings.mobileHomeProfiles[state.device.mobileHomeProfile].filter(w => w.id != widget.id)); + os.store.dispatch('settings/updateMobileHomeProfile'); + }, + + addDeckColumn(state, column) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + if (column.name == undefined) column.name = null; + deck.columns.push(column); + deck.layout.push([column.id]); + os.store.dispatch('settings/updateDeckProfile'); + }, + + removeDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + deck.columns = deck.columns.filter(c => c.id != id); + deck.layout = deck.layout.map(ids => erase(id, ids)); + deck.layout = deck.layout.filter(ids => ids.length > 0); + os.store.dispatch('settings/updateDeckProfile'); + }, + + swapDeckColumn(state, x) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const a = x.a; + const b = x.b; + const aX = deck.layout.findIndex(ids => ids.indexOf(a) != -1); + const aY = deck.layout[aX].findIndex(id => id == a); + const bX = deck.layout.findIndex(ids => ids.indexOf(b) != -1); + const bY = deck.layout[bX].findIndex(id => id == b); + deck.layout[aX][aY] = b; + deck.layout[bX][bY] = a; + os.store.dispatch('settings/updateDeckProfile'); + }, + + swapLeftDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + deck.layout.some((ids, i) => { + if (ids.indexOf(id) != -1) { + const left = deck.layout[i - 1]; + if (left) { + // https://vuejs.org/v2/guide/list.html#Caveats + //state.deck.layout[i - 1] = state.deck.layout[i]; + //state.deck.layout[i] = left; + deck.layout.splice(i - 1, 1, deck.layout[i]); + deck.layout.splice(i, 1, left); + } + return true; + } + }); + os.store.dispatch('settings/updateDeckProfile'); + }, + + swapRightDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + deck.layout.some((ids, i) => { + if (ids.indexOf(id) != -1) { + const right = deck.layout[i + 1]; + if (right) { + // https://vuejs.org/v2/guide/list.html#Caveats + //state.deck.layout[i + 1] = state.deck.layout[i]; + //state.deck.layout[i] = right; + deck.layout.splice(i + 1, 1, deck.layout[i]); + deck.layout.splice(i, 1, right); + } + return true; + } + }); + os.store.dispatch('settings/updateDeckProfile'); + }, + + swapUpDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const ids = deck.layout.find(ids => ids.indexOf(id) != -1); + ids.some((x, i) => { + if (x == id) { + const up = ids[i - 1]; + if (up) { + // https://vuejs.org/v2/guide/list.html#Caveats + //ids[i - 1] = id; + //ids[i] = up; + ids.splice(i - 1, 1, id); + ids.splice(i, 1, up); + } + return true; + } + }); + os.store.dispatch('settings/updateDeckProfile'); + }, + + swapDownDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const ids = deck.layout.find(ids => ids.indexOf(id) != -1); + ids.some((x, i) => { + if (x == id) { + const down = ids[i + 1]; + if (down) { + // https://vuejs.org/v2/guide/list.html#Caveats + //ids[i + 1] = id; + //ids[i] = down; + ids.splice(i + 1, 1, id); + ids.splice(i, 1, down); + } + return true; + } + }); + os.store.dispatch('settings/updateDeckProfile'); + }, + + stackLeftDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const i = deck.layout.findIndex(ids => ids.indexOf(id) != -1); + deck.layout = deck.layout.map(ids => erase(id, ids)); + const left = deck.layout[i - 1]; + if (left) deck.layout[i - 1].push(id); + deck.layout = deck.layout.filter(ids => ids.length > 0); + os.store.dispatch('settings/updateDeckProfile'); + }, + + popRightDeckColumn(state, id) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const i = deck.layout.findIndex(ids => ids.indexOf(id) != -1); + deck.layout = deck.layout.map(ids => erase(id, ids)); + deck.layout.splice(i + 1, 0, [id]); + deck.layout = deck.layout.filter(ids => ids.length > 0); + os.store.dispatch('settings/updateDeckProfile'); + }, + + addDeckWidget(state, x) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const column = deck.columns.find(c => c.id == x.id); + if (column == null) return; + column.widgets.unshift(x.widget); + os.store.dispatch('settings/updateDeckProfile'); + }, + + removeDeckWidget(state, x) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const column = deck.columns.find(c => c.id == x.id); + if (column == null) return; + column.widgets = column.widgets.filter(w => w.id != x.widget.id); + os.store.dispatch('settings/updateDeckProfile'); + }, + + renameDeckColumn(state, x) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + const column = deck.columns.find(c => c.id == x.id); + if (column == null) return; + column.name = x.name; + os.store.dispatch('settings/updateDeckProfile'); + }, + + updateDeckColumn(state, x) { + const deck = state.settings.deckProfiles[state.device.deckProfile]; + let column = deck.columns.find(c => c.id == x.id); + if (column == null) return; + column = x; + os.store.dispatch('settings/updateDeckProfile'); } }, @@ -159,176 +379,6 @@ export default (os: MiOS) => new Vuex.Store({ setVisibility(state, visibility) { state.visibility = visibility; }, - - setHome(state, data) { - state.home = data; - }, - - addHomeWidget(state, widget) { - state.home.unshift(widget); - }, - - setMobileHome(state, data) { - state.mobileHome = data; - }, - - updateWidget(state, x) { - let w; - - //#region Desktop home - if (state.home) { - w = state.home.find(w => w.id == x.id); - if (w) { - w.data = x.data; - } - } - //#endregion - - //#region Mobile home - if (state.mobileHome) { - w = state.mobileHome.find(w => w.id == x.id); - if (w) { - w.data = x.data; - } - } - //#endregion - }, - - addMobileHomeWidget(state, widget) { - state.mobileHome.unshift(widget); - }, - - removeMobileHomeWidget(state, widget) { - state.mobileHome = state.mobileHome.filter(w => w.id != widget.id); - }, - - addDeckColumn(state, column) { - if (column.name == undefined) column.name = null; - state.deck.columns.push(column); - state.deck.layout.push([column.id]); - }, - - removeDeckColumn(state, id) { - state.deck.columns = state.deck.columns.filter(c => c.id != id); - state.deck.layout = state.deck.layout.map(ids => erase(id, ids)); - state.deck.layout = state.deck.layout.filter(ids => ids.length > 0); - }, - - swapDeckColumn(state, x) { - const a = x.a; - const b = x.b; - const aX = state.deck.layout.findIndex(ids => ids.indexOf(a) != -1); - const aY = state.deck.layout[aX].findIndex(id => id == a); - const bX = state.deck.layout.findIndex(ids => ids.indexOf(b) != -1); - const bY = state.deck.layout[bX].findIndex(id => id == b); - state.deck.layout[aX][aY] = b; - state.deck.layout[bX][bY] = a; - }, - - swapLeftDeckColumn(state, id) { - state.deck.layout.some((ids, i) => { - if (ids.indexOf(id) != -1) { - const left = state.deck.layout[i - 1]; - if (left) { - // https://vuejs.org/v2/guide/list.html#Caveats - //state.deck.layout[i - 1] = state.deck.layout[i]; - //state.deck.layout[i] = left; - state.deck.layout.splice(i - 1, 1, state.deck.layout[i]); - state.deck.layout.splice(i, 1, left); - } - return true; - } - }); - }, - - swapRightDeckColumn(state, id) { - state.deck.layout.some((ids, i) => { - if (ids.indexOf(id) != -1) { - const right = state.deck.layout[i + 1]; - if (right) { - // https://vuejs.org/v2/guide/list.html#Caveats - //state.deck.layout[i + 1] = state.deck.layout[i]; - //state.deck.layout[i] = right; - state.deck.layout.splice(i + 1, 1, state.deck.layout[i]); - state.deck.layout.splice(i, 1, right); - } - return true; - } - }); - }, - - swapUpDeckColumn(state, id) { - const ids = state.deck.layout.find(ids => ids.indexOf(id) != -1); - ids.some((x, i) => { - if (x == id) { - const up = ids[i - 1]; - if (up) { - // https://vuejs.org/v2/guide/list.html#Caveats - //ids[i - 1] = id; - //ids[i] = up; - ids.splice(i - 1, 1, id); - ids.splice(i, 1, up); - } - return true; - } - }); - }, - - swapDownDeckColumn(state, id) { - const ids = state.deck.layout.find(ids => ids.indexOf(id) != -1); - ids.some((x, i) => { - if (x == id) { - const down = ids[i + 1]; - if (down) { - // https://vuejs.org/v2/guide/list.html#Caveats - //ids[i + 1] = id; - //ids[i] = down; - ids.splice(i + 1, 1, id); - ids.splice(i, 1, down); - } - return true; - } - }); - }, - - stackLeftDeckColumn(state, id) { - const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1); - state.deck.layout = state.deck.layout.map(ids => erase(id, ids)); - const left = state.deck.layout[i - 1]; - if (left) state.deck.layout[i - 1].push(id); - state.deck.layout = state.deck.layout.filter(ids => ids.length > 0); - }, - - popRightDeckColumn(state, id) { - const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1); - state.deck.layout = state.deck.layout.map(ids => erase(id, ids)); - state.deck.layout.splice(i + 1, 0, [id]); - state.deck.layout = state.deck.layout.filter(ids => ids.length > 0); - }, - - addDeckWidget(state, x) { - const column = state.deck.columns.find(c => c.id == x.id); - if (column == null) return; - column.widgets.unshift(x.widget); - }, - - removeDeckWidget(state, x) { - const column = state.deck.columns.find(c => c.id == x.id); - if (column == null) return; - column.widgets = column.widgets.filter(w => w.id != x.widget.id); - }, - - renameDeckColumn(state, x) { - const column = state.deck.columns.find(c => c.id == x.id); - if (column == null) return; - column.name = x.name; - }, - - updateDeckColumn(state, x) { - let column = state.deck.columns.find(c => c.id == x.id); - if (column == null) return; - column = x; - } } }, @@ -361,6 +411,42 @@ export default (os: MiOS) => new Vuex.Store({ }); } }, + + updateHomeProfile(ctx) { + const profiles = ctx.state.homeProfiles; + ctx.commit('set', { + key: 'homeProfiles', + value: profiles + }); + os.api('i/update-client-setting', { + name: 'homeProfiles', + value: profiles + }); + }, + + updateMobileHomeProfile(ctx) { + const profiles = ctx.state.mobileHomeProfiles; + ctx.commit('set', { + key: 'mobileHomeProfiles', + value: profiles + }); + os.api('i/update-client-setting', { + name: 'mobileHomeProfiles', + value: profiles + }); + }, + + updateDeckProfile(ctx) { + const profiles = ctx.state.deckProfiles; + ctx.commit('set', { + key: 'deckProfiles', + value: profiles + }); + os.api('i/update-client-setting', { + name: 'deckProfiles', + value: profiles + }); + }, } } } diff --git a/src/mfm/language.ts b/src/mfm/language.ts index 4750ea3380..75a2baaab9 100644 --- a/src/mfm/language.ts +++ b/src/mfm/language.ts @@ -157,12 +157,14 @@ export const mfmLanguage = P.createLanguage({ let url: string; if (!match) { const match = text.match(/^<(https?:\/\/.*?)>/); - if (!match) + if (!match) { return P.makeFailure(i, 'not a url'); + } url = match[1]; i += 2; - } else + } else { url = match[0]; + } url = removeOrphanedBrackets(url); while (url.endsWith('.') || url.endsWith(',')) { if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.')); diff --git a/src/misc/aiscript/evaluator.ts b/src/misc/aiscript/evaluator.ts index d93fcebcf7..f6165afb64 100644 --- a/src/misc/aiscript/evaluator.ts +++ b/src/misc/aiscript/evaluator.ts @@ -171,6 +171,7 @@ export class ASEvaluator { numberToString: (a: number) => a.toString(), splitStrByLine: (a: string) => a.split('\n'), pick: (list: any[], i: number) => list[i - 1], + listLen: (list: any[]) => list.length, random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability, rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)), randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)], diff --git a/src/misc/aiscript/index.ts b/src/misc/aiscript/index.ts index 8635399daa..3a21e9b1cc 100644 --- a/src/misc/aiscript/index.ts +++ b/src/misc/aiscript/index.ts @@ -74,6 +74,7 @@ export const funcDefs: Record => { + if (actor.isSuspended) return; + switch (activity.type) { case 'Create': await create(actor, activity); diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 4da0c7476c..1aa9a855dd 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -7,6 +7,7 @@ import * as pkg from '../../../../package.json'; import { Emojis } from '../../../models'; import { types, bool } from '../../../misc/schema'; import { getConnection } from 'typeorm'; +import redis from '../../../db/redis'; export const meta = { stability: 'stable', @@ -116,6 +117,7 @@ export default define(meta, async (ps, me) => { os: os.platform(), node: process.version, psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version), + redis: redis.server_info.redis_version, cpu: { model: os.cpus()[0].model, diff --git a/src/server/web/index.ts b/src/server/web/index.ts index c5a3497f44..8cf6a75208 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -20,6 +20,8 @@ import { Users, Notes, Emojis, UserProfiles, Pages } from '../../models'; import parseAcct from '../../misc/acct/parse'; import getNoteSummary from '../../misc/get-note-summary'; import { ensure } from '../../prelude/ensure'; +import { getConnection } from 'typeorm'; +import redis from '../../db/redis'; const client = `${__dirname}/../../client/`; @@ -250,6 +252,8 @@ router.get('/info', async ctx => { machine: os.hostname(), os: os.platform(), node: process.version, + psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version), + redis: redis.server_info.redis_version, cpu: { model: os.cpus()[0].model, cores: os.cpus().length diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts index e5b9ff6244..310cf88c02 100644 --- a/src/server/web/url-preview.ts +++ b/src/server/web/url-preview.ts @@ -12,18 +12,20 @@ module.exports = async (ctx: Koa.BaseContext) => { const meta = await fetchMeta(); logger.info(meta.summalyProxy - ? `(Proxy) Getting preview of ${ctx.query.url} ...` - : `Getting preview of ${ctx.query.url} ...`); + ? `(Proxy) Getting preview of ${ctx.query.url}@${ctx.query.lang} ...` + : `Getting preview of ${ctx.query.url}@${ctx.query.lang} ...`); try { const summary = meta.summalyProxy ? await request.get({ url: meta.summalyProxy, qs: { - url: ctx.query.url + url: ctx.query.url, + lang: ctx.query.lang || 'ja-JP' }, json: true }) : await summaly(ctx.query.url, { - followRedirects: false + followRedirects: false, + lang: ctx.query.lang || 'ja-JP' }); logger.succ(`Got preview of ${ctx.query.url}: ${summary.title}`); diff --git a/src/server/web/views/info.pug b/src/server/web/views/info.pug index c8b0bd939a..fc71e5c193 100644 --- a/src/server/web/views/info.pug +++ b/src/server/web/views/info.pug @@ -85,6 +85,12 @@ html tr th Node version td= node + tr + th PSQL version + td= psql + tr + th Redis version + td= redis tr th Machine td= machine diff --git a/src/services/following/delete.ts b/src/services/following/delete.ts index ad09f0e6d1..e3b5f8c414 100644 --- a/src/services/following/delete.ts +++ b/src/services/following/delete.ts @@ -22,7 +22,7 @@ export default async function(follower: User, followee: User, silent = false) { return; } - Followings.delete(following.id); + await Followings.delete(following.id); //#region Decrement following count Users.decrement({ id: follower.id }, 'followingCount', 1);