Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e3faf64061 | ||
![]() |
ed83993e15 | ||
![]() |
0f8847bb74 | ||
![]() |
a72cfa7535 | ||
![]() |
514b74a19d | ||
![]() |
a2c124306f | ||
![]() |
273f67e268 | ||
![]() |
2870a7e463 | ||
![]() |
65e5cfa68e | ||
![]() |
10e59957d1 | ||
![]() |
4f74373df3 | ||
![]() |
2d414bbf86 | ||
![]() |
a199969b81 | ||
![]() |
3aef5e6748 | ||
![]() |
2b536a7443 |
@@ -938,6 +938,7 @@ desktop/views/components/settings.profile.vue:
|
|||||||
save: "保存"
|
save: "保存"
|
||||||
locked-account: "アカウントの保護"
|
locked-account: "アカウントの保護"
|
||||||
is-locked: "フォローを承認制にする"
|
is-locked: "フォローを承認制にする"
|
||||||
|
careful-bot: "Botからのフォローだけ承認制にする"
|
||||||
other: "その他"
|
other: "その他"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
is-cat: "このアカウントはCatです"
|
is-cat: "このアカウントはCatです"
|
||||||
@@ -1420,6 +1421,7 @@ mobile/views/pages/settings/settings.profile.vue:
|
|||||||
banner: "バナー"
|
banner: "バナー"
|
||||||
is-cat: "このアカウントはCatです"
|
is-cat: "このアカウントはCatです"
|
||||||
is-locked: "フォローを承認制にする"
|
is-locked: "フォローを承認制にする"
|
||||||
|
careful-bot: "Botからのフォローだけ承認制にする"
|
||||||
advanced: "その他"
|
advanced: "その他"
|
||||||
privacy: "プライバシー"
|
privacy: "プライバシー"
|
||||||
save: "保存"
|
save: "保存"
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.12.1",
|
"version": "10.14.0",
|
||||||
"clientVersion": "1.0.10509",
|
"clientVersion": "1.0.10524",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
"qrcode": "1.3.0",
|
"qrcode": "1.3.0",
|
||||||
"ratelimiter": "3.2.0",
|
"ratelimiter": "3.2.0",
|
||||||
"recaptcha-promise": "0.1.3",
|
"recaptcha-promise": "0.1.3",
|
||||||
"reconnecting-websocket": "4.1.7",
|
"reconnecting-websocket": "4.1.8",
|
||||||
"redis": "2.8.0",
|
"redis": "2.8.0",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"request-promise-native": "1.0.5",
|
"request-promise-native": "1.0.5",
|
||||||
@@ -213,6 +213,7 @@
|
|||||||
"vue": "2.5.17",
|
"vue": "2.5.17",
|
||||||
"vue-chartjs": "3.4.0",
|
"vue-chartjs": "3.4.0",
|
||||||
"vue-color": "2.7.0",
|
"vue-color": "2.7.0",
|
||||||
|
"vue-content-loading": "1.5.3",
|
||||||
"vue-cropperjs": "2.2.2",
|
"vue-cropperjs": "2.2.2",
|
||||||
"vue-js-modal": "1.3.26",
|
"vue-js-modal": "1.3.26",
|
||||||
"vue-json-tree-view": "2.1.4",
|
"vue-json-tree-view": "2.1.4",
|
||||||
|
@@ -142,7 +142,7 @@
|
|||||||
localStorage.setItem('shouldFlush', 'false');
|
localStorage.setItem('shouldFlush', 'false');
|
||||||
|
|
||||||
// Random
|
// Random
|
||||||
localStorage.setItem('salt', Math.random().toString());
|
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
||||||
|
|
||||||
// Clear cache (service worker)
|
// Clear cache (service worker)
|
||||||
try {
|
try {
|
||||||
|
@@ -9,7 +9,7 @@ import MiOS from '../../mios';
|
|||||||
*/
|
*/
|
||||||
export default class Stream extends EventEmitter {
|
export default class Stream extends EventEmitter {
|
||||||
private stream: ReconnectingWebsocket;
|
private stream: ReconnectingWebsocket;
|
||||||
private state: string;
|
public state: string;
|
||||||
private sharedConnectionPools: Pool[] = [];
|
private sharedConnectionPools: Pool[] = [];
|
||||||
private sharedConnections: SharedConnection[] = [];
|
private sharedConnections: SharedConnection[] = [];
|
||||||
private nonSharedConnections: NonSharedConnection[] = [];
|
private nonSharedConnections: NonSharedConnection[] = [];
|
||||||
@@ -156,7 +156,7 @@ class Pool {
|
|||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
|
|
||||||
this.id = Math.random().toString();
|
this.id = Math.random().toString().substr(2, 8);
|
||||||
|
|
||||||
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ class NonSharedConnection extends Connection {
|
|||||||
super(stream, channel);
|
super(stream, channel);
|
||||||
|
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.id = Math.random().toString();
|
this.id = Math.random().toString().substr(2, 8);
|
||||||
|
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import noteSkeleton from './note-skeleton.vue';
|
||||||
import theme from './theme.vue';
|
import theme from './theme.vue';
|
||||||
import instance from './instance.vue';
|
import instance from './instance.vue';
|
||||||
import cwButton from './cw-button.vue';
|
import cwButton from './cw-button.vue';
|
||||||
@@ -44,6 +45,7 @@ import uiSelect from './ui/select.vue';
|
|||||||
import formButton from './ui/form/button.vue';
|
import formButton from './ui/form/button.vue';
|
||||||
import formRadio from './ui/form/radio.vue';
|
import formRadio from './ui/form/radio.vue';
|
||||||
|
|
||||||
|
Vue.component('mk-note-skeleton', noteSkeleton);
|
||||||
Vue.component('mk-theme', theme);
|
Vue.component('mk-theme', theme);
|
||||||
Vue.component('mk-instance', instance);
|
Vue.component('mk-instance', instance);
|
||||||
Vue.component('mk-cw-button', cwButton);
|
Vue.component('mk-cw-button', cwButton);
|
||||||
|
52
src/client/app/common/views/components/note-skeleton.vue
Normal file
52
src/client/app/common/views/components/note-skeleton.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<vue-content-loading v-if="width" :width="width" :height="100" :primary="primary" :secondary="secondary">
|
||||||
|
<circle cx="30" cy="30" r="30" />
|
||||||
|
<rect x="75" y="13" rx="4" ry="4" :width="150 + r1" height="15" />
|
||||||
|
<rect x="75" y="39" rx="4" ry="4" :width="260 + r2" height="10" />
|
||||||
|
<rect x="75" y="59" rx="4" ry="4" :width="230 + r3" height="10" />
|
||||||
|
</vue-content-loading>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import VueContentLoading from 'vue-content-loading';
|
||||||
|
import * as tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
VueContentLoading,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
width: 0,
|
||||||
|
r1: (Math.random() * 100) - 50,
|
||||||
|
r2: (Math.random() * 100) - 50,
|
||||||
|
r3: (Math.random() * 100) - 50
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
text(): tinycolor.Instance {
|
||||||
|
const text = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text'));
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
|
||||||
|
primary(): string {
|
||||||
|
return '#' + this.text.clone().toHex();
|
||||||
|
},
|
||||||
|
|
||||||
|
secondary(): string {
|
||||||
|
return '#' + this.text.clone().darken(20).toHex();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
let width = this.$el.clientWidth;
|
||||||
|
if (width < 400) width = 400;
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
@@ -114,7 +114,7 @@ export default define({
|
|||||||
this.connection.on('stats', this.onStats);
|
this.connection.on('stats', this.onStats);
|
||||||
this.connection.on('statsLog', this.onStatsLog);
|
this.connection.on('statsLog', this.onStatsLog);
|
||||||
this.connection.send('requestLog',{
|
this.connection.send('requestLog',{
|
||||||
id: Math.random().toString()
|
id: Math.random().toString().substr(2, 8)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
@@ -92,7 +92,7 @@ export default Vue.extend({
|
|||||||
this.connection.on('stats', this.onStats);
|
this.connection.on('stats', this.onStats);
|
||||||
this.connection.on('statsLog', this.onStatsLog);
|
this.connection.on('statsLog', this.onStatsLog);
|
||||||
this.connection.send('requestLog', {
|
this.connection.send('requestLog', {
|
||||||
id: Math.random().toString()
|
id: Math.random().toString().substr(2, 8)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
@@ -1,37 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="mk-ellipsis-icon">
|
|
||||||
<div></div><div></div><div></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-ellipsis-icon
|
|
||||||
width 70px
|
|
||||||
margin 0 auto
|
|
||||||
text-align center
|
|
||||||
|
|
||||||
> div
|
|
||||||
display inline-block
|
|
||||||
width 18px
|
|
||||||
height 18px
|
|
||||||
background-color rgba(#000, 0.3)
|
|
||||||
border-radius 100%
|
|
||||||
animation bounce 1.4s infinite ease-in-out both
|
|
||||||
|
|
||||||
&:nth-child(1)
|
|
||||||
animation-delay 0s
|
|
||||||
|
|
||||||
&:nth-child(2)
|
|
||||||
margin 0 6px
|
|
||||||
animation-delay 0.16s
|
|
||||||
|
|
||||||
&:nth-child(3)
|
|
||||||
animation-delay 0.32s
|
|
||||||
|
|
||||||
@keyframes bounce
|
|
||||||
0%, 80%, 100%
|
|
||||||
transform scale(0)
|
|
||||||
40%
|
|
||||||
transform scale(1)
|
|
||||||
|
|
||||||
</style>
|
|
@@ -9,7 +9,6 @@ import subNoteContent from './sub-note-content.vue';
|
|||||||
import window from './window.vue';
|
import window from './window.vue';
|
||||||
import noteFormWindow from './post-form-window.vue';
|
import noteFormWindow from './post-form-window.vue';
|
||||||
import renoteFormWindow from './renote-form-window.vue';
|
import renoteFormWindow from './renote-form-window.vue';
|
||||||
import ellipsisIcon from './ellipsis-icon.vue';
|
|
||||||
import mediaImage from './media-image.vue';
|
import mediaImage from './media-image.vue';
|
||||||
import mediaImageDialog from './media-image-dialog.vue';
|
import mediaImageDialog from './media-image-dialog.vue';
|
||||||
import mediaVideo from './media-video.vue';
|
import mediaVideo from './media-video.vue';
|
||||||
@@ -39,7 +38,6 @@ Vue.component('mk-sub-note-content', subNoteContent);
|
|||||||
Vue.component('mk-window', window);
|
Vue.component('mk-window', window);
|
||||||
Vue.component('mk-post-form-window', noteFormWindow);
|
Vue.component('mk-post-form-window', noteFormWindow);
|
||||||
Vue.component('mk-renote-form-window', renoteFormWindow);
|
Vue.component('mk-renote-form-window', renoteFormWindow);
|
||||||
Vue.component('mk-ellipsis-icon', ellipsisIcon);
|
|
||||||
Vue.component('mk-media-image', mediaImage);
|
Vue.component('mk-media-image', mediaImage);
|
||||||
Vue.component('mk-media-image-dialog', mediaImageDialog);
|
Vue.component('mk-media-image-dialog', mediaImageDialog);
|
||||||
Vue.component('mk-media-video', mediaVideo);
|
Vue.component('mk-media-video', mediaVideo);
|
||||||
|
@@ -9,6 +9,12 @@
|
|||||||
<button @click="resolveInitPromise">%i18n:@retry%</button>
|
<button @click="resolveInitPromise">%i18n:@retry%</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="skeleton" v-if="fetching">
|
||||||
|
<template v-for="i in 10">
|
||||||
|
<mk-note-skeleton :key="i"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||||
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes">
|
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes">
|
||||||
<template v-for="(note, i) in _notes">
|
<template v-for="(note, i) in _notes">
|
||||||
@@ -226,6 +232,10 @@ export default Vue.extend({
|
|||||||
> *
|
> *
|
||||||
transition transform .3s ease, opacity .3s ease
|
transition transform .3s ease, opacity .3s ease
|
||||||
|
|
||||||
|
> .skeleton
|
||||||
|
padding 32px
|
||||||
|
opacity 0.3
|
||||||
|
|
||||||
> .notes
|
> .notes
|
||||||
> .date
|
> .date
|
||||||
display block
|
display block
|
||||||
|
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-notifications">
|
<div class="mk-notifications">
|
||||||
|
<div class="skeleton" v-if="fetching">
|
||||||
|
<template v-for="i in 10">
|
||||||
|
<mk-note-skeleton :key="i"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="notifications" v-if="notifications.length != 0">
|
<div class="notifications" v-if="notifications.length != 0">
|
||||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||||
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div">
|
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div">
|
||||||
@@ -102,7 +108,6 @@
|
|||||||
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
|
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
|
||||||
</button>
|
</button>
|
||||||
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
|
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
|
||||||
<p class="loading" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -202,6 +207,10 @@ export default Vue.extend({
|
|||||||
> *
|
> *
|
||||||
transition transform .3s ease, opacity .3s ease
|
transition transform .3s ease, opacity .3s ease
|
||||||
|
|
||||||
|
> .skeleton
|
||||||
|
padding 16px
|
||||||
|
opacity 0.3
|
||||||
|
|
||||||
> .notifications
|
> .notifications
|
||||||
> div
|
> div
|
||||||
> .notification
|
> .notification
|
||||||
@@ -319,13 +328,4 @@ export default Vue.extend({
|
|||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color #aaa
|
||||||
|
|
||||||
> .loading
|
|
||||||
margin 0
|
|
||||||
padding 16px
|
|
||||||
text-align center
|
|
||||||
color #aaa
|
|
||||||
|
|
||||||
> [data-fa]
|
|
||||||
margin-right 4px
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -21,12 +21,13 @@
|
|||||||
<ui-button primary @click="save">%i18n:@save%</ui-button>
|
<ui-button primary @click="save">%i18n:@save%</ui-button>
|
||||||
<section>
|
<section>
|
||||||
<h2>%i18n:@locked-account%</h2>
|
<h2>%i18n:@locked-account%</h2>
|
||||||
<ui-switch v-model="$store.state.i.isLocked" @change="onChangeIsLocked">%i18n:@is-locked%</ui-switch>
|
<ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch>
|
||||||
|
<ui-switch v-model="carefulBot" @change="save(false)">%i18n:@careful-bot%</ui-switch>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>%i18n:@other%</h2>
|
<h2>%i18n:@other%</h2>
|
||||||
<ui-switch v-model="$store.state.i.isBot" @change="onChangeIsBot">%i18n:@is-bot%</ui-switch>
|
<ui-switch v-model="isBot" @change="save(false)">%i18n:@is-bot%</ui-switch>
|
||||||
<ui-switch v-model="$store.state.i.isCat" @change="onChangeIsCat">%i18n:@is-cat%</ui-switch>
|
<ui-switch v-model="isCat" @change="save(false)">%i18n:@is-cat%</ui-switch>
|
||||||
<ui-switch v-model="alwaysMarkNsfw">%i18n:common.always-mark-nsfw%</ui-switch>
|
<ui-switch v-model="alwaysMarkNsfw">%i18n:common.always-mark-nsfw%</ui-switch>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,6 +43,10 @@ export default Vue.extend({
|
|||||||
location: null,
|
location: null,
|
||||||
description: null,
|
description: null,
|
||||||
birthday: null,
|
birthday: null,
|
||||||
|
isBot: false,
|
||||||
|
isCat: false,
|
||||||
|
isLocked: false,
|
||||||
|
carefulBot: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -55,34 +60,29 @@ export default Vue.extend({
|
|||||||
this.location = this.$store.state.i.profile.location;
|
this.location = this.$store.state.i.profile.location;
|
||||||
this.description = this.$store.state.i.description;
|
this.description = this.$store.state.i.description;
|
||||||
this.birthday = this.$store.state.i.profile.birthday;
|
this.birthday = this.$store.state.i.profile.birthday;
|
||||||
|
this.isCat = this.$store.state.i.isCat;
|
||||||
|
this.isBot = this.$store.state.i.isBot;
|
||||||
|
this.isLocked = this.$store.state.i.isLocked;
|
||||||
|
this.carefulBot = this.$store.state.i.carefulBot;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateAvatar() {
|
updateAvatar() {
|
||||||
(this as any).apis.updateAvatar();
|
(this as any).apis.updateAvatar();
|
||||||
},
|
},
|
||||||
save() {
|
save(notify) {
|
||||||
(this as any).api('i/update', {
|
(this as any).api('i/update', {
|
||||||
name: this.name || null,
|
name: this.name || null,
|
||||||
location: this.location || null,
|
location: this.location || null,
|
||||||
description: this.description || null,
|
description: this.description || null,
|
||||||
birthday: this.birthday || null
|
birthday: this.birthday || null,
|
||||||
|
isCat: this.isCat,
|
||||||
|
isBot: this.isBot,
|
||||||
|
isLocked: this.isLocked,
|
||||||
|
carefulBot: this.carefulBot
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
(this as any).apis.notify('%i18n:@profile-updated%');
|
if (notify) {
|
||||||
});
|
(this as any).apis.notify('%i18n:@profile-updated%');
|
||||||
},
|
}
|
||||||
onChangeIsLocked() {
|
|
||||||
(this as any).api('i/update', {
|
|
||||||
isLocked: this.$store.state.i.isLocked
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onChangeIsBot() {
|
|
||||||
(this as any).api('i/update', {
|
|
||||||
isBot: this.$store.state.i.isBot
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onChangeIsCat() {
|
|
||||||
(this as any).api('i/update', {
|
|
||||||
isCat: this.$store.state.i.isCat
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-timeline-core">
|
<div class="mk-timeline-core">
|
||||||
<mk-friends-maker v-if="src == 'home' && alone"/>
|
<mk-friends-maker v-if="src == 'home' && alone"/>
|
||||||
<div class="fetching" v-if="fetching">
|
|
||||||
<mk-ellipsis-icon/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mk-notes ref="timeline" :more="existMore ? more : null">
|
<mk-notes ref="timeline" :more="existMore ? more : null">
|
||||||
<p :class="$style.empty" slot="empty">
|
<p :class="$style.empty" slot="empty">
|
||||||
@@ -170,15 +167,10 @@ export default Vue.extend({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
|
||||||
.mk-timeline-core
|
.mk-timeline-core
|
||||||
> .mk-friends-maker
|
> .mk-friends-maker
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px #eee
|
||||||
|
|
||||||
> .fetching
|
|
||||||
padding 64px 0
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="stylus" module>
|
<style lang="stylus" module>
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
<li @click="list">
|
<li @click="list">
|
||||||
<p>%fa:list%<span>%i18n:@lists%</span>%fa:angle-right%</p>
|
<p>%fa:list%<span>%i18n:@lists%</span>%fa:angle-right%</p>
|
||||||
</li>
|
</li>
|
||||||
<li @click="followRequests" v-if="$store.state.i.isLocked">
|
<li @click="followRequests" v-if="($store.state.i.isLocked || $store.state.i.carefulBot)">
|
||||||
<p>%fa:envelope R%<span>%i18n:@follow-requests%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></span>%fa:angle-right%</p>
|
<p>%fa:envelope R%<span>%i18n:@follow-requests%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></span>%fa:angle-right%</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -17,8 +17,6 @@ export default Vue.extend({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
|
||||||
.note
|
.note
|
||||||
display inline-block
|
display inline-block
|
||||||
padding 8px
|
padding 8px
|
||||||
|
@@ -78,7 +78,7 @@ export default Vue.extend({
|
|||||||
this.connection.on('stats', this.onStats);
|
this.connection.on('stats', this.onStats);
|
||||||
this.connection.on('statsLog', this.onStatsLog);
|
this.connection.on('statsLog', this.onStatsLog);
|
||||||
this.connection.send('requestLog', {
|
this.connection.send('requestLog', {
|
||||||
id: Math.random().toString(),
|
id: Math.random().toString().substr(2, 8),
|
||||||
length: 200
|
length: 200
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@@ -3,9 +3,6 @@
|
|||||||
<header :class="$style.header">
|
<header :class="$style.header">
|
||||||
<h1>{{ q }}</h1>
|
<h1>{{ q }}</h1>
|
||||||
</header>
|
</header>
|
||||||
<div :class="$style.loading" v-if="fetching">
|
|
||||||
<mk-ellipsis-icon/>
|
|
||||||
</div>
|
|
||||||
<p :class="$style.notAvailable" v-if="!fetching && notAvailable">%i18n:@not-available%</p>
|
<p :class="$style.notAvailable" v-if="!fetching && notAvailable">%i18n:@not-available%</p>
|
||||||
<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:not-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:not-found%'.split('{}')[1] }}</p>
|
<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:not-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:not-found%'.split('{}')[1] }}</p>
|
||||||
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
|
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
|
||||||
@@ -119,9 +116,6 @@ export default Vue.extend({
|
|||||||
border-radius 6px
|
border-radius 6px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
|
|
||||||
.loading
|
|
||||||
padding 64px 0
|
|
||||||
|
|
||||||
.empty
|
.empty
|
||||||
display block
|
display block
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
@@ -3,9 +3,6 @@
|
|||||||
<header :class="$style.header">
|
<header :class="$style.header">
|
||||||
<h1>#{{ $route.params.tag }}</h1>
|
<h1>#{{ $route.params.tag }}</h1>
|
||||||
</header>
|
</header>
|
||||||
<div :class="$style.loading" v-if="fetching">
|
|
||||||
<mk-ellipsis-icon/>
|
|
||||||
</div>
|
|
||||||
<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:no-posts-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:no-posts-found%'.split('{}')[1] }}</p>
|
<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:no-posts-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:no-posts-found%'.split('{}')[1] }}</p>
|
||||||
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
|
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
@@ -108,9 +105,6 @@ export default Vue.extend({
|
|||||||
border-radius 6px
|
border-radius 6px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
|
|
||||||
.loading
|
|
||||||
padding 64px 0
|
|
||||||
|
|
||||||
.empty
|
.empty
|
||||||
display block
|
display block
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
@@ -5,9 +5,6 @@
|
|||||||
<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%fa:comments% %i18n:@with-replies%</span>
|
<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%fa:comments% %i18n:@with-replies%</span>
|
||||||
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%fa:images% %i18n:@with-media%</span>
|
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%fa:images% %i18n:@with-media%</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="loading" v-if="fetching">
|
|
||||||
<mk-ellipsis-icon/>
|
|
||||||
</div>
|
|
||||||
<mk-notes ref="timeline" :more="existMore ? more : null">
|
<mk-notes ref="timeline" :more="existMore ? more : null">
|
||||||
<p class="empty" slot="empty">%fa:R comments%%i18n:@empty%</p>
|
<p class="empty" slot="empty">%fa:R comments%%i18n:@empty%</p>
|
||||||
</mk-notes>
|
</mk-notes>
|
||||||
@@ -152,9 +149,6 @@ export default Vue.extend({
|
|||||||
&:hover
|
&:hover
|
||||||
color var(--desktopTimelineSrcHover)
|
color var(--desktopTimelineSrcHover)
|
||||||
|
|
||||||
> .loading
|
|
||||||
padding 64px 0
|
|
||||||
|
|
||||||
> .empty
|
> .empty
|
||||||
display block
|
display block
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
@@ -443,10 +443,10 @@ export default class MiOS extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
const viaStream = this.stream && this.store.state.device.apiViaStream && !forceFetch;
|
const viaStream = this.stream && this.stream.state == 'connected' && this.store.state.device.apiViaStream && !forceFetch;
|
||||||
|
|
||||||
if (viaStream) {
|
if (viaStream) {
|
||||||
const id = Math.random().toString();
|
const id = Math.random().toString().substr(2, 8);
|
||||||
|
|
||||||
this.stream.once(`api:${id}`, res => {
|
this.stream.once(`api:${id}`, res => {
|
||||||
if (res == null || Object.keys(res).length == 0) {
|
if (res == null || Object.keys(res).length == 0) {
|
||||||
|
@@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
|
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
|
||||||
|
|
||||||
<div class="init" v-if="fetching">
|
<div class="skeleton" v-if="fetching">
|
||||||
%fa:spinner .pulse%%i18n:common.loading%
|
<template v-for="i in 10">
|
||||||
|
<mk-note-skeleton :key="i"/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!fetching && requestInitPromise != null">
|
<div v-if="!fetching && requestInitPromise != null">
|
||||||
@@ -251,13 +253,12 @@ export default Vue.extend({
|
|||||||
[data-fa]
|
[data-fa]
|
||||||
margin-right 8px
|
margin-right 8px
|
||||||
|
|
||||||
> .init
|
> .skeleton
|
||||||
padding 64px 0
|
padding 16px
|
||||||
text-align center
|
opacity 0.3
|
||||||
color #999
|
|
||||||
|
|
||||||
> [data-fa]
|
@media (min-width 500px)
|
||||||
margin-right 4px
|
padding 32px
|
||||||
|
|
||||||
> .empty
|
> .empty
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-notifications">
|
<div class="mk-notifications">
|
||||||
|
<div class="skeleton" v-if="fetching">
|
||||||
|
<template v-for="i in 10">
|
||||||
|
<mk-note-skeleton :key="i"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||||
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications">
|
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications">
|
||||||
<template v-for="(notification, i) in _notifications">
|
<template v-for="(notification, i) in _notifications">
|
||||||
@@ -17,7 +23,6 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
|
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
|
||||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -179,13 +184,11 @@ export default Vue.extend({
|
|||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color #aaa
|
||||||
|
|
||||||
> .fetching
|
> .skeleton
|
||||||
margin 0
|
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
opacity 0.3
|
||||||
color #aaa
|
|
||||||
|
|
||||||
> [data-fa]
|
@media (min-width 500px)
|
||||||
margin-right 4px
|
padding 32px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<li><router-link to="/" :data-active="$route.name == 'index'">%fa:home%%i18n:@timeline%%fa:angle-right%</router-link></li>
|
<li><router-link to="/" :data-active="$route.name == 'index'">%fa:home%%i18n:@timeline%%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/i/notifications" :data-active="$route.name == 'notifications'">%fa:R bell%%i18n:@notifications%<template v-if="hasUnreadNotification">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/notifications" :data-active="$route.name == 'notifications'">%fa:R bell%%i18n:@notifications%<template v-if="hasUnreadNotification">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'">%fa:R comments%%i18n:@messaging%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'">%fa:R comments%%i18n:@messaging%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
<li v-if="$store.getters.isSignedIn && $store.state.i.isLocked"><router-link to="/i/received-follow-requests" :data-active="$route.name == 'received-follow-requests'">%fa:R envelope%%i18n:@follow-requests%<template v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li v-if="$store.getters.isSignedIn && ($store.state.i.isLocked || $store.state.i.carefulBot)"><router-link to="/i/received-follow-requests" :data-active="$route.name == 'received-follow-requests'">%fa:R envelope%%i18n:@follow-requests%<template v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
<li><router-link to="/reversi" :data-active="$route.name == 'reversi'">%fa:gamepad%%i18n:@game%<template v-if="hasGameInvitation">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/reversi" :data-active="$route.name == 'reversi'">%fa:gamepad%%i18n:@game%<template v-if="hasGameInvitation">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
|
@@ -58,6 +58,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch>
|
<ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch>
|
||||||
|
<ui-switch v-model="carefulBot" @change="save(false)">%i18n:@careful-bot%</ui-switch>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
@@ -80,6 +81,7 @@ export default Vue.extend({
|
|||||||
bannerId: null,
|
bannerId: null,
|
||||||
isCat: false,
|
isCat: false,
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
|
carefulBot: false,
|
||||||
saving: false,
|
saving: false,
|
||||||
avatarUploading: false,
|
avatarUploading: false,
|
||||||
bannerUploading: false
|
bannerUploading: false
|
||||||
@@ -103,6 +105,7 @@ export default Vue.extend({
|
|||||||
this.bannerId = this.$store.state.i.bannerId;
|
this.bannerId = this.$store.state.i.bannerId;
|
||||||
this.isCat = this.$store.state.i.isCat;
|
this.isCat = this.$store.state.i.isCat;
|
||||||
this.isLocked = this.$store.state.i.isLocked;
|
this.isLocked = this.$store.state.i.isLocked;
|
||||||
|
this.carefulBot = this.$store.state.i.carefulBot;
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -161,7 +164,8 @@ export default Vue.extend({
|
|||||||
avatarId: this.avatarId,
|
avatarId: this.avatarId,
|
||||||
bannerId: this.bannerId,
|
bannerId: this.bannerId,
|
||||||
isCat: this.isCat,
|
isCat: this.isCat,
|
||||||
isLocked: this.isLocked
|
isLocked: this.isLocked,
|
||||||
|
carefulBot: this.carefulBot
|
||||||
}).then(i => {
|
}).then(i => {
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
this.$store.state.i.avatarId = i.avatarId;
|
this.$store.state.i.avatarId = i.avatarId;
|
||||||
|
@@ -65,6 +65,16 @@ type IUserBase = {
|
|||||||
*/
|
*/
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Botか否か
|
||||||
|
*/
|
||||||
|
isBot: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Botからのフォローを承認制にするか
|
||||||
|
*/
|
||||||
|
carefulBot: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* このアカウントに届いているフォローリクエストの数
|
* このアカウントに届いているフォローリクエストの数
|
||||||
*/
|
*/
|
||||||
@@ -94,7 +104,6 @@ export interface ILocalUser extends IUserBase {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
};
|
};
|
||||||
lastUsedAt: Date;
|
lastUsedAt: Date;
|
||||||
isBot: boolean;
|
|
||||||
isCat: boolean;
|
isCat: boolean;
|
||||||
isAdmin?: boolean;
|
isAdmin?: boolean;
|
||||||
isVerified?: boolean;
|
isVerified?: boolean;
|
||||||
|
@@ -67,6 +67,12 @@ export const meta = {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
carefulBot: $.bool.optional.note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'Botからのフォローを承認制にするか'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
isBot: $.bool.optional.note({
|
isBot: $.bool.optional.note({
|
||||||
desc: {
|
desc: {
|
||||||
'ja-JP': 'Botか否か'
|
'ja-JP': 'Botか否か'
|
||||||
@@ -110,6 +116,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
|
|||||||
if (ps.wallpaperId !== undefined) updates.wallpaperId = ps.wallpaperId;
|
if (ps.wallpaperId !== undefined) updates.wallpaperId = ps.wallpaperId;
|
||||||
if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
|
if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
|
||||||
if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
|
if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
|
||||||
|
if (typeof ps.carefulBot == 'boolean') updates.carefulBot = ps.carefulBot;
|
||||||
if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
|
if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
|
||||||
if (typeof ps.autoWatch == 'boolean') updates['settings.autoWatch'] = ps.autoWatch;
|
if (typeof ps.autoWatch == 'boolean') updates['settings.autoWatch'] = ps.autoWatch;
|
||||||
if (typeof ps.alwaysMarkNsfw == 'boolean') updates['settings.alwaysMarkNsfw'] = ps.alwaysMarkNsfw;
|
if (typeof ps.alwaysMarkNsfw == 'boolean') updates['settings.alwaysMarkNsfw'] = ps.alwaysMarkNsfw;
|
||||||
|
@@ -146,9 +146,9 @@ export default class Connection {
|
|||||||
*/
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
private onChannelConnectRequested(payload: any) {
|
private onChannelConnectRequested(payload: any) {
|
||||||
const { channel, id, params } = payload;
|
const { channel, id, params, pong } = payload;
|
||||||
log(`CH CONNECT: ${id} ${channel} by @${this.user.username}`);
|
log(`CH CONNECT: ${id} ${channel} by @${this.user.username}`);
|
||||||
this.connectChannel(id, params, channel);
|
this.connectChannel(id, params, channel, pong);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,7 +177,7 @@ export default class Connection {
|
|||||||
* チャンネルに接続
|
* チャンネルに接続
|
||||||
*/
|
*/
|
||||||
@autobind
|
@autobind
|
||||||
public connectChannel(id: string, params: any, channel: string) {
|
public connectChannel(id: string, params: any, channel: string, pong = false) {
|
||||||
// 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視
|
// 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視
|
||||||
if ((channels as any)[channel].shouldShare && this.channels.some(c => c.chName === channel)) {
|
if ((channels as any)[channel].shouldShare && this.channels.some(c => c.chName === channel)) {
|
||||||
return;
|
return;
|
||||||
@@ -186,9 +186,12 @@ export default class Connection {
|
|||||||
const ch: Channel = new (channels as any)[channel](id, this);
|
const ch: Channel = new (channels as any)[channel](id, this);
|
||||||
this.channels.push(ch);
|
this.channels.push(ch);
|
||||||
ch.init(params);
|
ch.init(params);
|
||||||
this.sendMessageToWs('connected', {
|
|
||||||
id: id
|
if (pong) {
|
||||||
});
|
this.sendMessageToWs('connected', {
|
||||||
|
id: id
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -64,14 +64,14 @@ module.exports = (server: http.Server) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
main.connectChannel(Math.random().toString(), null,
|
main.connectChannel(Math.random().toString().substr(2, 8), null,
|
||||||
request.resourceURL.pathname === '/' ? 'homeTimeline' :
|
request.resourceURL.pathname === '/' ? 'homeTimeline' :
|
||||||
request.resourceURL.pathname === '/local-timeline' ? 'localTimeline' :
|
request.resourceURL.pathname === '/local-timeline' ? 'localTimeline' :
|
||||||
request.resourceURL.pathname === '/hybrid-timeline' ? 'hybridTimeline' :
|
request.resourceURL.pathname === '/hybrid-timeline' ? 'hybridTimeline' :
|
||||||
request.resourceURL.pathname === '/global-timeline' ? 'globalTimeline' : null);
|
request.resourceURL.pathname === '/global-timeline' ? 'globalTimeline' : null);
|
||||||
|
|
||||||
if (request.resourceURL.pathname === '/') {
|
if (request.resourceURL.pathname === '/') {
|
||||||
main.connectChannel(Math.random().toString(), null, 'main');
|
main.connectChannel(Math.random().toString().substr(2, 8), null, 'main');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,70 +11,75 @@ import { deliver } from '../../queue';
|
|||||||
import createFollowRequest from './requests/create';
|
import createFollowRequest from './requests/create';
|
||||||
|
|
||||||
export default async function(follower: IUser, followee: IUser) {
|
export default async function(follower: IUser, followee: IUser) {
|
||||||
if (followee.isLocked || isLocalUser(follower) && isRemoteUser(followee)) {
|
// フォロー対象が鍵アカウントである or
|
||||||
|
// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
|
||||||
|
// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである
|
||||||
|
// 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく
|
||||||
|
if (followee.isLocked || (followee.carefulBot && follower.isBot) || (isLocalUser(follower) && isRemoteUser(followee))) {
|
||||||
await createFollowRequest(follower, followee);
|
await createFollowRequest(follower, followee);
|
||||||
} else {
|
return;
|
||||||
const following = await Following.insert({
|
}
|
||||||
createdAt: new Date(),
|
|
||||||
followerId: follower._id,
|
|
||||||
followeeId: followee._id,
|
|
||||||
|
|
||||||
// 非正規化
|
const following = await Following.insert({
|
||||||
_follower: {
|
createdAt: new Date(),
|
||||||
host: follower.host,
|
followerId: follower._id,
|
||||||
inbox: isRemoteUser(follower) ? follower.inbox : undefined,
|
followeeId: followee._id,
|
||||||
sharedInbox: isRemoteUser(follower) ? follower.sharedInbox : undefined
|
|
||||||
},
|
|
||||||
_followee: {
|
|
||||||
host: followee.host,
|
|
||||||
inbox: isRemoteUser(followee) ? followee.inbox : undefined,
|
|
||||||
sharedInbox: isRemoteUser(followee) ? followee.sharedInbox : undefined
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//#region Increment following count
|
// 非正規化
|
||||||
User.update({ _id: follower._id }, {
|
_follower: {
|
||||||
$inc: {
|
host: follower.host,
|
||||||
followingCount: 1
|
inbox: isRemoteUser(follower) ? follower.inbox : undefined,
|
||||||
}
|
sharedInbox: isRemoteUser(follower) ? follower.sharedInbox : undefined
|
||||||
});
|
},
|
||||||
|
_followee: {
|
||||||
FollowingLog.insert({
|
host: followee.host,
|
||||||
createdAt: following.createdAt,
|
inbox: isRemoteUser(followee) ? followee.inbox : undefined,
|
||||||
userId: follower._id,
|
sharedInbox: isRemoteUser(followee) ? followee.sharedInbox : undefined
|
||||||
count: follower.followingCount + 1
|
|
||||||
});
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Increment followers count
|
|
||||||
User.update({ _id: followee._id }, {
|
|
||||||
$inc: {
|
|
||||||
followersCount: 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
FollowedLog.insert({
|
|
||||||
createdAt: following.createdAt,
|
|
||||||
userId: followee._id,
|
|
||||||
count: followee.followersCount + 1
|
|
||||||
});
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
// Publish follow event
|
|
||||||
if (isLocalUser(follower)) {
|
|
||||||
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'follow', packed));
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Publish followed event
|
//#region Increment following count
|
||||||
if (isLocalUser(followee)) {
|
User.update({ _id: follower._id }, {
|
||||||
packUser(follower, followee).then(packed => publishMainStream(followee._id, 'followed', packed)),
|
$inc: {
|
||||||
|
followingCount: 1
|
||||||
// 通知を作成
|
|
||||||
notify(followee._id, follower._id, 'follow');
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (isRemoteUser(follower) && isLocalUser(followee)) {
|
FollowingLog.insert({
|
||||||
const content = pack(renderAccept(renderFollow(follower, followee)));
|
createdAt: following.createdAt,
|
||||||
deliver(followee, content, follower.inbox);
|
userId: follower._id,
|
||||||
|
count: follower.followingCount + 1
|
||||||
|
});
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Increment followers count
|
||||||
|
User.update({ _id: followee._id }, {
|
||||||
|
$inc: {
|
||||||
|
followersCount: 1
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
FollowedLog.insert({
|
||||||
|
createdAt: following.createdAt,
|
||||||
|
userId: followee._id,
|
||||||
|
count: followee.followersCount + 1
|
||||||
|
});
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
// Publish follow event
|
||||||
|
if (isLocalUser(follower)) {
|
||||||
|
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'follow', packed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish followed event
|
||||||
|
if (isLocalUser(followee)) {
|
||||||
|
packUser(follower, followee).then(packed => publishMainStream(followee._id, 'followed', packed)),
|
||||||
|
|
||||||
|
// 通知を作成
|
||||||
|
notify(followee._id, follower._id, 'follow');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRemoteUser(follower) && isLocalUser(followee)) {
|
||||||
|
const content = pack(renderAccept(renderFollow(follower, followee)));
|
||||||
|
deliver(followee, content, follower.inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
import User, { isLocalUser, isRemoteUser, ILocalUser, IUser } from '../../models/user';
|
import User, { isLocalUser, isRemoteUser, ILocalUser, IUser } from '../../models/user';
|
||||||
import Note from '../../models/note';
|
import Note, { packMany } from '../../models/note';
|
||||||
import Following from '../../models/following';
|
import Following from '../../models/following';
|
||||||
import renderAdd from '../../remote/activitypub/renderer/add';
|
import renderAdd from '../../remote/activitypub/renderer/add';
|
||||||
import renderRemove from '../../remote/activitypub/renderer/remove';
|
import renderRemove from '../../remote/activitypub/renderer/remove';
|
||||||
@@ -27,11 +27,11 @@ export async function addPinned(user: IUser, noteId: mongo.ObjectID) {
|
|||||||
let pinnedNoteIds = user.pinnedNoteIds || [];
|
let pinnedNoteIds = user.pinnedNoteIds || [];
|
||||||
|
|
||||||
//#region 現在ピン留め投稿している投稿が実際にデータベースに存在しているのかチェック
|
//#region 現在ピン留め投稿している投稿が実際にデータベースに存在しているのかチェック
|
||||||
// データベースの欠損などで存在していない場合があるので。
|
// データベースの欠損などで存在していない(または破損している)場合があるので。
|
||||||
// 存在していなかったらピン留め投稿から外す
|
// 存在していなかったらピン留め投稿から外す
|
||||||
const pinnedNotes = (await Promise.all(pinnedNoteIds.map(id => Note.findOne({ _id: id })))).filter(x => x != null);
|
const pinnedNotes = await packMany(pinnedNoteIds, null, { detail: true });
|
||||||
|
|
||||||
pinnedNoteIds = pinnedNoteIds.filter(id => pinnedNotes.some(n => n._id.equals(id)));
|
pinnedNoteIds = pinnedNoteIds.filter(id => pinnedNotes.some(n => n.id.toString() === id.toHexString()));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (pinnedNoteIds.length >= 5) {
|
if (pinnedNoteIds.length >= 5) {
|
||||||
|
Reference in New Issue
Block a user