Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
c3c885de47 | |||
8a8c079b2f | |||
b4a30e2a25 | |||
f19f92c538 | |||
e1a946ab45 | |||
aa1817737e | |||
25ec5a24ab | |||
2118fadc48 | |||
ca2230f690 | |||
0ed197d4d9 | |||
046976dffc | |||
bb8139196e | |||
1fea2cdcbe |
@ -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,
|
||||
|
@ -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(',')})`
|
||||
|
@ -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 [];
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -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({
|
||||
|
@ -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))));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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') {
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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))));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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') {
|
||||
|
@ -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 = [
|
||||
|
@ -12,5 +12,6 @@ export type IMeta = {
|
||||
originalUsersCount: number;
|
||||
};
|
||||
disableRegistration?: boolean;
|
||||
disableLocalTimeline?: boolean;
|
||||
hidedTags?: string[];
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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({
|
||||
|
@ -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({
|
||||
|
@ -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));
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
135
src/stream.ts
135
src/stream.ts
@ -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;
|
||||
|
Reference in New Issue
Block a user