Merge branch 'develop' of https://github.com/syuilo/misskey into develop
This commit is contained in:
commit
538bb978ed
|
@ -55,7 +55,7 @@
|
||||||
"@types/koa-send": "4.1.1",
|
"@types/koa-send": "4.1.1",
|
||||||
"@types/koa-views": "2.0.3",
|
"@types/koa-views": "2.0.3",
|
||||||
"@types/koa__cors": "2.2.3",
|
"@types/koa__cors": "2.2.3",
|
||||||
"@types/minio": "6.0.2",
|
"@types/minio": "7.0.0",
|
||||||
"@types/mkdirp": "0.5.2",
|
"@types/mkdirp": "0.5.2",
|
||||||
"@types/mocha": "5.2.3",
|
"@types/mocha": "5.2.3",
|
||||||
"@types/mongodb": "3.1.4",
|
"@types/mongodb": "3.1.4",
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
"@types/webpack": "4.4.11",
|
"@types/webpack": "4.4.11",
|
||||||
"@types/webpack-stream": "3.2.10",
|
"@types/webpack-stream": "3.2.10",
|
||||||
"@types/websocket": "0.0.40",
|
"@types/websocket": "0.0.40",
|
||||||
"@types/ws": "6.0.0",
|
"@types/ws": "6.0.1",
|
||||||
"animejs": "2.2.0",
|
"animejs": "2.2.0",
|
||||||
"autosize": "4.0.2",
|
"autosize": "4.0.2",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
|
|
|
@ -140,7 +140,7 @@
|
||||||
// Random
|
// Random
|
||||||
localStorage.setItem('salt', Math.random().toString());
|
localStorage.setItem('salt', Math.random().toString());
|
||||||
|
|
||||||
// Clear cache (serive worker)
|
// Clear cache (service worker)
|
||||||
try {
|
try {
|
||||||
navigator.serviceWorker.controller.postMessage('clear');
|
navigator.serviceWorker.controller.postMessage('clear');
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default async function(mios: MiOS, force = false, silent = false) {
|
||||||
localStorage.setItem('should-refresh', 'true');
|
localStorage.setItem('should-refresh', 'true');
|
||||||
localStorage.setItem('v', newer);
|
localStorage.setItem('v', newer);
|
||||||
|
|
||||||
// Clear cache (serive worker)
|
// Clear cache (service worker)
|
||||||
try {
|
try {
|
||||||
if (navigator.serviceWorker.controller) {
|
if (navigator.serviceWorker.controller) {
|
||||||
navigator.serviceWorker.controller.postMessage('clear');
|
navigator.serviceWorker.controller.postMessage('clear');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import * as uuid from 'uuid';
|
import * as uuid from 'uuid';
|
||||||
import Connection from './stream';
|
import Connection from './stream';
|
||||||
|
import { erase } from '../../../../../prelude/array';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ストリーム接続を管理するクラス
|
* ストリーム接続を管理するクラス
|
||||||
|
@ -89,7 +90,7 @@ export default abstract class StreamManager<T extends Connection> extends EventE
|
||||||
* @param userId use で発行したユーザーID
|
* @param userId use で発行したユーザーID
|
||||||
*/
|
*/
|
||||||
public dispose(userId) {
|
public dispose(userId) {
|
||||||
this.users = this.users.filter(id => id != userId);
|
this.users = erase(userId, this.users);
|
||||||
|
|
||||||
this._connection.user = `Managed (${ this.users.length })`;
|
this._connection.user = `Managed (${ this.users.length })`;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { erase } from '../../../../../prelude/array';
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -53,7 +54,7 @@ export default Vue.extend({
|
||||||
|
|
||||||
get() {
|
get() {
|
||||||
return {
|
return {
|
||||||
choices: this.choices.filter(choice => choice != '')
|
choices: erase('', this.choices)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ import getFace from '../../../common/scripts/get-face';
|
||||||
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
||||||
import parse from '../../../../../mfm/parse';
|
import parse from '../../../../../mfm/parse';
|
||||||
import { host } from '../../../config';
|
import { host } from '../../../config';
|
||||||
|
import { erase } from '../../../../../prelude/array';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -346,7 +347,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
removeVisibleUser(user) {
|
removeVisibleUser(user) {
|
||||||
this.visibleUsers = this.visibleUsers.filter(u => u != user);
|
this.visibleUsers = erase(user, this.visibleUsers);
|
||||||
},
|
},
|
||||||
|
|
||||||
post() {
|
post() {
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { host, copyright } from '../../../config';
|
import { host, copyright } from '../../../config';
|
||||||
|
import { concat } from '../../../../../prelude/array';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
|
@ -119,8 +120,8 @@ export default Vue.extend({
|
||||||
(this as any).api('notes/local-timeline', {
|
(this as any).api('notes/local-timeline', {
|
||||||
fileType: image,
|
fileType: image,
|
||||||
limit: 6
|
limit: 6
|
||||||
}).then(notes => {
|
}).then((notes: any[]) => {
|
||||||
const files = [].concat(...notes.map(n => n.files));
|
const files = concat(notes.map((n: any): any[] => n.files));
|
||||||
this.photos = files.filter(f => image.includes(f.type)).slice(0, 6);
|
this.photos = files.filter(f => image.includes(f.type)).slice(0, 6);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,7 @@ import Err from './common/views/components/connect-failed.vue';
|
||||||
import { LocalTimelineStreamManager } from './common/scripts/streaming/local-timeline';
|
import { LocalTimelineStreamManager } from './common/scripts/streaming/local-timeline';
|
||||||
import { HybridTimelineStreamManager } from './common/scripts/streaming/hybrid-timeline';
|
import { HybridTimelineStreamManager } from './common/scripts/streaming/hybrid-timeline';
|
||||||
import { GlobalTimelineStreamManager } from './common/scripts/streaming/global-timeline';
|
import { GlobalTimelineStreamManager } from './common/scripts/streaming/global-timeline';
|
||||||
|
import { erase } from '../../prelude/array';
|
||||||
|
|
||||||
//#region api requests
|
//#region api requests
|
||||||
let spinner = null;
|
let spinner = null;
|
||||||
|
@ -537,7 +538,7 @@ export default class MiOS extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public unregisterStreamConnection(connection: Connection) {
|
public unregisterStreamConnection(connection: Connection) {
|
||||||
this.connections = this.connections.filter(c => c != connection);
|
this.connections = erase(connection, this.connections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ import MkVisibilityChooser from '../../../common/views/components/visibility-cho
|
||||||
import getFace from '../../../common/scripts/get-face';
|
import getFace from '../../../common/scripts/get-face';
|
||||||
import parse from '../../../../../mfm/parse';
|
import parse from '../../../../../mfm/parse';
|
||||||
import { host } from '../../../config';
|
import { host } from '../../../config';
|
||||||
|
import { erase } from '../../../../../prelude/array';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -262,7 +263,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
removeVisibleUser(user) {
|
removeVisibleUser(user) {
|
||||||
this.visibleUsers = this.visibleUsers.filter(u => u != user);
|
this.visibleUsers = erase(user, this.visibleUsers);
|
||||||
},
|
},
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { apiUrl, copyright, host } from '../../../config';
|
import { apiUrl, copyright, host } from '../../../config';
|
||||||
|
import { concat } from '../../../../../prelude/array';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
|
@ -79,8 +80,8 @@ export default Vue.extend({
|
||||||
(this as any).api('notes/local-timeline', {
|
(this as any).api('notes/local-timeline', {
|
||||||
fileType: image,
|
fileType: image,
|
||||||
limit: 6
|
limit: 6
|
||||||
}).then(notes => {
|
}).then((notes: any[]) => {
|
||||||
const files = [].concat(...notes.map(n => n.files));
|
const files = concat(notes.map((n: any): any[] => n.files));
|
||||||
this.photos = files.filter(f => image.includes(f.type)).slice(0, 6);
|
this.photos = files.filter(f => image.includes(f.type)).slice(0, 6);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import * as nestedProperty from 'nested-property';
|
||||||
|
|
||||||
import MiOS from './mios';
|
import MiOS from './mios';
|
||||||
import { hostname } from './config';
|
import { hostname } from './config';
|
||||||
|
import { erase } from '../../prelude/array';
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
home: null,
|
home: null,
|
||||||
|
@ -195,7 +196,7 @@ export default (os: MiOS) => new Vuex.Store({
|
||||||
|
|
||||||
removeDeckColumn(state, id) {
|
removeDeckColumn(state, id) {
|
||||||
state.deck.columns = state.deck.columns.filter(c => c.id != id);
|
state.deck.columns = state.deck.columns.filter(c => c.id != id);
|
||||||
state.deck.layout = state.deck.layout.map(ids => ids.filter(x => x != id));
|
state.deck.layout = state.deck.layout.map(ids => erase(id, ids));
|
||||||
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -266,7 +267,7 @@ export default (os: MiOS) => new Vuex.Store({
|
||||||
|
|
||||||
stackLeftDeckColumn(state, id) {
|
stackLeftDeckColumn(state, id) {
|
||||||
const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
||||||
state.deck.layout = state.deck.layout.map(ids => ids.filter(x => x != id));
|
state.deck.layout = state.deck.layout.map(ids => erase(id, ids));
|
||||||
const left = state.deck.layout[i - 1];
|
const left = state.deck.layout[i - 1];
|
||||||
if (left) state.deck.layout[i - 1].push(id);
|
if (left) state.deck.layout[i - 1].push(id);
|
||||||
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
||||||
|
@ -274,7 +275,7 @@ export default (os: MiOS) => new Vuex.Store({
|
||||||
|
|
||||||
popRightDeckColumn(state, id) {
|
popRightDeckColumn(state, id) {
|
||||||
const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
||||||
state.deck.layout = state.deck.layout.map(ids => ids.filter(x => x != id));
|
state.deck.layout = state.deck.layout.map(ids => erase(id, ids));
|
||||||
state.deck.layout.splice(i + 1, 0, [id]);
|
state.deck.layout.splice(i + 1, 0, [id]);
|
||||||
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import composeNotification from './common/scripts/compose-notification';
|
import composeNotification from './common/scripts/compose-notification';
|
||||||
|
import { erase } from '../../prelude/array';
|
||||||
|
|
||||||
// キャッシュするリソース
|
// キャッシュするリソース
|
||||||
const cachee = [
|
const cachee = [
|
||||||
|
@ -24,8 +25,7 @@ self.addEventListener('activate', ev => {
|
||||||
// Clean up old caches
|
// Clean up old caches
|
||||||
ev.waitUntil(
|
ev.waitUntil(
|
||||||
caches.keys().then(keys => Promise.all(
|
caches.keys().then(keys => Promise.all(
|
||||||
keys
|
erase(_VERSION_, keys)
|
||||||
.filter(key => key != _VERSION_)
|
|
||||||
.map(key => caches.delete(key))
|
.map(key => caches.delete(key))
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { count, countIf } from "../../prelude/array";
|
import { count, concat } from "../../prelude/array";
|
||||||
|
|
||||||
// MISSKEY REVERSI ENGINE
|
// MISSKEY REVERSI ENGINE
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ export default class Reversi {
|
||||||
* 白石の数
|
* 白石の数
|
||||||
*/
|
*/
|
||||||
public get whiteCount() {
|
public get whiteCount() {
|
||||||
return count(BLACK, this.board);
|
return count(WHITE, this.board);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -238,87 +238,55 @@ export default class Reversi {
|
||||||
/**
|
/**
|
||||||
* 指定のマスに石を置いた時の、反転させられる石を取得します
|
* 指定のマスに石を置いた時の、反転させられる石を取得します
|
||||||
* @param color 自分の色
|
* @param color 自分の色
|
||||||
* @param pos 位置
|
* @param initPos 位置
|
||||||
*/
|
*/
|
||||||
public effects(color: Color, pos: number): number[] {
|
public effects(color: Color, initPos: number): number[] {
|
||||||
const enemyColor = !color;
|
const enemyColor = !color;
|
||||||
|
|
||||||
// ひっくり返せる石(の位置)リスト
|
const diffVectors: [number, number][] = [
|
||||||
let stones: number[] = [];
|
[ 0, -1], // 上
|
||||||
|
[ +1, -1], // 右上
|
||||||
|
[ +1, 0], // 右
|
||||||
|
[ +1, +1], // 右下
|
||||||
|
[ 0, +1], // 下
|
||||||
|
[ -1, +1], // 左下
|
||||||
|
[ -1, 0], // 左
|
||||||
|
[ -1, -1] // 左上
|
||||||
|
];
|
||||||
|
|
||||||
const initPos = pos;
|
const effectsInLine = ([dx, dy]: [number, number]): number[] => {
|
||||||
|
const nextPos = (x: number, y: number): [number, number] => [x + dx, y + dy];
|
||||||
// 走査
|
|
||||||
const iterate = (fn: (i: number) => number[]) => {
|
|
||||||
let i = 1;
|
|
||||||
const found = [];
|
|
||||||
|
|
||||||
|
const found: number[] = []; // 挟めるかもしれない相手の石を入れておく配列
|
||||||
|
let [x, y] = this.transformPosToXy(initPos);
|
||||||
while (true) {
|
while (true) {
|
||||||
let [x, y] = fn(i);
|
[x, y] = nextPos(x, y);
|
||||||
|
|
||||||
// 座標が指し示す位置がボード外に出たとき
|
// 座標が指し示す位置がボード外に出たとき
|
||||||
if (this.opts.loopedBoard) {
|
if (this.opts.loopedBoard) {
|
||||||
if (x < 0 ) x = this.mapWidth - ((-x) % this.mapWidth);
|
x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth;
|
||||||
if (y < 0 ) y = this.mapHeight - ((-y) % this.mapHeight);
|
y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight;
|
||||||
if (x >= this.mapWidth ) x = x % this.mapWidth;
|
|
||||||
if (y >= this.mapHeight) y = y % this.mapHeight;
|
|
||||||
|
|
||||||
// for debug
|
|
||||||
//if (x < 0 || y < 0 || x >= this.mapWidth || y >= this.mapHeight) {
|
|
||||||
// console.log(x, y);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 一周して自分に帰ってきたら
|
|
||||||
if (this.transformXyToPos(x, y) == initPos) {
|
if (this.transformXyToPos(x, y) == initPos) {
|
||||||
// ↓のコメントアウトを外すと、「現時点で自分の石が隣接していないが、
|
// 盤面の境界でループし、自分が石を置く位置に戻ってきたとき、挟めるようにしている (ref: Test4のマップ)
|
||||||
// そこに置いたとするとループして最終的に挟んだことになる」というケースを有効化します。(Test4のマップで違いが分かります)
|
return found;
|
||||||
// このケースを有効にした方が良いのか無効にした方が良いのか判断がつかなかったためとりあえず無効としておきます
|
|
||||||
// (あと無効な方がゲームとしておもしろそうだった)
|
|
||||||
stones = stones.concat(found);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (x == -1 || y == -1 || x == this.mapWidth || y == this.mapHeight) break;
|
if (x == -1 || y == -1 || x == this.mapWidth || y == this.mapHeight) {
|
||||||
|
return []; // 挟めないことが確定 (盤面外に到達)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pos = this.transformXyToPos(x, y);
|
const pos = this.transformXyToPos(x, y);
|
||||||
|
if (this.mapDataGet(pos) === 'null') return []; // 挟めないことが確定 (配置不可能なマスに到達)
|
||||||
//#region 「配置不能」マスに当たった場合走査終了
|
|
||||||
const pixel = this.mapDataGet(pos);
|
|
||||||
if (pixel == 'null') break;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
// 石取得
|
|
||||||
const stone = this.board[pos];
|
const stone = this.board[pos];
|
||||||
|
if (stone === null) return []; // 挟めないことが確定 (石が置かれていないマスに到達)
|
||||||
// 石が置かれていないマスなら走査終了
|
if (stone === enemyColor) found.push(pos); // 挟めるかもしれない (相手の石を発見)
|
||||||
if (stone === null) break;
|
if (stone === color) return found; // 挟めることが確定 (対となる自分の石を発見)
|
||||||
|
|
||||||
// 相手の石なら「ひっくり返せるかもリスト」に入れておく
|
|
||||||
if (stone === enemyColor) found.push(pos);
|
|
||||||
|
|
||||||
// 自分の石なら「ひっくり返せるかもリスト」を「ひっくり返せるリスト」に入れ、走査終了
|
|
||||||
if (stone === color) {
|
|
||||||
stones = stones.concat(found);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [x, y] = this.transformPosToXy(pos);
|
return concat(diffVectors.map(effectsInLine));
|
||||||
|
|
||||||
iterate(i => [x , y - i]); // 上
|
|
||||||
iterate(i => [x + i, y - i]); // 右上
|
|
||||||
iterate(i => [x + i, y ]); // 右
|
|
||||||
iterate(i => [x + i, y + i]); // 右下
|
|
||||||
iterate(i => [x , y + i]); // 下
|
|
||||||
iterate(i => [x - i, y + i]); // 左下
|
|
||||||
iterate(i => [x - i, y ]); // 左
|
|
||||||
iterate(i => [x - i, y - i]); // 左上
|
|
||||||
|
|
||||||
return stones;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,6 +6,18 @@ export function count<T>(x: T, xs: T[]): number {
|
||||||
return countIf(y => x === y, xs);
|
return countIf(y => x === y, xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function intersperse<T>(sep: T, xs: T[]): T[] {
|
export function concat<T>(xss: T[][]): T[] {
|
||||||
return [].concat(...xs.map(x => [sep, x])).slice(1);
|
return ([] as T[]).concat(...xss);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function intersperse<T>(sep: T, xs: T[]): T[] {
|
||||||
|
return concat(xs.map(x => [sep, x])).slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function erase<T>(x: T, xs: T[]): T[] {
|
||||||
|
return xs.filter(y => x !== y);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unique<T>(xs: T[]): T[] {
|
||||||
|
return [...new Set(xs)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Note from '../../../../models/note';
|
import Note from '../../../../models/note';
|
||||||
|
import { erase } from '../../../../prelude/array';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要
|
トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要
|
||||||
|
@ -85,8 +86,7 @@ export default () => new Promise(async (res, rej) => {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// タグを人気順に並べ替え
|
// タグを人気順に並べ替え
|
||||||
let hots = (await Promise.all(hotsPromises))
|
let hots = erase(null, await Promise.all(hotsPromises))
|
||||||
.filter(x => x != null)
|
|
||||||
.sort((a, b) => b.count - a.count)
|
.sort((a, b) => b.count - a.count)
|
||||||
.map(tag => tag.name)
|
.map(tag => tag.name)
|
||||||
.slice(0, max);
|
.slice(0, max);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Mute from '../../../../models/mute';
|
||||||
import { getFriendIds } from '../../common/get-friends';
|
import { getFriendIds } from '../../common/get-friends';
|
||||||
import { pack } from '../../../../models/note';
|
import { pack } from '../../../../models/note';
|
||||||
import getParams from '../../get-params';
|
import getParams from '../../get-params';
|
||||||
|
import { erase } from '../../../../prelude/array';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -103,23 +104,23 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
|
||||||
if (psErr) throw psErr;
|
if (psErr) throw psErr;
|
||||||
|
|
||||||
if (ps.includeUserUsernames != null) {
|
if (ps.includeUserUsernames != null) {
|
||||||
const ids = (await Promise.all(ps.includeUserUsernames.map(async (username) => {
|
const ids = erase(null, await Promise.all(ps.includeUserUsernames.map(async (username) => {
|
||||||
const _user = await User.findOne({
|
const _user = await User.findOne({
|
||||||
usernameLower: username.toLowerCase()
|
usernameLower: username.toLowerCase()
|
||||||
});
|
});
|
||||||
return _user ? _user._id : null;
|
return _user ? _user._id : null;
|
||||||
}))).filter(id => id != null);
|
})));
|
||||||
|
|
||||||
ids.forEach(id => ps.includeUserIds.push(id));
|
ids.forEach(id => ps.includeUserIds.push(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.excludeUserUsernames != null) {
|
if (ps.excludeUserUsernames != null) {
|
||||||
const ids = (await Promise.all(ps.excludeUserUsernames.map(async (username) => {
|
const ids = erase(null, await Promise.all(ps.excludeUserUsernames.map(async (username) => {
|
||||||
const _user = await User.findOne({
|
const _user = await User.findOne({
|
||||||
usernameLower: username.toLowerCase()
|
usernameLower: username.toLowerCase()
|
||||||
});
|
});
|
||||||
return _user ? _user._id : null;
|
return _user ? _user._id : null;
|
||||||
}))).filter(id => id != null);
|
})));
|
||||||
|
|
||||||
ids.forEach(id => ps.excludeUserIds.push(id));
|
ids.forEach(id => ps.excludeUserIds.push(id));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import isQuote from '../../misc/is-quote';
|
||||||
import { TextElementMention } from '../../mfm/parse/elements/mention';
|
import { TextElementMention } from '../../mfm/parse/elements/mention';
|
||||||
import { TextElementHashtag } from '../../mfm/parse/elements/hashtag';
|
import { TextElementHashtag } from '../../mfm/parse/elements/hashtag';
|
||||||
import { updateNoteStats } from '../update-chart';
|
import { updateNoteStats } from '../update-chart';
|
||||||
|
import { erase, unique } from '../../prelude/array';
|
||||||
|
|
||||||
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
||||||
if (data.viaMobile == null) data.viaMobile = false;
|
if (data.viaMobile == null) data.viaMobile = false;
|
||||||
|
|
||||||
if (data.visibleUsers) {
|
if (data.visibleUsers) {
|
||||||
data.visibleUsers = data.visibleUsers.filter(x => x != null);
|
data.visibleUsers = erase(null, data.visibleUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.reply && data.reply.deletedAt != null) {
|
if (data.reply && data.reply.deletedAt != null) {
|
||||||
|
@ -384,7 +385,7 @@ function extractHashtags(tokens: ReturnType<typeof parse>): string[] {
|
||||||
.map(t => (t as TextElementHashtag).hashtag)
|
.map(t => (t as TextElementHashtag).hashtag)
|
||||||
.filter(tag => tag.length <= 100);
|
.filter(tag => tag.length <= 100);
|
||||||
|
|
||||||
return [...new Set(hashtags)];
|
return unique(hashtags);
|
||||||
}
|
}
|
||||||
|
|
||||||
function index(note: INote) {
|
function index(note: INote) {
|
||||||
|
@ -541,20 +542,20 @@ function incNotesCount(user: IUser) {
|
||||||
async function extractMentionedUsers(tokens: ReturnType<typeof parse>): Promise<IUser[]> {
|
async function extractMentionedUsers(tokens: ReturnType<typeof parse>): Promise<IUser[]> {
|
||||||
if (tokens == null) return [];
|
if (tokens == null) return [];
|
||||||
|
|
||||||
const mentionTokens = [...new Set(
|
const mentionTokens = unique(
|
||||||
tokens
|
tokens
|
||||||
.filter(t => t.type == 'mention') as TextElementMention[]
|
.filter(t => t.type == 'mention') as TextElementMention[]
|
||||||
)];
|
);
|
||||||
|
|
||||||
const mentionedUsers = [...new Set(
|
const mentionedUsers = unique(
|
||||||
(await Promise.all(mentionTokens.map(async m => {
|
erase(null, await Promise.all(mentionTokens.map(async m => {
|
||||||
try {
|
try {
|
||||||
return await resolveUser(m.username, m.host);
|
return await resolveUser(m.username, m.host);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}))).filter(x => x != null)
|
})))
|
||||||
)];
|
);
|
||||||
|
|
||||||
return mentionedUsers;
|
return mentionedUsers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"no-empty":false,
|
"no-empty":false,
|
||||||
"ordered-imports": [false],
|
"ordered-imports": [false],
|
||||||
"arrow-parens": false,
|
"arrow-parens": false,
|
||||||
|
"array-type": false,
|
||||||
"object-literal-shorthand": false,
|
"object-literal-shorthand": false,
|
||||||
"object-literal-key-quotes": false,
|
"object-literal-key-quotes": false,
|
||||||
"triple-equals": [false],
|
"triple-equals": [false],
|
||||||
|
|
Loading…
Reference in a new issue