mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-31 20:35:52 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9059c149dd | |||
| 7d8e70b2ac | |||
| 89105f5641 | |||
| 1813d17b4c | |||
| ce27b36fd0 | |||
| e635a87628 | |||
| 80c52433cc | |||
| 1472f0b141 | |||
| 4d914f5c0a | |||
| 0318f7344f | |||
| 413fbb3d0c | |||
| 8bc47baf4f | |||
| e3f6d42a47 | |||
| 8230935fd3 | |||
| f968d05ea0 | |||
| d6e5dc2167 | |||
| 460147fea2 | |||
| cea44834bb | |||
| 1af50fd7b8 | |||
| b18013025f |
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "2.37.5",
|
"version": "2.38.3",
|
||||||
"clientVersion": "1.0.6468",
|
"clientVersion": "1.0.6486",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -40,6 +40,17 @@ export default Vue.component('mk-note-html', {
|
|||||||
ast = this.ast;
|
ast = this.ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ast.filter(x => x.type != 'hashtag').length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ast[ast.length - 1] && (
|
||||||
|
ast[ast.length - 1].type == 'hashtag' ||
|
||||||
|
(ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == ' ') ||
|
||||||
|
(ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == '\n'))) {
|
||||||
|
ast.pop();
|
||||||
|
}
|
||||||
|
|
||||||
// Parse ast to DOM
|
// Parse ast to DOM
|
||||||
const els = flatten(ast.map(token => {
|
const els = flatten(ast.map(token => {
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
@@ -92,7 +103,7 @@ export default Vue.component('mk-note-html', {
|
|||||||
case 'hashtag':
|
case 'hashtag':
|
||||||
return createElement('a', {
|
return createElement('a', {
|
||||||
attrs: {
|
attrs: {
|
||||||
href: `${url}/search?q=${token.content}`,
|
href: `${url}/tags/${token.hashtag}`,
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
}
|
}
|
||||||
}, token.content);
|
}, token.content);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ root(isDark)
|
|||||||
> .fetching
|
> .fetching
|
||||||
> .empty
|
> .empty
|
||||||
margin 0
|
margin 0
|
||||||
padding 12px 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color #aaa
|
||||||
|
|
||||||
@@ -76,18 +76,13 @@ root(isDark)
|
|||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
|
||||||
> div
|
> div
|
||||||
.chart-enter
|
.chart-move
|
||||||
.chart-leave-to
|
transition transform 1s ease
|
||||||
opacity 0
|
|
||||||
transform translateY(-30px)
|
|
||||||
|
|
||||||
> *
|
|
||||||
transition transform .3s ease, opacity .3s ease
|
|
||||||
|
|
||||||
> div
|
> div
|
||||||
display flex
|
display flex
|
||||||
align-items center
|
align-items center
|
||||||
padding 16px
|
padding 14px 16px
|
||||||
|
|
||||||
&:not(:last-child)
|
&:not(:last-child)
|
||||||
border-bottom solid 1px isDark ? #393f4f : #eee
|
border-bottom solid 1px isDark ? #393f4f : #eee
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import * as XDraggable from 'vuedraggable';
|
|||||||
import getKao from '../../../common/scripts/get-kao';
|
import getKao from '../../../common/scripts/get-kao';
|
||||||
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
||||||
import parse from '../../../../../text/parse';
|
import parse from '../../../../../text/parse';
|
||||||
|
import { host } from '../../../config';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
@@ -129,6 +130,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
// 自分は除外
|
// 自分は除外
|
||||||
if (this.$store.state.i.username == x.username && x.host == null) return;
|
if (this.$store.state.i.username == x.username && x.host == null) return;
|
||||||
|
if (this.$store.state.i.username == x.username && x.host == host) return;
|
||||||
|
|
||||||
// 重複は除外
|
// 重複は除外
|
||||||
if (this.text.indexOf(`${mention} `) != -1) return;
|
if (this.text.indexOf(`${mention} `) != -1) return;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="gqpwvtwtprsbmnssnbicggtwqhmylhnq">
|
<div class="gqpwvtwtprsbmnssnbicggtwqhmylhnq">
|
||||||
<template v-if="edit">
|
<template v-if="edit">
|
||||||
<header>
|
<header>
|
||||||
<select v-model="widgetAdderSelected">
|
<select v-model="widgetAdderSelected" @change="addWidget">
|
||||||
<option value="profile">%i18n:common.widgets.profile%</option>
|
<option value="profile">%i18n:common.widgets.profile%</option>
|
||||||
<option value="analog-clock">%i18n:common.widgets.analog-clock%</option>
|
<option value="analog-clock">%i18n:common.widgets.analog-clock%</option>
|
||||||
<option value="calendar">%i18n:common.widgets.calendar%</option>
|
<option value="calendar">%i18n:common.widgets.calendar%</option>
|
||||||
@@ -30,20 +30,15 @@
|
|||||||
<option value="nav">%i18n:common.widgets.nav%</option>
|
<option value="nav">%i18n:common.widgets.nav%</option>
|
||||||
<option value="tips">%i18n:common.widgets.tips%</option>
|
<option value="tips">%i18n:common.widgets.tips%</option>
|
||||||
</select>
|
</select>
|
||||||
<button @click="addWidget">%i18n:@add%</button>
|
|
||||||
</header>
|
</header>
|
||||||
<x-draggable
|
<x-draggable
|
||||||
:list="column.widgets"
|
:list="column.widgets"
|
||||||
:options="{ handle: '.handle', animation: 150 }"
|
:options="{ animation: 150 }"
|
||||||
@sort="onWidgetSort"
|
@sort="onWidgetSort"
|
||||||
>
|
>
|
||||||
<div v-for="widget in column.widgets" class="customize-container" :key="widget.id">
|
<div v-for="widget in column.widgets" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="widgetFunc(widget.id)">
|
||||||
<header>
|
<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
|
||||||
<span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
|
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="deck"/>
|
||||||
</header>
|
|
||||||
<div @click="widgetFunc(widget.id)">
|
|
||||||
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="deck"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</x-draggable>
|
</x-draggable>
|
||||||
</template>
|
</template>
|
||||||
@@ -142,6 +137,13 @@ export default Vue.extend({
|
|||||||
|
|
||||||
root(isDark)
|
root(isDark)
|
||||||
.gqpwvtwtprsbmnssnbicggtwqhmylhnq
|
.gqpwvtwtprsbmnssnbicggtwqhmylhnq
|
||||||
|
> header
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
> *
|
||||||
|
width 100%
|
||||||
|
padding 4px
|
||||||
|
|
||||||
.widget, .customize-container
|
.widget, .customize-container
|
||||||
margin 8px
|
margin 8px
|
||||||
|
|
||||||
@@ -149,7 +151,21 @@ root(isDark)
|
|||||||
margin-top 0
|
margin-top 0
|
||||||
|
|
||||||
.customize-container
|
.customize-container
|
||||||
background #fff
|
cursor move
|
||||||
|
|
||||||
|
> *:not(.remove)
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
|
> .remove
|
||||||
|
position absolute
|
||||||
|
z-index 1
|
||||||
|
top 8px
|
||||||
|
right 8px
|
||||||
|
width 32px
|
||||||
|
height 32px
|
||||||
|
color #fff
|
||||||
|
background rgba(#000, 0.7)
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
> header
|
> header
|
||||||
color isDark ? #fff : #000
|
color isDark ? #fff : #000
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import MkUserLists from './views/pages/user-lists.vue';
|
|||||||
import MkUserList from './views/pages/user-list.vue';
|
import MkUserList from './views/pages/user-list.vue';
|
||||||
import MkSettings from './views/pages/settings.vue';
|
import MkSettings from './views/pages/settings.vue';
|
||||||
import MkOthello from './views/pages/othello.vue';
|
import MkOthello from './views/pages/othello.vue';
|
||||||
|
import MkTag from './views/pages/tag.vue';
|
||||||
|
|
||||||
Vue.use(MdCard);
|
Vue.use(MdCard);
|
||||||
Vue.use(MdButton);
|
Vue.use(MdButton);
|
||||||
@@ -88,6 +89,7 @@ init((launch) => {
|
|||||||
{ path: '/i/drive/file/:file', component: MkDrive },
|
{ path: '/i/drive/file/:file', component: MkDrive },
|
||||||
{ path: '/selectdrive', component: MkSelectDrive },
|
{ path: '/selectdrive', component: MkSelectDrive },
|
||||||
{ path: '/search', component: MkSearch },
|
{ path: '/search', component: MkSearch },
|
||||||
|
{ path: '/tags/:tag', component: MkTag },
|
||||||
{ path: '/othello', name: 'othello', component: MkOthello },
|
{ path: '/othello', name: 'othello', component: MkOthello },
|
||||||
{ path: '/othello/:game', component: MkOthello },
|
{ path: '/othello/:game', component: MkOthello },
|
||||||
{ path: '/@:user', component: MkUser },
|
{ path: '/@:user', component: MkUser },
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import * as XDraggable from 'vuedraggable';
|
|||||||
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
|
||||||
import getKao from '../../../common/scripts/get-kao';
|
import getKao from '../../../common/scripts/get-kao';
|
||||||
import parse from '../../../../../text/parse';
|
import parse from '../../../../../text/parse';
|
||||||
|
import { host } from '../../../config';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
@@ -123,6 +124,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
// 自分は除外
|
// 自分は除外
|
||||||
if (this.$store.state.i.username == x.username && x.host == null) return;
|
if (this.$store.state.i.username == x.username && x.host == null) return;
|
||||||
|
if (this.$store.state.i.username == x.username && x.host == host) return;
|
||||||
|
|
||||||
// 重複は除外
|
// 重複は除外
|
||||||
if (this.text.indexOf(`${mention} `) != -1) return;
|
if (this.text.indexOf(`${mention} `) != -1) return;
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<mk-ui>
|
||||||
|
<span slot="header">%fa:hashtag%{{ $route.params.tag }}</span>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<p v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
|
||||||
|
<mk-notes ref="timeline" :more="existMore ? more : null"/>
|
||||||
|
</main>
|
||||||
|
</mk-ui>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Progress from '../../../common/scripts/loading';
|
||||||
|
|
||||||
|
const limit = 20;
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
moreFetching: false,
|
||||||
|
existMore: false,
|
||||||
|
offset: 0,
|
||||||
|
empty: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: 'fetch'
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.fetch();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
this.fetching = true;
|
||||||
|
Progress.start();
|
||||||
|
|
||||||
|
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
|
||||||
|
(this as any).api('notes/search_by_tag', {
|
||||||
|
limit: limit + 1,
|
||||||
|
offset: this.offset,
|
||||||
|
tag: this.$route.params.tag
|
||||||
|
}).then(notes => {
|
||||||
|
if (notes.length == 0) this.empty = true;
|
||||||
|
if (notes.length == limit + 1) {
|
||||||
|
notes.pop();
|
||||||
|
this.existMore = true;
|
||||||
|
}
|
||||||
|
res(notes);
|
||||||
|
this.fetching = false;
|
||||||
|
Progress.done();
|
||||||
|
}, rej);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
more() {
|
||||||
|
this.offset += limit;
|
||||||
|
|
||||||
|
const promise = (this as any).api('notes/search_by_tag', {
|
||||||
|
limit: limit + 1,
|
||||||
|
offset: this.offset,
|
||||||
|
tag: this.$route.params.tag
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.then(notes => {
|
||||||
|
if (notes.length == limit + 1) {
|
||||||
|
notes.pop();
|
||||||
|
} else {
|
||||||
|
this.existMore = false;
|
||||||
|
}
|
||||||
|
notes.forEach(n => (this.$refs.timeline as any).append(n));
|
||||||
|
this.moreFetching = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
+6
-1
@@ -48,6 +48,11 @@ export type INote = {
|
|||||||
repliesCount: number;
|
repliesCount: number;
|
||||||
reactionCounts: any;
|
reactionCounts: any;
|
||||||
mentions: mongo.ObjectID[];
|
mentions: mongo.ObjectID[];
|
||||||
|
mentionedRemoteUsers: Array<{
|
||||||
|
uri: string;
|
||||||
|
username: string;
|
||||||
|
host: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public ... 公開
|
* public ... 公開
|
||||||
@@ -289,7 +294,7 @@ export const pack = async (
|
|||||||
|
|
||||||
// Poll
|
// Poll
|
||||||
if (meId && _note.poll && !hide) {
|
if (meId && _note.poll && !hide) {
|
||||||
_note.poll = (async (poll) => {
|
_note.poll = (async poll => {
|
||||||
const vote = await PollVote
|
const vote = await PollVote
|
||||||
.findOne({
|
.findOne({
|
||||||
userId: meId,
|
userId: meId,
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ const log = debug('misskey:activitypub');
|
|||||||
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> {
|
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> {
|
||||||
const uri = activity.id || activity;
|
const uri = activity.id || activity;
|
||||||
|
|
||||||
|
// アナウンサーが凍結されていたらスキップ
|
||||||
|
if (actor.isSuspended) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof uri !== 'string') {
|
if (typeof uri !== 'string') {
|
||||||
throw new Error('invalid announce');
|
throw new Error('invalid announce');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
|
|
||||||
export default tag => ({
|
export default (tag: string) => ({
|
||||||
type: 'Hashtag',
|
type: 'Hashtag',
|
||||||
href: `${config.url}/tags/${encodeURIComponent(tag)}`,
|
href: `${config.url}/tags/${encodeURIComponent(tag)}`,
|
||||||
name: '#' + tag
|
name: '#' + tag
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default (mention: {
|
||||||
|
uri: string;
|
||||||
|
username: string;
|
||||||
|
host: string;
|
||||||
|
}) => ({
|
||||||
|
type: 'Mention',
|
||||||
|
href: mention.uri,
|
||||||
|
name: `@${mention.username}@${mention.host}`
|
||||||
|
});
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import renderDocument from './document';
|
import renderDocument from './document';
|
||||||
import renderHashtag from './hashtag';
|
import renderHashtag from './hashtag';
|
||||||
|
import renderMention from './mention';
|
||||||
import config from '../../../config';
|
import config from '../../../config';
|
||||||
import DriveFile from '../../../models/drive-file';
|
import DriveFile from '../../../models/drive-file';
|
||||||
import Note, { INote } from '../../../models/note';
|
import Note, { INote } from '../../../models/note';
|
||||||
@@ -45,6 +46,18 @@ export default async function renderNote(note: INote, dive = true) {
|
|||||||
|
|
||||||
const attributedTo = `${config.url}/users/${user._id}`;
|
const attributedTo = `${config.url}/users/${user._id}`;
|
||||||
|
|
||||||
|
const mentions = note.mentionedRemoteUsers && note.mentionedRemoteUsers.length > 0
|
||||||
|
? note.mentionedRemoteUsers.map(x => x.uri)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const cc = ['public', 'home', 'followers'].includes(note.visibility)
|
||||||
|
? [`${attributedTo}/followers`].concat(mentions)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const hashtagTags = (note.tags || []).map(renderHashtag);
|
||||||
|
const mentionTags = (note.mentionedRemoteUsers || []).map(renderMention);
|
||||||
|
const tag = hashtagTags.concat(mentionTags)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `${config.url}/notes/${note._id}`,
|
id: `${config.url}/notes/${note._id}`,
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
@@ -52,9 +65,9 @@ export default async function renderNote(note: INote, dive = true) {
|
|||||||
content: toHtml(note),
|
content: toHtml(note),
|
||||||
published: note.createdAt.toISOString(),
|
published: note.createdAt.toISOString(),
|
||||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||||
cc: `${attributedTo}/followers`,
|
cc,
|
||||||
inReplyTo,
|
inReplyTo,
|
||||||
attachment: (await promisedFiles).map(renderDocument),
|
attachment: (await promisedFiles).map(renderDocument),
|
||||||
tag: (note.tags || []).map(renderHashtag)
|
tag
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as debug from 'debug';
|
|||||||
|
|
||||||
import User, { IUser } from '../../../models/user';
|
import User, { IUser } from '../../../models/user';
|
||||||
import Mute from '../../../models/mute';
|
import Mute from '../../../models/mute';
|
||||||
import { pack as packNote } from '../../../models/note';
|
import { pack as packNote, pack } from '../../../models/note';
|
||||||
import readNotification from '../common/read-notification';
|
import readNotification from '../common/read-notification';
|
||||||
import call from '../call';
|
import call from '../call';
|
||||||
import { IApp } from '../../../models/app';
|
import { IApp } from '../../../models/app';
|
||||||
@@ -48,6 +48,14 @@ export default async function(
|
|||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
// Renoteなら再pack
|
||||||
|
if (x.type == 'note' && x.body.renoteId != null) {
|
||||||
|
x.body.renote = await pack(x.body.renoteId, user, {
|
||||||
|
detail: true
|
||||||
|
});
|
||||||
|
data = JSON.stringify(x);
|
||||||
|
}
|
||||||
|
|
||||||
connection.send(data);
|
connection.send(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
connection.send(data);
|
connection.send(data);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import * as redis from 'redis';
|
|||||||
|
|
||||||
import { IUser } from '../../../models/user';
|
import { IUser } from '../../../models/user';
|
||||||
import Mute from '../../../models/mute';
|
import Mute from '../../../models/mute';
|
||||||
|
import { pack } from '../../../models/note';
|
||||||
|
|
||||||
export default async function(
|
export default async function(
|
||||||
request: websocket.request,
|
request: websocket.request,
|
||||||
@@ -31,6 +32,13 @@ export default async function(
|
|||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
// Renoteなら再pack
|
||||||
|
if (note.renoteId != null) {
|
||||||
|
note.renote = await pack(note.renoteId, user, {
|
||||||
|
detail: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
connection.send(JSON.stringify({
|
connection.send(JSON.stringify({
|
||||||
type: 'note',
|
type: 'note',
|
||||||
body: note
|
body: note
|
||||||
|
|||||||
+56
-49
@@ -204,6 +204,62 @@ export default async (user: IUser, data: {
|
|||||||
return packAp(content);
|
return packAp(content);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//#region メンション
|
||||||
|
if (data.text) {
|
||||||
|
// TODO: Drop dupulicates
|
||||||
|
const mentionTokens = tokens
|
||||||
|
.filter(t => t.type == 'mention');
|
||||||
|
|
||||||
|
// TODO: Drop dupulicates
|
||||||
|
const mentionedUsers = (await Promise.all(mentionTokens.map(async m => {
|
||||||
|
try {
|
||||||
|
return await resolveUser(m.username, m.host);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}))).filter(x => x != null);
|
||||||
|
|
||||||
|
// Append mentions data
|
||||||
|
if (mentionedUsers.length > 0) {
|
||||||
|
const set = {
|
||||||
|
mentions: mentionedUsers.map(u => u._id),
|
||||||
|
mentionedRemoteUsers: mentionedUsers.filter(u => isRemoteUser(u)).map(u => ({
|
||||||
|
uri: (u as IRemoteUser).uri,
|
||||||
|
username: u.username,
|
||||||
|
host: u.host
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
Note.update({ _id: note._id }, {
|
||||||
|
$set: set
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(note, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
mentionedUsers.filter(u => isLocalUser(u)).forEach(async u => {
|
||||||
|
event(u, 'mention', noteObj);
|
||||||
|
|
||||||
|
// 既に言及されたユーザーに対する返信や引用renoteの場合も無視
|
||||||
|
if (data.reply && data.reply.userId.equals(u._id)) return;
|
||||||
|
if (data.renote && data.renote.userId.equals(u._id)) return;
|
||||||
|
|
||||||
|
// Create notification
|
||||||
|
notify(u._id, user._id, 'mention', {
|
||||||
|
noteId: note._id
|
||||||
|
});
|
||||||
|
|
||||||
|
nm.push(u._id, 'mention');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isLocalUser(user)) {
|
||||||
|
mentionedUsers.filter(u => isRemoteUser(u)).forEach(async u => {
|
||||||
|
deliver(user, await render(), (u as IRemoteUser).inbox);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
if (isLocalUser(user)) {
|
if (isLocalUser(user)) {
|
||||||
if (note.visibility == 'private' || note.visibility == 'followers' || note.visibility == 'specified') {
|
if (note.visibility == 'private' || note.visibility == 'followers' || note.visibility == 'specified') {
|
||||||
@@ -287,55 +343,6 @@ export default async (user: IUser, data: {
|
|||||||
}
|
}
|
||||||
//#endergion
|
//#endergion
|
||||||
|
|
||||||
//#region メンション
|
|
||||||
if (data.text) {
|
|
||||||
// TODO: Drop dupulicates
|
|
||||||
const mentions = tokens
|
|
||||||
.filter(t => t.type == 'mention');
|
|
||||||
|
|
||||||
let mentionedUsers = await Promise.all(mentions.map(async m => {
|
|
||||||
try {
|
|
||||||
return await resolveUser(m.username, m.host);
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// TODO: Drop dupulicates
|
|
||||||
mentionedUsers = mentionedUsers.filter(x => x != null);
|
|
||||||
|
|
||||||
mentionedUsers.filter(u => isLocalUser(u)).forEach(async u => {
|
|
||||||
event(u, 'mention', noteObj);
|
|
||||||
|
|
||||||
// 既に言及されたユーザーに対する返信や引用renoteの場合も無視
|
|
||||||
if (data.reply && data.reply.userId.equals(u._id)) return;
|
|
||||||
if (data.renote && data.renote.userId.equals(u._id)) return;
|
|
||||||
|
|
||||||
// Create notification
|
|
||||||
notify(u._id, user._id, 'mention', {
|
|
||||||
noteId: note._id
|
|
||||||
});
|
|
||||||
|
|
||||||
nm.push(u._id, 'mention');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isLocalUser(user)) {
|
|
||||||
mentionedUsers.filter(u => isRemoteUser(u)).forEach(async u => {
|
|
||||||
deliver(user, await render(), (u as IRemoteUser).inbox);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append mentions data
|
|
||||||
if (mentionedUsers.length > 0) {
|
|
||||||
Note.update({ _id: note._id }, {
|
|
||||||
$set: {
|
|
||||||
mentions: mentionedUsers.map(u => u._id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
// If has in reply to note
|
// If has in reply to note
|
||||||
if (data.reply) {
|
if (data.reply) {
|
||||||
// Increment replies count
|
// Increment replies count
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default async function(user: IUser, note: INote) {
|
|||||||
$set: {
|
$set: {
|
||||||
deletedAt: new Date(),
|
deletedAt: new Date(),
|
||||||
text: null,
|
text: null,
|
||||||
|
tags: [],
|
||||||
mediaIds: [],
|
mediaIds: [],
|
||||||
poll: null
|
poll: null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user