Compare commits

..

13 Commits

Author SHA1 Message Date
c3c885de47 8.35.0 2018-09-13 02:52:29 +09:00
8a8c079b2f Improve following/followers view
Closes #2235
2018-09-13 02:08:17 +09:00
b4a30e2a25 Refactor: remove needless await 2018-09-13 02:07:03 +09:00
f19f92c538 Clean up: Remove unused import 2018-09-13 01:50:36 +09:00
e1a946ab45 Fix bug 2018-09-13 01:50:21 +09:00
aa1817737e Fix glitch 2018-09-13 01:06:18 +09:00
25ec5a24ab Add toLowerCase 2018-09-12 06:33:02 +09:00
2118fadc48 Add toUpperCase function (#2697) 2018-09-12 03:51:33 +09:00
ca2230f690 Refactor 2018-09-12 03:32:47 +09:00
0ed197d4d9 Use unique (#2695) 2018-09-12 03:02:14 +09:00
046976dffc Resolve #2691 2018-09-12 02:48:19 +09:00
bb8139196e Fix wrong indentation 2018-09-12 01:48:30 +09:00
1fea2cdcbe Fix bug 2018-09-11 20:57:25 +09:00
24 changed files with 298 additions and 215 deletions

View File

@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "8.34.4",
"clientVersion": "1.0.9572",
"version": "8.35.0",
"clientVersion": "1.0.9589",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,

View File

@ -1,15 +1,15 @@
<template>
<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
<span class="inner" :style="style"></span>
<span class="mk-avatar" :style="style" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
<span class="inner" :style="icon"></span>
</span>
<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
<span class="inner" :style="style"></span>
<span class="mk-avatar" :style="style" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
<span class="inner" :style="icon"></span>
</span>
<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
<span class="inner" :style="style"></span>
<router-link class="mk-avatar" :style="style" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
<span class="inner" :style="icon"></span>
</router-link>
<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
<span class="inner" :style="style"></span>
<router-link class="mk-avatar" :style="style" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
<span class="inner" :style="icon"></span>
</router-link>
</template>
@ -42,6 +42,11 @@ export default Vue.extend({
return this.user.isCat && this.$store.state.settings.circleIcons;
},
style(): any {
return {
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
};
},
icon(): any {
return {
backgroundColor: this.lightmode
? `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`

View File

@ -1,4 +1,4 @@
import Vue from 'vue';
import Vue, { VNode } from 'vue';
import * as emojilib from 'emojilib';
import { length } from 'stringz';
import parse from '../../../../../mfm/parse';
@ -6,10 +6,7 @@ import getAcct from '../../../../../misc/acct/render';
import { url } from '../../../config';
import MkUrl from './url.vue';
import MkGoogle from './google.vue';
const flatten = list => list.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
);
import { concat } from '../../../../../prelude/array';
export default Vue.component('misskey-flavored-markdown', {
props: {
@ -32,20 +29,20 @@ export default Vue.component('misskey-flavored-markdown', {
},
render(createElement) {
let ast;
let ast: any[];
if (this.ast == null) {
// Parse text to ast
ast = parse(this.text);
} else {
ast = this.ast;
ast = this.ast as any[];
}
let bigCount = 0;
let motionCount = 0;
// Parse ast to DOM
const els = flatten(ast.map(token => {
const els = concat(ast.map((token): VNode[] => {
switch (token.type) {
case 'text': {
const text = token.content.replace(/(\r\n|\n|\r)/g, '\n');
@ -56,12 +53,12 @@ export default Vue.component('misskey-flavored-markdown', {
x[x.length - 1].pop();
return x;
} else {
return createElement('span', text.replace(/\n/g, ' '));
return [createElement('span', text.replace(/\n/g, ' '))];
}
}
case 'bold': {
return createElement('b', token.bold);
return [createElement('b', token.bold)];
}
case 'big': {
@ -95,23 +92,23 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'url': {
return createElement(MkUrl, {
return [createElement(MkUrl, {
props: {
url: token.content,
target: '_blank'
}
});
})];
}
case 'link': {
return createElement('a', {
return [createElement('a', {
attrs: {
class: 'link',
href: token.url,
target: '_blank',
title: token.url
}
}, token.title);
}, token.title)];
}
case 'mention': {
@ -129,16 +126,16 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'hashtag': {
return createElement('a', {
return [createElement('a', {
attrs: {
href: `${url}/tags/${encodeURIComponent(token.hashtag)}`,
target: '_blank'
}
}, token.content);
}, token.content)];
}
case 'code': {
return createElement('pre', {
return [createElement('pre', {
class: 'code'
}, [
createElement('code', {
@ -146,15 +143,15 @@ export default Vue.component('misskey-flavored-markdown', {
innerHTML: token.html
}
})
]);
])];
}
case 'inline-code': {
return createElement('code', {
return [createElement('code', {
domProps: {
innerHTML: token.html
}
});
})];
}
case 'quote': {
@ -164,43 +161,45 @@ export default Vue.component('misskey-flavored-markdown', {
const x = text2.split('\n')
.map(t => [createElement('span', t), createElement('br')]);
x[x.length - 1].pop();
return createElement('div', {
return [createElement('div', {
attrs: {
class: 'quote'
}
}, x);
}, x)];
} else {
return createElement('span', {
return [createElement('span', {
attrs: {
class: 'quote'
}
}, text2.replace(/\n/g, ' '));
}, text2.replace(/\n/g, ' '))];
}
}
case 'title': {
return createElement('div', {
return [createElement('div', {
attrs: {
class: 'title'
}
}, token.title);
}, token.title)];
}
case 'emoji': {
const emoji = emojilib.lib[token.emoji];
return createElement('span', emoji ? emoji.char : token.content);
return [createElement('span', emoji ? emoji.char : token.content)];
}
case 'search': {
return createElement(MkGoogle, {
return [createElement(MkGoogle, {
props: {
q: token.query
}
});
})];
}
default: {
console.log('unknown ast type:', token.type);
return [];
}
}
}));

View File

@ -32,7 +32,6 @@
<script lang="ts">
import Vue from 'vue';
import parseAcct from '../../../../../misc/acct/parse';
import getUserName from '../../../../../misc/get-user-name';
import Progress from '../../../common/scripts/loading';
export default Vue.extend({

View File

@ -62,7 +62,7 @@ import getFace from '../../../common/scripts/get-face';
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
import parse from '../../../../../mfm/parse';
import { host } from '../../../config';
import { erase } from '../../../../../prelude/array';
import { erase, unique } from '../../../../../prelude/array';
import { length } from 'stringz';
import parseAcct from '../../../../../misc/acct/parse';
@ -397,7 +397,7 @@ export default Vue.extend({
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.type == 'hashtag').map(x => x.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(hashtags.concat(history).reduce((a, c) => a.includes(c) ? a : [...a, c], [])));
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
},

View File

@ -2,8 +2,8 @@
<div class="mk-timeline">
<header>
<span :data-active="src == 'home'" @click="src = 'home'">%fa:home% %i18n:@home%</span>
<span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% %i18n:@local%</span>
<span :data-active="src == 'hybrid'" @click="src = 'hybrid'">%fa:share-alt% %i18n:@hybrid%</span>
<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline">%fa:R comments% %i18n:@local%</span>
<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline">%fa:share-alt% %i18n:@hybrid%</span>
<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span>
<span :data-active="src == 'list'" @click="src = 'list'" v-if="list">%fa:list% {{ list.title }}</span>
<button @click="chooseList" title="%i18n:@list%">%fa:list%</button>
@ -29,7 +29,8 @@ export default Vue.extend({
data() {
return {
src: 'home',
list: null
list: null,
enableLocalTimeline: false
};
},
@ -44,6 +45,10 @@ export default Vue.extend({
},
created() {
(this as any).os.getMeta().then(meta => {
this.enableLocalTimeline = !meta.disableLocalTimeline;
});
if (this.$store.state.device.tl) {
this.src = this.$store.state.device.tl.src;
if (this.src == 'list') {

View File

@ -1,17 +1,16 @@
<template>
<div class="root item">
<mk-avatar class="avatar" :user="user"/>
<div class="main">
<header>
<router-link class="name" :to="user | userPage" v-user-preview="user.id">{{ user | userName }}</router-link>
<span class="username">@{{ user | acct }}</span>
</header>
<div class="body">
<p class="followed" v-if="user.isFollowed">%i18n:@followed%</p>
<div class="description">{{ user.description }}</div>
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
<div class="banner" :style="bannerStyle"></div>
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<div class="body">
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
<span class="username">@{{ user | acct }}</span>
<div class="description">
<misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/>
</div>
<p class="followed" v-if="user.isFollowed">%i18n:@followed%</p>
<mk-follow-button :user="user" :size="'big'"/>
</div>
<mk-follow-button :user="user"/>
</div>
</template>
@ -19,76 +18,69 @@
import Vue from 'vue';
export default Vue.extend({
props: ['user']
props: ['user'],
computed: {
bannerStyle(): any {
if (this.user.bannerUrl == null) return {};
return {
backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null,
backgroundImage: `url(${ this.user.bannerUrl })`
};
}
},
});
</script>
<style lang="stylus" scoped>
.root.item
padding 16px
font-size 16px
.zvdbznxvfixtmujpsigoccczftvpiwqh
$bg = #fff
&:after
content ""
display block
clear both
margin 16px auto
max-width calc(100% - 32px)
font-size 16px
text-align center
background $bg
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
> .banner
height 100px
background-color #f9f4f4
background-position center
background-size cover
> .avatar
display block
float left
margin 0 16px 0 0
width 58px
height 58px
border-radius 8px
margin -40px auto 0 auto
width 80px
height 80px
border-radius 100%
border solid 4px $bg
> .main
float left
width calc(100% - 74px)
> .body
padding 4px 32px 32px 32px
> header
margin-bottom 2px
@media (max-width 400px)
padding 4px 16px 16px 16px
> .name
display inline
margin 0
padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
> .name
font-size 20px
font-weight bold
&:hover
text-decoration underline
> .username
display block
opacity 0.7
> .username
text-align left
margin 0 0 0 8px
color #ccc
> .description
margin 16px 0
> .body
> .followed
display inline-block
margin 0 0 4px 0
padding 2px 8px
vertical-align top
font-size 10px
color #71afc7
background #eefaff
border-radius 4px
> .description
cursor default
display block
margin 0
padding 0
overflow-wrap break-word
font-size 1.1em
color #717171
> .mk-follow-button
position absolute
top 16px
right 16px
> .followed
margin 0 0 16px 0
padding 0
line-height 24px
font-size 0.8em
color #71afc7
background #eefaff
border-radius 4px
</style>

View File

@ -33,7 +33,7 @@ export default Vue.extend({
props: ['fetch', 'count', 'youKnowCount'],
data() {
return {
limit: 30,
limit: 20,
mode: 'all',
fetching: true,
moreFetching: false,
@ -73,10 +73,14 @@ export default Vue.extend({
.mk-users-list
height 100%
background #fff
overflow auto
background #eee
> nav
z-index 1
z-index 10
position sticky
top 0
background #fff
box-shadow 0 1px 0 rgba(#000, 0.1)
> div
@ -114,16 +118,14 @@ export default Vue.extend({
background #eee
border-radius 20px
> .users
height calc(100% - 54px)
overflow auto
> button
display block
width calc(100% - 32px)
margin 16px
padding 16px
> *
border-bottom solid 1px rgba(#000, 0.05)
> *
max-width 600px
margin 0 auto
&:hover
background rgba(#000, 0.1)
> .no
margin 0

View File

@ -1,22 +1,34 @@
<template>
<div class="obdskegsannmntldydackcpzezagxqfy mk-admin-card">
<header>%i18n:@dashboard%</header>
<div v-if="stats" class="stats">
<div><b>%fa:user% {{ stats.originalUsersCount | number }}</b><span>%i18n:@original-users%</span></div>
<div><span>%fa:user% {{ stats.usersCount | number }}</span><span>%i18n:@all-users%</span></div>
<div><b>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div>
<div><span>%fa:pencil-alt% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div>
</div>
<div class="cpu-memory">
<x-cpu-memory :connection="connection"/>
</div>
<div>
<label>
<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
<span>disableRegistration</span>
</label>
<button class="ui" @click="invite">%i18n:@invite%</button>
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
<div class="form">
<div>
<label>
<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
<span>%i18n:@disableRegistration%</span>
</label>
<button class="ui" @click="invite">%i18n:@invite%</button>
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
</div>
<div>
<label>
<input type="checkbox" v-model="disableLocalTimeline" @change="updateMeta">
<span>%i18n:@disableLocalTimeline%</span>
</label>
</div>
</div>
</div>
</template>
@ -33,6 +45,7 @@ export default Vue.extend({
return {
stats: null,
disableRegistration: false,
disableLocalTimeline: false,
inviteCode: null,
connection: null,
connectionId: null
@ -44,6 +57,7 @@ export default Vue.extend({
(this as any).os.getMeta().then(meta => {
this.disableRegistration = meta.disableRegistration;
this.disableLocalTimeline = meta.disableLocalTimeline;
});
(this as any).api('stats').then(stats => {
@ -61,7 +75,8 @@ export default Vue.extend({
},
updateMeta() {
(this as any).api('admin/update-meta', {
disableRegistration: this.disableRegistration
disableRegistration: this.disableRegistration,
disableLocalTimeline: this.disableLocalTimeline
});
}
}
@ -97,4 +112,8 @@ export default Vue.extend({
border solid 1px #eee
border-radius: 8px
> .form
> div
border-bottom solid 1px #eee
</style>

View File

@ -308,7 +308,7 @@ export default Vue.extend({
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.type == 'hashtag').map(x => x.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(hashtags.concat(history).reduce((a, c) => a.includes(c) ? a : [...a, c], [])));
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
},

View File

@ -24,8 +24,8 @@
<div class="body">
<div>
<span :data-active="src == 'home'" @click="src = 'home'">%fa:home% %i18n:@home%</span>
<span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% %i18n:@local%</span>
<span :data-active="src == 'hybrid'" @click="src = 'hybrid'">%fa:share-alt% %i18n:@hybrid%</span>
<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline">%fa:R comments% %i18n:@local%</span>
<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline">%fa:share-alt% %i18n:@hybrid%</span>
<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span>
<template v-if="lists">
<span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id">%fa:list% {{ l.title }}</span>
@ -60,7 +60,8 @@ export default Vue.extend({
src: 'home',
list: null,
lists: null,
showNav: false
showNav: false,
enableLocalTimeline: false
};
},
@ -85,6 +86,10 @@ export default Vue.extend({
},
created() {
(this as any).os.getMeta().then(meta => {
this.enableLocalTimeline = !meta.disableLocalTimeline;
});
if (this.$store.state.device.tl) {
this.src = this.$store.state.device.tl.src;
if (this.src == 'list') {

View File

@ -1,4 +1,4 @@
import { capitalize } from "../../../prelude/string";
import { capitalize, toUpperCase } from "../../../prelude/string";
function escape(text: string) {
return text
@ -92,7 +92,7 @@ const _keywords = [
const keywords = _keywords
.concat(_keywords.map(capitalize))
.concat(_keywords.map(k => k.toUpperCase()))
.concat(_keywords.map(toUpperCase))
.sort((a, b) => b.length - a.length);
const symbols = [

View File

@ -12,5 +12,6 @@ export type IMeta = {
originalUsersCount: number;
};
disableRegistration?: boolean;
disableLocalTimeline?: boolean;
hidedTags?: string[];
};

View File

@ -1,3 +1,11 @@
export function capitalize(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
return toUpperCase(s.charAt(0)) + toLowerCase(s.slice(1));
}
export function toUpperCase(s: string): string {
return s.toUpperCase();
}
export function toLowerCase(s: string): string {
return s.toLowerCase();
}

View File

@ -23,6 +23,12 @@ export const meta = {
}
}),
disableLocalTimeline: $.bool.optional.nullable.note({
desc: {
'ja-JP': 'ローカルタイムライン(とソーシャルタイムライン)を無効にするか否か'
}
}),
hidedTags: $.arr($.str).optional.nullable.note({
desc: {
'ja-JP': '統計などで無視するハッシュタグ'
@ -45,6 +51,10 @@ export default (params: any) => new Promise(async (res, rej) => {
set.disableRegistration = ps.disableRegistration;
}
if (typeof ps.disableLocalTimeline === 'boolean') {
set.disableLocalTimeline = ps.disableLocalTimeline;
}
if (Array.isArray(ps.hidedTags)) {
set.hidedTags = ps.hidedTags;
}

View File

@ -57,7 +57,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}
// Create following
create(follower, followee);
await create(follower, followee);
// Send response
res(await pack(followee._id, user));

View File

@ -57,7 +57,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}
// Delete following
deleteFollowing(follower, followee);
await deleteFollowing(follower, followee);
// Send response
res(await pack(followee._id, user));

View File

@ -34,6 +34,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
},
broadcasts: meta.broadcasts,
disableRegistration: meta.disableRegistration,
disableLocalTimeline: meta.disableLocalTimeline,
driveCapacityPerLocalUserMb: config.localDriveCapacityMb,
recaptchaSitekey: config.recaptcha ? config.recaptcha.site_key : null,
swPublickey: config.sw ? config.sw.public_key : null,

View File

@ -73,8 +73,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
// Serialize
const users = await Promise.all(following.map(async f =>
await pack(f.followerId, me, { detail: true })));
const users = await Promise.all(following.map(f => pack(f.followerId, me, { detail: true })));
// Response
res({

View File

@ -73,8 +73,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}
// Serialize
const users = await Promise.all(following.map(async f =>
await pack(f.followeeId, me, { detail: true })));
const users = await Promise.all(following.map(f => pack(f.followeeId, me, { detail: true })));
// Response
res({

View File

@ -36,6 +36,13 @@ export default async function(
// Subscribe Home stream channel
subscriber.on(`user-stream:${user._id}`, async x => {
// Renoteなら再pack
if (x.type == 'note' && x.body.renoteId != null) {
x.body.renote = await pack(x.body.renoteId, user, {
detail: true
});
}
//#region 流れてきたメッセージがミュートしているユーザーが関わるものだったら無視する
if (x.type == 'note') {
if (mutedUserIds.includes(x.body.userId)) {
@ -54,13 +61,6 @@ export default async function(
}
//#endregion
// Renoteなら再pack
if (x.type == 'note' && x.body.renoteId != null) {
x.body.renote = await pack(x.body.renoteId, user, {
detail: true
});
}
connection.send(JSON.stringify(x));
});

View File

@ -19,6 +19,13 @@ export default async function(
subscriber.on(`hybrid-timeline:${user._id}`, onEvent);
async function onEvent(note: any) {
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await pack(note.renoteId, user, {
detail: true
});
}
//#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (mutedUserIds.indexOf(note.userId) != -1) {
return;
@ -31,13 +38,6 @@ export default async function(
}
//#endregion
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await pack(note.renoteId, user, {
detail: true
});
}
connection.send(JSON.stringify({
type: 'note',
body: note

View File

@ -16,6 +16,13 @@ export default async function(
// Subscribe stream
subscriber.on('local-timeline', async note => {
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await pack(note.renoteId, user, {
detail: true
});
}
//#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (mutedUserIds.indexOf(note.userId) != -1) {
return;
@ -28,13 +35,6 @@ export default async function(
}
//#endregion
// Renoteなら再pack
if (note.renoteId != null) {
note.renote = await pack(note.renoteId, user, {
detail: true
});
}
connection.send(JSON.stringify({
type: 'note',
body: note

View File

@ -1,58 +1,97 @@
import * as mongo from 'mongodb';
import Xev from 'xev';
const ev = new Xev();
import Meta, { IMeta } from './models/meta';
type ID = string | mongo.ObjectID;
function publish(channel: string, type: string, value?: any): void {
const message = type == null ? value : value == null ?
{ type: type } :
{ type: type, body: value };
class Publisher {
private ev: Xev;
private meta: IMeta;
ev.emit(channel, message);
constructor() {
this.ev = new Xev();
setInterval(async () => {
this.meta = await Meta.findOne({});
}, 5000);
}
public getMeta = async () => {
if (this.meta != null) return this.meta;
this.meta = await Meta.findOne({});
return this.meta;
}
private publish = (channel: string, type: string, value?: any): void => {
const message = type == null ? value : value == null ?
{ type: type } :
{ type: type, body: value };
this.ev.emit(channel, message);
}
public publishUserStream = (userId: ID, type: string, value?: any): void => {
this.publish(`user-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
public publishDriveStream = (userId: ID, type: string, value?: any): void => {
this.publish(`drive-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
public publishNoteStream = (noteId: ID, type: string): void => {
this.publish(`note-stream:${noteId}`, null, noteId);
}
public publishUserListStream = (listId: ID, type: string, value?: any): void => {
this.publish(`user-list-stream:${listId}`, type, typeof value === 'undefined' ? null : value);
}
public publishMessagingStream = (userId: ID, otherpartyId: ID, type: string, value?: any): void => {
this.publish(`messaging-stream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value);
}
public publishMessagingIndexStream = (userId: ID, type: string, value?: any): void => {
this.publish(`messaging-index-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
public publishReversiStream = (userId: ID, type: string, value?: any): void => {
this.publish(`reversi-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
public publishReversiGameStream = (gameId: ID, type: string, value?: any): void => {
this.publish(`reversi-game-stream:${gameId}`, type, typeof value === 'undefined' ? null : value);
}
public publishLocalTimelineStream = async (note: any): Promise<void> => {
const meta = await this.getMeta();
if (meta.disableLocalTimeline) return;
this.publish('local-timeline', null, note);
}
public publishHybridTimelineStream = async (userId: ID, note: any): Promise<void> => {
const meta = await this.getMeta();
if (meta.disableLocalTimeline) return;
this.publish(userId ? `hybrid-timeline:${userId}` : 'hybrid-timeline', null, note);
}
public publishGlobalTimelineStream = (note: any): void => {
this.publish('global-timeline', null, note);
}
}
export function publishUserStream(userId: ID, type: string, value?: any): void {
publish(`user-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
const publisher = new Publisher();
export function publishDriveStream(userId: ID, type: string, value?: any): void {
publish(`drive-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
export default publisher;
export function publishNoteStream(noteId: ID, type: string): void {
publish(`note-stream:${noteId}`, null, noteId);
}
export function publishUserListStream(listId: ID, type: string, value?: any): void {
publish(`user-list-stream:${listId}`, type, typeof value === 'undefined' ? null : value);
}
export function publishMessagingStream(userId: ID, otherpartyId: ID, type: string, value?: any): void {
publish(`messaging-stream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value);
}
export function publishMessagingIndexStream(userId: ID, type: string, value?: any): void {
publish(`messaging-index-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
export function publishReversiStream(userId: ID, type: string, value?: any): void {
publish(`reversi-stream:${userId}`, type, typeof value === 'undefined' ? null : value);
}
export function publishReversiGameStream(gameId: ID, type: string, value?: any): void {
publish(`reversi-game-stream:${gameId}`, type, typeof value === 'undefined' ? null : value);
}
export function publishLocalTimelineStream(note: any): void {
publish('local-timeline', null, note);
}
export function publishHybridTimelineStream(userId: ID, note: any): void {
publish(userId ? `hybrid-timeline:${userId}` : 'hybrid-timeline', null, note);
}
export function publishGlobalTimelineStream(note: any): void {
publish('global-timeline', null, note);
}
export const publishUserStream = publisher.publishUserStream;
export const publishDriveStream = publisher.publishDriveStream;
export const publishNoteStream = publisher.publishNoteStream;
export const publishUserListStream = publisher.publishUserListStream;
export const publishMessagingStream = publisher.publishMessagingStream;
export const publishMessagingIndexStream = publisher.publishMessagingIndexStream;
export const publishReversiStream = publisher.publishReversiStream;
export const publishReversiGameStream = publisher.publishReversiGameStream;
export const publishLocalTimelineStream = publisher.publishLocalTimelineStream;
export const publishHybridTimelineStream = publisher.publishHybridTimelineStream;
export const publishGlobalTimelineStream = publisher.publishGlobalTimelineStream;