diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd6e095b..e7800ab72 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 29395ecfd..ff7b730c2 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 08ca17592..3b667258f 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 bc6515cd5..2541e1ca6 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 5ac2561d1..fd8e96588 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 bbfa7f10e..174b69110 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 100b2f912..fe593c24f 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 d9223b1c4..88673e804 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 8f6fc1896..85ad415d7 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 72070e4d4..5272973ec 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 857a00ca9..355b3cb68 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 c42b54baa..296c0ccca 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 eecde5c73..e7a34c4ac 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 002e3bfab..01450b956 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 39d9920a4..5b4e6799e 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 e5946f04f..9d609db15 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 632ddf2ed..ba4deafe3 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 4b454f880..e64968007 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 0cbc40da5..1254eb5e5 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 9c61e4605..20fbcbb04 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 ae1446b93..a73ef45c3 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 7130fddb3..c012de131 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 6f545eb09..852d2c393 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 4750ea338..75a2baaab 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 d93fcebcf..f6165afb6 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 8635399da..3a21e9b1c 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 4da0c7476..1aa9a855d 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 c5a3497f4..8cf6a7520 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 e5b9ff624..310cf88c0 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 c8b0bd939..fc71e5c19 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 ad09f0e6d..e3b5f8c41 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);