Merge branch 'notification-read-api' into swn
This commit is contained in:
commit
7f8e3b0dee
60 changed files with 511 additions and 468 deletions
|
@ -10,6 +10,12 @@
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 12.101.1 (2021/12/29)
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
- SVG絵文字が表示できないのを修正
|
||||||
|
- エクスポートした絵文字の拡張子がfalseになることがあるのを修正
|
||||||
|
|
||||||
## 12.101.0 (2021/12/29)
|
## 12.101.0 (2021/12/29)
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
|
@ -87,7 +87,7 @@ Configuration files are located in [`/.github/workflows`](/.github/workflows).
|
||||||
|
|
||||||
## Vue
|
## Vue
|
||||||
Misskey uses Vue(v3) as its front-end framework.
|
Misskey uses Vue(v3) as its front-end framework.
|
||||||
**When creating a new component, please use the Composition API (and [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html)) instead of the Options API.**
|
**When creating a new component, please use the Composition API (with [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html) and [ref sugar](https://github.com/vuejs/rfcs/discussions/369)) instead of the Options API.**
|
||||||
Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
|
Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
|
||||||
|
|
||||||
## Adding MisskeyRoom items
|
## Adding MisskeyRoom items
|
||||||
|
|
|
@ -743,7 +743,7 @@ online: "オンライン"
|
||||||
active: "アクティブ"
|
active: "アクティブ"
|
||||||
offline: "オフライン"
|
offline: "オフライン"
|
||||||
notRecommended: "非推奨"
|
notRecommended: "非推奨"
|
||||||
botProtection: "Bot防御"
|
botProtection: "Botプロテクション"
|
||||||
instanceBlocking: "インスタンスブロック"
|
instanceBlocking: "インスタンスブロック"
|
||||||
selectAccount: "アカウントを選択"
|
selectAccount: "アカウントを選択"
|
||||||
enabled: "有効"
|
enabled: "有効"
|
||||||
|
@ -754,7 +754,7 @@ administration: "管理"
|
||||||
accounts: "アカウント"
|
accounts: "アカウント"
|
||||||
switch: "切り替え"
|
switch: "切り替え"
|
||||||
noMaintainerInformationWarning: "管理者情報が設定されていません。"
|
noMaintainerInformationWarning: "管理者情報が設定されていません。"
|
||||||
noBotProtectionWarning: "Bot防御が設定されていません。"
|
noBotProtectionWarning: "Botプロテクションが設定されていません。"
|
||||||
configure: "設定する"
|
configure: "設定する"
|
||||||
postToGallery: "ギャラリーへ投稿"
|
postToGallery: "ギャラリーへ投稿"
|
||||||
gallery: "ギャラリー"
|
gallery: "ギャラリー"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "12.101.0",
|
"version": "12.101.1",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -65,7 +65,8 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
|
||||||
|
|
||||||
for (const emoji of customEmojis) {
|
for (const emoji of customEmojis) {
|
||||||
const exportId = ulid().toLowerCase();
|
const exportId = ulid().toLowerCase();
|
||||||
const emojiPath = path + '/' + exportId + '.' + mime.extension(emoji.type);
|
const ext = mime.extension(emoji.type);
|
||||||
|
const emojiPath = path + '/' + exportId + (ext ? '.' + ext : '');
|
||||||
fs.writeFileSync(emojiPath, '', 'binary');
|
fs.writeFileSync(emojiPath, '', 'binary');
|
||||||
let downloaded = false;
|
let downloaded = false;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ export async function proxyMedia(ctx: Koa.Context) {
|
||||||
|
|
||||||
const { mime, ext } = await detectType(path);
|
const { mime, ext } = await detectType(path);
|
||||||
|
|
||||||
|
if (!mime.startsWith('image/')) throw 403;
|
||||||
if (!FILE_TYPE_BROWSERSAFE.includes(mime)) throw 403;
|
if (!FILE_TYPE_BROWSERSAFE.includes(mime)) throw 403;
|
||||||
|
|
||||||
let image: IImage;
|
let image: IImage;
|
||||||
|
|
|
@ -161,7 +161,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!['image/jpeg', 'image/png', 'image/webp'].includes(type)) {
|
if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml'].includes(type)) {
|
||||||
logger.debug(`web image and thumbnail not created (not an required file)`);
|
logger.debug(`web image and thumbnail not created (not an required file)`);
|
||||||
return {
|
return {
|
||||||
webpublic: null,
|
webpublic: null,
|
||||||
|
@ -202,7 +202,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||||
webpublic = await convertSharpToJpeg(img, 2048, 2048);
|
webpublic = await convertSharpToJpeg(img, 2048, 2048);
|
||||||
} else if (['image/webp'].includes(type)) {
|
} else if (['image/webp'].includes(type)) {
|
||||||
webpublic = await convertSharpToWebp(img, 2048, 2048);
|
webpublic = await convertSharpToWebp(img, 2048, 2048);
|
||||||
} else if (['image/png'].includes(type)) {
|
} else if (['image/png', 'image/svg+xml'].includes(type)) {
|
||||||
webpublic = await convertSharpToPng(img, 2048, 2048);
|
webpublic = await convertSharpToPng(img, 2048, 2048);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`web image not created (not an required image)`);
|
logger.debug(`web image not created (not an required image)`);
|
||||||
|
@ -221,7 +221,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||||
try {
|
try {
|
||||||
if (['image/jpeg', 'image/webp'].includes(type)) {
|
if (['image/jpeg', 'image/webp'].includes(type)) {
|
||||||
thumbnail = await convertSharpToJpeg(img, 498, 280);
|
thumbnail = await convertSharpToJpeg(img, 498, 280);
|
||||||
} else if (['image/png'].includes(type)) {
|
} else if (['image/png', 'image/svg+xml'].includes(type)) {
|
||||||
thumbnail = await convertSharpToPngOrJpeg(img, 498, 280);
|
thumbnail = await convertSharpToPngOrJpeg(img, 498, 280);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`thumbnail not created (not an required file)`);
|
logger.debug(`thumbnail not created (not an required file)`);
|
||||||
|
|
2
packages/client/@types/vue.d.ts
vendored
2
packages/client/@types/vue.d.ts
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
/// <reference types="vue/macros-global" />
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
import type { DefineComponent } from 'vue';
|
import type { DefineComponent } from 'vue';
|
||||||
const component: DefineComponent<{}, {}, any>;
|
const component: DefineComponent<{}, {}, any>;
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
"v-debounce": "0.1.2",
|
"v-debounce": "0.1.2",
|
||||||
"vanilla-tilt": "1.7.2",
|
"vanilla-tilt": "1.7.2",
|
||||||
"vue": "3.2.26",
|
"vue": "3.2.26",
|
||||||
"vue-loader": "16.8.3",
|
"vue-loader": "17.0.0",
|
||||||
"vue-prism-editor": "2.0.0-alpha.2",
|
"vue-prism-editor": "2.0.0-alpha.2",
|
||||||
"vue-router": "4.0.5",
|
"vue-router": "4.0.5",
|
||||||
"vue-style-loader": "4.1.3",
|
"vue-style-loader": "4.1.3",
|
||||||
|
|
|
@ -53,6 +53,7 @@ import XFolder from './drive.folder.vue';
|
||||||
import XFile from './drive.file.vue';
|
import XFile from './drive.file.vue';
|
||||||
import MkButton from './ui/button.vue';
|
import MkButton from './ui/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -140,7 +141,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connection = markRaw(os.stream.useChannel('drive'));
|
this.connection = markRaw(stream.useChannel('drive'));
|
||||||
|
|
||||||
this.connection.on('fileCreated', this.onStreamDriveFileCreated);
|
this.connection.on('fileCreated', this.onStreamDriveFileCreated);
|
||||||
this.connection.on('fileUpdated', this.onStreamDriveFileUpdated);
|
this.connection.on('fileUpdated', this.onStreamDriveFileUpdated);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, markRaw } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -71,7 +72,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = markRaw(os.stream.useChannel('main'));
|
this.connection = markRaw(stream.useChannel('main'));
|
||||||
|
|
||||||
this.connection.on('follow', this.onFollowChange);
|
this.connection.on('follow', this.onFollowChange);
|
||||||
this.connection.on('unfollow', this.onFollowChange);
|
this.connection.on('unfollow', this.onFollowChange);
|
||||||
|
|
|
@ -7,12 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
27
packages/client/src/components/form/split.vue
Normal file
27
packages/client/src/components/form/split.vue
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<div class="terlnhxf _formBlock">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
minWidth: number;
|
||||||
|
}>(), {
|
||||||
|
minWidth: 210,
|
||||||
|
});
|
||||||
|
|
||||||
|
const minWidth = props.minWidth + 'px';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.terlnhxf {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(v-bind('minWidth'), 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
|
||||||
|
> ::v-deep(*) {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,7 +13,8 @@
|
||||||
<i class="check fas fa-check"></i>
|
<i class="check fas fa-check"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="label">
|
<span class="label">
|
||||||
<span @click="toggle"><slot></slot></span>
|
<!-- TODO: 無名slotの方は廃止 -->
|
||||||
|
<span @click="toggle"><slot name="label"></slot><slot></slot></span>
|
||||||
<p class="caption"><slot name="caption"></slot></p>
|
<p class="caption"><slot name="caption"></slot></p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -140,6 +140,7 @@ import { checkWordMute } from '@/scripts/check-word-mute';
|
||||||
import { userPage } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
import { notePage } from '@/filters/note';
|
import { notePage } from '@/filters/note';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { noteActions, noteViewInterruptors } from '@/store';
|
import { noteActions, noteViewInterruptors } from '@/store';
|
||||||
import { reactionPicker } from '@/scripts/reaction-picker';
|
import { reactionPicker } from '@/scripts/reaction-picker';
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
||||||
|
@ -260,7 +261,7 @@ export default defineComponent({
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection = os.stream;
|
this.connection = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
|
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
|
||||||
|
|
|
@ -122,6 +122,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||||
import { checkWordMute } from '@/scripts/check-word-mute';
|
import { checkWordMute } from '@/scripts/check-word-mute';
|
||||||
import { userPage } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { noteActions, noteViewInterruptors } from '@/store';
|
import { noteActions, noteViewInterruptors } from '@/store';
|
||||||
import { reactionPicker } from '@/scripts/reaction-picker';
|
import { reactionPicker } from '@/scripts/reaction-picker';
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
||||||
|
@ -245,7 +246,7 @@ export default defineComponent({
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection = os.stream;
|
this.connection = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.collapsed = this.appearNote.cw == null && this.appearNote.text && (
|
this.collapsed = this.appearNote.cw == null && this.appearNote.text && (
|
||||||
|
|
|
@ -74,6 +74,7 @@ import { notePage } from '@/filters/note';
|
||||||
import { userPage } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { useTooltip } from '@/scripts/use-tooltip';
|
import { useTooltip } from '@/scripts/use-tooltip';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -106,7 +107,7 @@ export default defineComponent({
|
||||||
if (!props.notification.isRead) {
|
if (!props.notification.isRead) {
|
||||||
const readObserver = new IntersectionObserver((entries, observer) => {
|
const readObserver = new IntersectionObserver((entries, observer) => {
|
||||||
if (!entries.some(entry => entry.isIntersecting)) return;
|
if (!entries.some(entry => entry.isIntersecting)) return;
|
||||||
os.stream.send('readNotification', {
|
stream.send('readNotification', {
|
||||||
id: props.notification.id
|
id: props.notification.id
|
||||||
});
|
});
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
|
@ -114,7 +115,7 @@ export default defineComponent({
|
||||||
|
|
||||||
readObserver.observe(elRef.value);
|
readObserver.observe(elRef.value);
|
||||||
|
|
||||||
const connection = os.stream.useChannel('main');
|
const connection = stream.useChannel('main');
|
||||||
connection.on('readAllNotifications', () => readObserver.disconnect());
|
connection.on('readAllNotifications', () => readObserver.disconnect());
|
||||||
|
|
||||||
watch(props.notification.isRead, () => {
|
watch(props.notification.isRead, () => {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import XList from './date-separated-list.vue';
|
||||||
import XNote from './note.vue';
|
import XNote from './note.vue';
|
||||||
import { notificationTypes } from 'misskey-js';
|
import { notificationTypes } from 'misskey-js';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
import MkButton from '@/components/ui/button.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -100,7 +101,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = markRaw(os.stream.useChannel('main'));
|
this.connection = markRaw(stream.useChannel('main'));
|
||||||
this.connection.on('notification', this.onNotification);
|
this.connection.on('notification', this.onNotification);
|
||||||
|
|
||||||
this.connection.on('readAllNotifications', () => {
|
this.connection.on('readAllNotifications', () => {
|
||||||
|
@ -133,7 +134,7 @@ export default defineComponent({
|
||||||
onNotification(notification) {
|
onNotification(notification) {
|
||||||
const isMuted = !this.allIncludeTypes.includes(notification.type);
|
const isMuted = !this.allIncludeTypes.includes(notification.type);
|
||||||
if (isMuted || document.visibilityState === 'visible') {
|
if (isMuted || document.visibilityState === 'visible') {
|
||||||
os.stream.send('readNotification', {
|
stream.send('readNotification', {
|
||||||
id: notification.id
|
id: notification.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,11 +74,11 @@ import { formatTimeString } from '@/scripts/format-time-string';
|
||||||
import { Autocomplete } from '@/scripts/autocomplete';
|
import { Autocomplete } from '@/scripts/autocomplete';
|
||||||
import { noteVisibilities } from 'misskey-js';
|
import { noteVisibilities } from 'misskey-js';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { selectFiles } from '@/scripts/select-file';
|
import { selectFiles } from '@/scripts/select-file';
|
||||||
import { defaultStore, notePostInterruptors, postFormActions } from '@/store';
|
import { defaultStore, notePostInterruptors, postFormActions } from '@/store';
|
||||||
import { throttle } from 'throttle-debounce';
|
import { throttle } from 'throttle-debounce';
|
||||||
import MkInfo from '@/components/ui/info.vue';
|
import MkInfo from '@/components/ui/info.vue';
|
||||||
import { defaultStore } from '@/store';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -176,7 +176,7 @@ export default defineComponent({
|
||||||
imeText: '',
|
imeText: '',
|
||||||
typing: throttle(3000, () => {
|
typing: throttle(3000, () => {
|
||||||
if (this.channel) {
|
if (this.channel) {
|
||||||
os.stream.send('typingOnChannel', { channel: this.channel.id });
|
stream.send('typingOnChannel', { channel: this.channel.id });
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
postFormActions,
|
postFormActions,
|
||||||
|
|
|
@ -83,6 +83,7 @@ import MkTab from '@/components/tab.vue';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
import MkButton from '@/components/ui/button.vue';
|
||||||
import follow from '@/directives/follow-append';
|
import follow from '@/directives/follow-append';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -104,15 +105,15 @@ export default defineComponent({
|
||||||
const connections = shallowRef([]);
|
const connections = shallowRef([]);
|
||||||
const pools = shallowRef([]);
|
const pools = shallowRef([]);
|
||||||
const refreshStreamInfo = () => {
|
const refreshStreamInfo = () => {
|
||||||
console.log(os.stream.sharedConnectionPools, os.stream.sharedConnections, os.stream.nonSharedConnections);
|
console.log(stream.sharedConnectionPools, stream.sharedConnections, stream.nonSharedConnections);
|
||||||
const conn = os.stream.sharedConnections.map(c => ({
|
const conn = stream.sharedConnections.map(c => ({
|
||||||
id: c.id, name: c.name, channel: c.channel, users: c.pool.users, in: c.inCount, out: c.outCount,
|
id: c.id, name: c.name, channel: c.channel, users: c.pool.users, in: c.inCount, out: c.outCount,
|
||||||
})).concat(os.stream.nonSharedConnections.map(c => ({
|
})).concat(stream.nonSharedConnections.map(c => ({
|
||||||
id: c.id, name: c.name, channel: c.channel, users: null, in: c.inCount, out: c.outCount,
|
id: c.id, name: c.name, channel: c.channel, users: null, in: c.inCount, out: c.outCount,
|
||||||
})));
|
})));
|
||||||
conn.sort((a, b) => (a.id > b.id) ? 1 : -1);
|
conn.sort((a, b) => (a.id > b.id) ? 1 : -1);
|
||||||
connections.value = conn;
|
connections.value = conn;
|
||||||
pools.value = os.stream.sharedConnectionPools;
|
pools.value = stream.sharedConnectionPools;
|
||||||
};
|
};
|
||||||
const interval = setInterval(refreshStreamInfo, 1000);
|
const interval = setInterval(refreshStreamInfo, 1000);
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { defineComponent, markRaw } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import XNotes from './notes.vue';
|
import XNotes from './notes.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -92,33 +93,33 @@ export default defineComponent({
|
||||||
this.query = {
|
this.query = {
|
||||||
antennaId: this.antenna
|
antennaId: this.antenna
|
||||||
};
|
};
|
||||||
this.connection = markRaw(os.stream.useChannel('antenna', {
|
this.connection = markRaw(stream.useChannel('antenna', {
|
||||||
antennaId: this.antenna
|
antennaId: this.antenna
|
||||||
}));
|
}));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
} else if (this.src == 'home') {
|
} else if (this.src == 'home') {
|
||||||
endpoint = 'notes/timeline';
|
endpoint = 'notes/timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('homeTimeline'));
|
this.connection = markRaw(stream.useChannel('homeTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
|
|
||||||
this.connection2 = markRaw(os.stream.useChannel('main'));
|
this.connection2 = markRaw(stream.useChannel('main'));
|
||||||
this.connection2.on('follow', onChangeFollowing);
|
this.connection2.on('follow', onChangeFollowing);
|
||||||
this.connection2.on('unfollow', onChangeFollowing);
|
this.connection2.on('unfollow', onChangeFollowing);
|
||||||
} else if (this.src == 'local') {
|
} else if (this.src == 'local') {
|
||||||
endpoint = 'notes/local-timeline';
|
endpoint = 'notes/local-timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('localTimeline'));
|
this.connection = markRaw(stream.useChannel('localTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
} else if (this.src == 'social') {
|
} else if (this.src == 'social') {
|
||||||
endpoint = 'notes/hybrid-timeline';
|
endpoint = 'notes/hybrid-timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('hybridTimeline'));
|
this.connection = markRaw(stream.useChannel('hybridTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
} else if (this.src == 'global') {
|
} else if (this.src == 'global') {
|
||||||
endpoint = 'notes/global-timeline';
|
endpoint = 'notes/global-timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('globalTimeline'));
|
this.connection = markRaw(stream.useChannel('globalTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
} else if (this.src == 'mentions') {
|
} else if (this.src == 'mentions') {
|
||||||
endpoint = 'notes/mentions';
|
endpoint = 'notes/mentions';
|
||||||
this.connection = markRaw(os.stream.useChannel('main'));
|
this.connection = markRaw(stream.useChannel('main'));
|
||||||
this.connection.on('mention', prepend);
|
this.connection.on('mention', prepend);
|
||||||
} else if (this.src == 'directs') {
|
} else if (this.src == 'directs') {
|
||||||
endpoint = 'notes/mentions';
|
endpoint = 'notes/mentions';
|
||||||
|
@ -130,14 +131,14 @@ export default defineComponent({
|
||||||
prepend(note);
|
prepend(note);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.connection = markRaw(os.stream.useChannel('main'));
|
this.connection = markRaw(stream.useChannel('main'));
|
||||||
this.connection.on('mention', onNote);
|
this.connection.on('mention', onNote);
|
||||||
} else if (this.src == 'list') {
|
} else if (this.src == 'list') {
|
||||||
endpoint = 'notes/user-list-timeline';
|
endpoint = 'notes/user-list-timeline';
|
||||||
this.query = {
|
this.query = {
|
||||||
listId: this.list
|
listId: this.list
|
||||||
};
|
};
|
||||||
this.connection = markRaw(os.stream.useChannel('userList', {
|
this.connection = markRaw(stream.useChannel('userList', {
|
||||||
listId: this.list
|
listId: this.list
|
||||||
}));
|
}));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
|
@ -148,7 +149,7 @@ export default defineComponent({
|
||||||
this.query = {
|
this.query = {
|
||||||
channelId: this.channel
|
channelId: this.channel
|
||||||
};
|
};
|
||||||
this.connection = markRaw(os.stream.useChannel('channel', {
|
this.connection = markRaw(stream.useChannel('channel', {
|
||||||
channelId: this.channel
|
channelId: this.channel
|
||||||
}));
|
}));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XModalWindow ref="dialog"
|
<XModalWindow ref="dialogEl"
|
||||||
:with-ok-button="true"
|
:with-ok-button="true"
|
||||||
:ok-button-disabled="selected == null"
|
:ok-button-disabled="selected == null"
|
||||||
@click="cancel()"
|
@click="cancel()"
|
||||||
|
@ -8,20 +8,20 @@
|
||||||
@closed="$emit('closed')"
|
@closed="$emit('closed')"
|
||||||
>
|
>
|
||||||
<template #header>{{ $ts.selectUser }}</template>
|
<template #header>{{ $ts.selectUser }}</template>
|
||||||
<div class="tbhwbxda _monolithic_">
|
<div class="tbhwbxda">
|
||||||
<div class="_section">
|
<div class="form">
|
||||||
<div class="_inputSplit">
|
<FormSplit :min-width="170">
|
||||||
<MkInput ref="username" v-model="username" class="input" @update:modelValue="search">
|
<MkInput ref="usernameEl" v-model="username" @update:modelValue="search">
|
||||||
<template #label>{{ $ts.username }}</template>
|
<template #label>{{ $ts.username }}</template>
|
||||||
<template #prefix>@</template>
|
<template #prefix>@</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkInput v-model="host" class="input" @update:modelValue="search">
|
<MkInput v-model="host" @update:modelValue="search">
|
||||||
<template #label>{{ $ts.host }}</template>
|
<template #label>{{ $ts.host }}</template>
|
||||||
<template #prefix>@</template>
|
<template #prefix>@</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
</FormSplit>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-if="username != '' || host != ''" class="result" :class="{ hit: users.length > 0 }">
|
||||||
<div v-if="username != '' || host != ''" class="_section result" :class="{ hit: users.length > 0 }">
|
|
||||||
<div v-if="users.length > 0" class="users">
|
<div v-if="users.length > 0" class="users">
|
||||||
<div v-for="user in users" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
|
<div v-for="user in users" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
|
||||||
<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
|
<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<span>{{ $ts.noUsers }}</span>
|
<span>{{ $ts.noUsers }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="username == '' && host == ''" class="_section recent">
|
<div v-if="username == '' && host == ''" class="recent">
|
||||||
<div class="users">
|
<div class="users">
|
||||||
<div v-for="user in recentUsers" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
|
<div v-for="user in recentUsers" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
|
||||||
<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
|
<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
|
||||||
|
@ -50,87 +50,89 @@
|
||||||
</XModalWindow>
|
</XModalWindow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { nextTick, onMounted } from 'vue';
|
||||||
import MkInput from './form/input.vue';
|
import * as misskey from 'misskey-js';
|
||||||
|
import MkInput from '@/components/form/input.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
export default defineComponent({
|
const emit = defineEmits<{
|
||||||
components: {
|
(e: 'ok', selected: misskey.entities.UserDetailed): void;
|
||||||
MkInput,
|
(e: 'cancel'): void;
|
||||||
XModalWindow,
|
(e: 'closed'): void;
|
||||||
},
|
}>();
|
||||||
|
|
||||||
props: {
|
let username = $ref('');
|
||||||
},
|
let host = $ref('');
|
||||||
|
let users: misskey.entities.UserDetailed[] = $ref([]);
|
||||||
|
let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
|
||||||
|
let selected: misskey.entities.UserDetailed | null = $ref(null);
|
||||||
|
let usernameEl: HTMLElement = $ref();
|
||||||
|
let dialogEl = $ref();
|
||||||
|
|
||||||
emits: ['ok', 'cancel', 'closed'],
|
const focus = () => {
|
||||||
|
if (usernameEl) {
|
||||||
|
usernameEl.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
data() {
|
const search = () => {
|
||||||
return {
|
if (username === '' && host === '') {
|
||||||
username: '',
|
users = [];
|
||||||
host: '',
|
|
||||||
recentUsers: [],
|
|
||||||
users: [],
|
|
||||||
selected: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async mounted() {
|
|
||||||
this.focus();
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.recentUsers = await os.api('users/show', {
|
|
||||||
userIds: this.$store.state.recentlyUsedUsers
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
search() {
|
|
||||||
if (this.username == '' && this.host == '') {
|
|
||||||
this.users = [];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
os.api('users/search-by-username-and-host', {
|
os.api('users/search-by-username-and-host', {
|
||||||
username: this.username,
|
username: username,
|
||||||
host: this.host,
|
host: host,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
detail: false
|
detail: false
|
||||||
}).then(users => {
|
}).then(_users => {
|
||||||
this.users = users;
|
users = _users;
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
focus() {
|
const ok = () => {
|
||||||
this.$refs.username.focus();
|
if (selected == null) return;
|
||||||
},
|
emit('ok', selected);
|
||||||
|
dialogEl.close();
|
||||||
ok() {
|
|
||||||
this.$emit('ok', this.selected);
|
|
||||||
this.$refs.dialog.close();
|
|
||||||
|
|
||||||
// 最近使ったユーザー更新
|
// 最近使ったユーザー更新
|
||||||
let recents = this.$store.state.recentlyUsedUsers;
|
let recents = defaultStore.state.recentlyUsedUsers;
|
||||||
recents = recents.filter(x => x !== this.selected.id);
|
recents = recents.filter(x => x !== selected.id);
|
||||||
recents.unshift(this.selected.id);
|
recents.unshift(selected.id);
|
||||||
this.$store.set('recentlyUsedUsers', recents.splice(0, 16));
|
defaultStore.set('recentlyUsedUsers', recents.splice(0, 16));
|
||||||
},
|
};
|
||||||
|
|
||||||
cancel() {
|
const cancel = () => {
|
||||||
this.$emit('cancel');
|
emit('cancel');
|
||||||
this.$refs.dialog.close();
|
dialogEl.close();
|
||||||
},
|
};
|
||||||
}
|
|
||||||
|
onMounted(() => {
|
||||||
|
focus();
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
os.api('users/show', {
|
||||||
|
userIds: defaultStore.state.recentlyUsedUsers,
|
||||||
|
}).then(users => {
|
||||||
|
recentUsers = users;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tbhwbxda {
|
.tbhwbxda {
|
||||||
> ._section {
|
> .form {
|
||||||
|
padding: 0 var(--root-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .result, > .recent {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
|
@ -26,7 +26,8 @@ import { router } from '@/router';
|
||||||
import { applyTheme } from '@/scripts/theme';
|
import { applyTheme } from '@/scripts/theme';
|
||||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
|
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { stream, confirm, alert, post, popup, toast } from '@/os';
|
import { confirm, alert, post, popup, toast } from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
|
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
|
||||||
import { defaultStore, ColdDeviceStorage } from '@/store';
|
import { defaultStore, ColdDeviceStorage } from '@/store';
|
||||||
|
|
|
@ -12,8 +12,6 @@ import { resolve } from '@/router';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
export const stream = markRaw(new Misskey.Stream(url, $i));
|
|
||||||
|
|
||||||
export const pendingApiRequestsCount = ref(0);
|
export const pendingApiRequestsCount = ref(0);
|
||||||
let apiRequestsCount = 0; // for debug
|
let apiRequestsCount = 0; // for debug
|
||||||
export const apiRequests = ref([]); // for debug
|
export const apiRequests = ref([]); // for debug
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<div class="_inputSplit _formBlock">
|
<FormSplit>
|
||||||
<MkKeyValue class="_formBlock">
|
<MkKeyValue class="_formBlock">
|
||||||
<template #key>{{ $ts.administrator }}</template>
|
<template #key>{{ $ts.administrator }}</template>
|
||||||
<template #value>{{ $instance.maintainerName }}</template>
|
<template #value>{{ $instance.maintainerName }}</template>
|
||||||
|
@ -33,14 +33,14 @@
|
||||||
<template #key>{{ $ts.contact }}</template>
|
<template #key>{{ $ts.contact }}</template>
|
||||||
<template #value>{{ $instance.maintainerEmail }}</template>
|
<template #value>{{ $instance.maintainerEmail }}</template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</div>
|
</FormSplit>
|
||||||
<FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ $ts.tos }}</FormLink>
|
<FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ $ts.tos }}</FormLink>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSuspense :p="initStats">
|
<FormSuspense :p="initStats">
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ $ts.statistics }}</template>
|
<template #label>{{ $ts.statistics }}</template>
|
||||||
<div class="_inputSplit">
|
<FormSplit>
|
||||||
<MkKeyValue class="_formBlock">
|
<MkKeyValue class="_formBlock">
|
||||||
<template #key>{{ $ts.users }}</template>
|
<template #key>{{ $ts.users }}</template>
|
||||||
<template #value>{{ number(stats.originalUsersCount) }}</template>
|
<template #value>{{ number(stats.originalUsersCount) }}</template>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<template #key>{{ $ts.notes }}</template>
|
<template #key>{{ $ts.notes }}</template>
|
||||||
<template #value>{{ number(stats.originalNotesCount) }}</template>
|
<template #value>{{ number(stats.originalNotesCount) }}</template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</div>
|
</FormSplit>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ import { version, instanceName } from '@/config';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import MkKeyValue from '@/components/key-value.vue';
|
import MkKeyValue from '@/components/key-value.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import number from '@/filters/number';
|
import number from '@/filters/number';
|
||||||
|
@ -85,6 +86,7 @@ export default defineComponent({
|
||||||
FormSection,
|
FormSection,
|
||||||
FormLink,
|
FormLink,
|
||||||
FormSuspense,
|
FormSuspense,
|
||||||
|
FormSplit,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -23,14 +23,14 @@
|
||||||
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
|
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
<div class="_inputSplit">
|
<FormSplit>
|
||||||
<MkInput v-model="ad.ratio" type="number">
|
<MkInput v-model="ad.ratio" type="number">
|
||||||
<template #label>{{ $ts.ratio }}</template>
|
<template #label>{{ $ts.ratio }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkInput v-model="ad.expiresAt" type="date">
|
<MkInput v-model="ad.expiresAt" type="date">
|
||||||
<template #label>{{ $ts.expiration }}</template>
|
<template #label>{{ $ts.expiration }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
</div>
|
</FormSplit>
|
||||||
<MkTextarea v-model="ad.memo" class="_formBlock">
|
<MkTextarea v-model="ad.memo" class="_formBlock">
|
||||||
<template #label>{{ $ts.memo }}</template>
|
<template #label>{{ $ts.memo }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
|
@ -49,6 +49,7 @@ import MkButton from '@/components/ui/button.vue';
|
||||||
import MkInput from '@/components/form/input.vue';
|
import MkInput from '@/components/form/input.vue';
|
||||||
import MkTextarea from '@/components/form/textarea.vue';
|
import MkTextarea from '@/components/form/textarea.vue';
|
||||||
import FormRadios from '@/components/form/radios.vue';
|
import FormRadios from '@/components/form/radios.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ export default defineComponent({
|
||||||
MkInput,
|
MkInput,
|
||||||
MkTextarea,
|
MkTextarea,
|
||||||
FormRadios,
|
FormRadios,
|
||||||
|
FormSplit,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
|
|
@ -1,50 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<FormBase>
|
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<FormSwitch v-model="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch>
|
<div class="_formRoot">
|
||||||
|
<FormSwitch v-model="enableEmail" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.enableEmail }}</template>
|
||||||
|
<template #caption>{{ $ts.emailConfigInfo }}</template>
|
||||||
|
</FormSwitch>
|
||||||
|
|
||||||
<template v-if="enableEmail">
|
<template v-if="enableEmail">
|
||||||
<FormInput v-model="email" type="email">
|
<FormInput v-model="email" type="email" class="_formBlock">
|
||||||
<span>{{ $ts.emailAddress }}</span>
|
<template #label>{{ $ts.emailAddress }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
|
<FormSection>
|
||||||
<div class="_debobigegoLabel">{{ $ts.smtpConfig }}</div>
|
<template #label>{{ $ts.smtpConfig }}</template>
|
||||||
<div class="main">
|
<FormSplit :min-width="280">
|
||||||
<FormInput v-model="smtpHost">
|
<FormInput v-model="smtpHost" class="_formBlock">
|
||||||
<span>{{ $ts.smtpHost }}</span>
|
<template #label>{{ $ts.smtpHost }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInput v-model="smtpPort" type="number">
|
<FormInput v-model="smtpPort" type="number" class="_formBlock">
|
||||||
<span>{{ $ts.smtpPort }}</span>
|
<template #label>{{ $ts.smtpPort }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInput v-model="smtpUser">
|
</FormSplit>
|
||||||
<span>{{ $ts.smtpUser }}</span>
|
<FormSplit :min-width="280">
|
||||||
|
<FormInput v-model="smtpUser" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.smtpUser }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInput v-model="smtpPass" type="password">
|
<FormInput v-model="smtpPass" type="password" class="_formBlock">
|
||||||
<span>{{ $ts.smtpPass }}</span>
|
<template #label>{{ $ts.smtpPass }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
<FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
|
</FormSplit>
|
||||||
<FormSwitch v-model="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch>
|
<FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
|
||||||
</div>
|
<FormSwitch v-model="smtpSecure" class="_formBlock">
|
||||||
</div>
|
<template #label>{{ $ts.smtpSecure }}</template>
|
||||||
|
<template #caption>{{ $ts.smtpSecureInfo }}</template>
|
||||||
<FormButton @click="testEmail">{{ $ts.testEmail }}</FormButton>
|
</FormSwitch>
|
||||||
|
</FormSection>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</FormBase>
|
</MkSpacer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
import FormSwitch from '@/components/form/switch.vue';
|
||||||
import FormInput from '@/components/debobigego/input.vue';
|
import FormInput from '@/components/form/input.vue';
|
||||||
import FormButton from '@/components/debobigego/button.vue';
|
import FormInfo from '@/components/ui/info.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import FormGroup from '@/components/debobigego/group.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import FormInfo from '@/components/debobigego/info.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
import { fetchInstance } from '@/instance';
|
import { fetchInstance } from '@/instance';
|
||||||
|
@ -53,9 +58,8 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FormSwitch,
|
FormSwitch,
|
||||||
FormInput,
|
FormInput,
|
||||||
FormBase,
|
FormSplit,
|
||||||
FormGroup,
|
FormSection,
|
||||||
FormButton,
|
|
||||||
FormInfo,
|
FormInfo,
|
||||||
FormSuspense,
|
FormSuspense,
|
||||||
},
|
},
|
||||||
|
@ -68,6 +72,16 @@ export default defineComponent({
|
||||||
title: this.$ts.emailServer,
|
title: this.$ts.emailServer,
|
||||||
icon: 'fas fa-envelope',
|
icon: 'fas fa-envelope',
|
||||||
bg: 'var(--bg)',
|
bg: 'var(--bg)',
|
||||||
|
actions: [{
|
||||||
|
asFullButton: true,
|
||||||
|
text: this.$ts.testEmail,
|
||||||
|
handler: this.testEmail,
|
||||||
|
}, {
|
||||||
|
asFullButton: true,
|
||||||
|
icon: 'fas fa-check',
|
||||||
|
text: this.$ts.save,
|
||||||
|
handler: this.save,
|
||||||
|
}],
|
||||||
},
|
},
|
||||||
enableEmail: false,
|
enableEmail: false,
|
||||||
email: null,
|
email: null,
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="tab === 'remote'" class="remote">
|
<div v-else-if="tab === 'remote'" class="remote">
|
||||||
<div class="_inputSplit">
|
<FormSplit>
|
||||||
<MkInput v-model="queryRemote" :debounce="true" type="search">
|
<MkInput v-model="queryRemote" :debounce="true" type="search">
|
||||||
<template #prefix><i class="fas fa-search"></i></template>
|
<template #prefix><i class="fas fa-search"></i></template>
|
||||||
<template #label>{{ $ts.search }}</template>
|
<template #label>{{ $ts.search }}</template>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<MkInput v-model="host" :debounce="true">
|
<MkInput v-model="host" :debounce="true">
|
||||||
<template #label>{{ $ts.host }}</template>
|
<template #label>{{ $ts.host }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
</div>
|
</FormSplit>
|
||||||
<MkPagination ref="remoteEmojis" :pagination="remotePagination">
|
<MkPagination ref="remoteEmojis" :pagination="remotePagination">
|
||||||
<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
|
<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
|
||||||
<template v-slot="{items}">
|
<template v-slot="{items}">
|
||||||
|
@ -57,6 +57,7 @@ import MkButton from '@/components/ui/button.vue';
|
||||||
import MkInput from '@/components/form/input.vue';
|
import MkInput from '@/components/form/input.vue';
|
||||||
import MkPagination from '@/components/ui/pagination.vue';
|
import MkPagination from '@/components/ui/pagination.vue';
|
||||||
import MkTab from '@/components/tab.vue';
|
import MkTab from '@/components/tab.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import { selectFiles } from '@/scripts/select-file';
|
import { selectFiles } from '@/scripts/select-file';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
@ -67,6 +68,7 @@ export default defineComponent({
|
||||||
MkButton,
|
MkButton,
|
||||||
MkInput,
|
MkInput,
|
||||||
MkPagination,
|
MkPagination,
|
||||||
|
FormSplit,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
<template>
|
|
||||||
<FormBase>
|
|
||||||
<FormSuspense :p="init">
|
|
||||||
<FormSwitch v-model="cacheRemoteFiles">
|
|
||||||
{{ $ts.cacheRemoteFiles }}
|
|
||||||
<template #desc>{{ $ts.cacheRemoteFilesDescription }}</template>
|
|
||||||
</FormSwitch>
|
|
||||||
|
|
||||||
<FormSwitch v-model="proxyRemoteFiles">
|
|
||||||
{{ $ts.proxyRemoteFiles }}
|
|
||||||
<template #desc>{{ $ts.proxyRemoteFilesDescription }}</template>
|
|
||||||
</FormSwitch>
|
|
||||||
|
|
||||||
<FormInput v-model="localDriveCapacityMb" type="number">
|
|
||||||
<span>{{ $ts.driveCapacityPerLocalAccount }}</span>
|
|
||||||
<template #suffix>MB</template>
|
|
||||||
<template #desc>{{ $ts.inMb }}</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">
|
|
||||||
<span>{{ $ts.driveCapacityPerRemoteAccount }}</span>
|
|
||||||
<template #suffix>MB</template>
|
|
||||||
<template #desc>{{ $ts.inMb }}</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
|
||||||
</FormSuspense>
|
|
||||||
</FormBase>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
|
||||||
import FormInput from '@/components/debobigego/input.vue';
|
|
||||||
import FormButton from '@/components/debobigego/button.vue';
|
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
|
||||||
import FormGroup from '@/components/debobigego/group.vue';
|
|
||||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import * as symbols from '@/symbols';
|
|
||||||
import { fetchInstance } from '@/instance';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
FormSwitch,
|
|
||||||
FormInput,
|
|
||||||
FormBase,
|
|
||||||
FormGroup,
|
|
||||||
FormButton,
|
|
||||||
FormSuspense,
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['info'],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
[symbols.PAGE_INFO]: {
|
|
||||||
title: this.$ts.files,
|
|
||||||
icon: 'fas fa-cloud',
|
|
||||||
bg: 'var(--bg)',
|
|
||||||
},
|
|
||||||
cacheRemoteFiles: false,
|
|
||||||
proxyRemoteFiles: false,
|
|
||||||
localDriveCapacityMb: 0,
|
|
||||||
remoteDriveCapacityMb: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async mounted() {
|
|
||||||
this.$emit('info', this[symbols.PAGE_INFO]);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async init() {
|
|
||||||
const meta = await os.api('meta', { detail: true });
|
|
||||||
this.cacheRemoteFiles = meta.cacheRemoteFiles;
|
|
||||||
this.proxyRemoteFiles = meta.proxyRemoteFiles;
|
|
||||||
this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
|
|
||||||
this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
|
|
||||||
},
|
|
||||||
save() {
|
|
||||||
os.apiWithDialog('admin/update-meta', {
|
|
||||||
cacheRemoteFiles: this.cacheRemoteFiles,
|
|
||||||
proxyRemoteFiles: this.proxyRemoteFiles,
|
|
||||||
localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
|
|
||||||
remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
|
|
||||||
}).then(() => {
|
|
||||||
fetchInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div v-if="!narrow || page == null" class="nav">
|
<div v-if="!narrow || page == null" class="nav">
|
||||||
<MkHeader :info="header"></MkHeader>
|
<MkHeader :info="header"></MkHeader>
|
||||||
|
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :content-max="700" :margin-min="16">
|
||||||
<div class="lxpfedzu">
|
<div class="lxpfedzu">
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
|
<img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
|
||||||
|
@ -162,11 +162,6 @@ export default defineComponent({
|
||||||
text: i18n.locale.general,
|
text: i18n.locale.general,
|
||||||
to: '/admin/settings',
|
to: '/admin/settings',
|
||||||
active: page.value === 'settings',
|
active: page.value === 'settings',
|
||||||
}, {
|
|
||||||
icon: 'fas fa-cloud',
|
|
||||||
text: i18n.locale.files,
|
|
||||||
to: '/admin/files-settings',
|
|
||||||
active: page.value === 'files-settings',
|
|
||||||
}, {
|
}, {
|
||||||
icon: 'fas fa-envelope',
|
icon: 'fas fa-envelope',
|
||||||
text: i18n.locale.emailServer,
|
text: i18n.locale.emailServer,
|
||||||
|
@ -236,7 +231,6 @@ export default defineComponent({
|
||||||
case 'database': return defineAsyncComponent(() => import('./database.vue'));
|
case 'database': return defineAsyncComponent(() => import('./database.vue'));
|
||||||
case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
|
case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
|
||||||
case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
|
case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
|
||||||
case 'files-settings': return defineAsyncComponent(() => import('./files-settings.vue'));
|
|
||||||
case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
|
case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
|
||||||
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
|
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
|
||||||
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
||||||
|
|
|
@ -101,6 +101,7 @@ const alpha = (hex, a) => {
|
||||||
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
||||||
};
|
};
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -119,7 +120,7 @@ export default defineComponent({
|
||||||
stats: null,
|
stats: null,
|
||||||
serverInfo: null,
|
serverInfo: null,
|
||||||
connection: null,
|
connection: null,
|
||||||
queueConnection: markRaw(os.stream.useChannel('queueStats')),
|
queueConnection: markRaw(stream.useChannel('queueStats')),
|
||||||
memUsage: 0,
|
memUsage: 0,
|
||||||
chartCpuMem: null,
|
chartCpuMem: null,
|
||||||
chartNet: null,
|
chartNet: null,
|
||||||
|
@ -150,7 +151,7 @@ export default defineComponent({
|
||||||
os.api('admin/server-info', {}).then(res => {
|
os.api('admin/server-info', {}).then(res => {
|
||||||
this.serverInfo = res;
|
this.serverInfo = res;
|
||||||
|
|
||||||
this.connection = markRaw(os.stream.useChannel('serverStats'));
|
this.connection = markRaw(stream.useChannel('serverStats'));
|
||||||
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', {
|
||||||
|
|
|
@ -1,76 +1,78 @@
|
||||||
<template>
|
<template>
|
||||||
<FormBase>
|
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<FormSwitch v-model="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch>
|
<div class="_formRoot">
|
||||||
|
<FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch>
|
||||||
|
|
||||||
<template v-if="useObjectStorage">
|
<template v-if="useObjectStorage">
|
||||||
<FormInput v-model="objectStorageBaseUrl">
|
<FormInput v-model="objectStorageBaseUrl" class="_formBlock">
|
||||||
<span>{{ $ts.objectStorageBaseUrl }}</span>
|
<template #label>{{ $ts.objectStorageBaseUrl }}</template>
|
||||||
<template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template>
|
<template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="objectStorageBucket">
|
<FormInput v-model="objectStorageBucket" class="_formBlock">
|
||||||
<span>{{ $ts.objectStorageBucket }}</span>
|
<template #label>{{ $ts.objectStorageBucket }}</template>
|
||||||
<template #desc>{{ $ts.objectStorageBucketDesc }}</template>
|
<template #caption>{{ $ts.objectStorageBucketDesc }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="objectStoragePrefix">
|
<FormInput v-model="objectStoragePrefix" class="_formBlock">
|
||||||
<span>{{ $ts.objectStoragePrefix }}</span>
|
<template #label>{{ $ts.objectStoragePrefix }}</template>
|
||||||
<template #desc>{{ $ts.objectStoragePrefixDesc }}</template>
|
<template #caption>{{ $ts.objectStoragePrefixDesc }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="objectStorageEndpoint">
|
<FormInput v-model="objectStorageEndpoint" class="_formBlock">
|
||||||
<span>{{ $ts.objectStorageEndpoint }}</span>
|
<template #label>{{ $ts.objectStorageEndpoint }}</template>
|
||||||
<template #desc>{{ $ts.objectStorageEndpointDesc }}</template>
|
<template #caption>{{ $ts.objectStorageEndpointDesc }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="objectStorageRegion">
|
<FormInput v-model="objectStorageRegion" class="_formBlock">
|
||||||
<span>{{ $ts.objectStorageRegion }}</span>
|
<template #label>{{ $ts.objectStorageRegion }}</template>
|
||||||
<template #desc>{{ $ts.objectStorageRegionDesc }}</template>
|
<template #caption>{{ $ts.objectStorageRegionDesc }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="objectStorageAccessKey">
|
<FormSplit :min-width="280">
|
||||||
|
<FormInput v-model="objectStorageAccessKey" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
<template #prefix><i class="fas fa-key"></i></template>
|
||||||
<span>Access key</span>
|
<template #label>Access key</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="objectStorageSecretKey">
|
<FormInput v-model="objectStorageSecretKey" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
<template #prefix><i class="fas fa-key"></i></template>
|
||||||
<span>Secret key</span>
|
<template #label>Secret key</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
</FormSplit>
|
||||||
|
|
||||||
<FormSwitch v-model="objectStorageUseSSL">
|
<FormSwitch v-model="objectStorageUseSSL" class="_formBlock">
|
||||||
{{ $ts.objectStorageUseSSL }}
|
<template #label>{{ $ts.objectStorageUseSSL }}</template>
|
||||||
<template #desc>{{ $ts.objectStorageUseSSLDesc }}</template>
|
<template #caption>{{ $ts.objectStorageUseSSLDesc }}</template>
|
||||||
</FormSwitch>
|
</FormSwitch>
|
||||||
|
|
||||||
<FormSwitch v-model="objectStorageUseProxy">
|
<FormSwitch v-model="objectStorageUseProxy" class="_formBlock">
|
||||||
{{ $ts.objectStorageUseProxy }}
|
<template #label>{{ $ts.objectStorageUseProxy }}</template>
|
||||||
<template #desc>{{ $ts.objectStorageUseProxyDesc }}</template>
|
<template #caption>{{ $ts.objectStorageUseProxyDesc }}</template>
|
||||||
</FormSwitch>
|
</FormSwitch>
|
||||||
|
|
||||||
<FormSwitch v-model="objectStorageSetPublicRead">
|
<FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock">
|
||||||
{{ $ts.objectStorageSetPublicRead }}
|
<template #label>{{ $ts.objectStorageSetPublicRead }}</template>
|
||||||
</FormSwitch>
|
</FormSwitch>
|
||||||
|
|
||||||
<FormSwitch v-model="objectStorageS3ForcePathStyle">
|
<FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock">
|
||||||
s3ForcePathStyle
|
<template #label>s3ForcePathStyle</template>
|
||||||
</FormSwitch>
|
</FormSwitch>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</FormBase>
|
</MkSpacer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
import FormSwitch from '@/components/form/switch.vue';
|
||||||
import FormInput from '@/components/debobigego/input.vue';
|
import FormInput from '@/components/form/input.vue';
|
||||||
import FormButton from '@/components/debobigego/button.vue';
|
import FormGroup from '@/components/form/group.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import FormGroup from '@/components/debobigego/group.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
import { fetchInstance } from '@/instance';
|
import { fetchInstance } from '@/instance';
|
||||||
|
@ -79,10 +81,10 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FormSwitch,
|
FormSwitch,
|
||||||
FormInput,
|
FormInput,
|
||||||
FormBase,
|
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormButton,
|
|
||||||
FormSuspense,
|
FormSuspense,
|
||||||
|
FormSplit,
|
||||||
|
FormSection,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
@ -93,6 +95,12 @@ export default defineComponent({
|
||||||
title: this.$ts.objectStorage,
|
title: this.$ts.objectStorage,
|
||||||
icon: 'fas fa-cloud',
|
icon: 'fas fa-cloud',
|
||||||
bg: 'var(--bg)',
|
bg: 'var(--bg)',
|
||||||
|
actions: [{
|
||||||
|
asFullButton: true,
|
||||||
|
icon: 'fas fa-check',
|
||||||
|
text: this.$ts.save,
|
||||||
|
handler: this.save,
|
||||||
|
}],
|
||||||
},
|
},
|
||||||
useObjectStorage: false,
|
useObjectStorage: false,
|
||||||
objectStorageBaseUrl: null,
|
objectStorageBaseUrl: null,
|
||||||
|
|
|
@ -81,6 +81,7 @@ import number from '@/filters/number';
|
||||||
import MkInstanceInfo from './instance.vue';
|
import MkInstanceInfo from './instance.vue';
|
||||||
import XMetrics from './metrics.vue';
|
import XMetrics from './metrics.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -113,7 +114,7 @@ export default defineComponent({
|
||||||
notesComparedToThePrevDay: null,
|
notesComparedToThePrevDay: null,
|
||||||
fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
|
fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
|
||||||
fetchModLogs: () => os.api('admin/show-moderation-logs', {}),
|
fetchModLogs: () => os.api('admin/show-moderation-logs', {}),
|
||||||
queueStatsConnection: markRaw(os.stream.useChannel('queueStats')),
|
queueStatsConnection: markRaw(stream.useChannel('queueStats')),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import XQueue from './queue.chart.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
import FormBase from '@/components/debobigego/base.vue';
|
||||||
import FormButton from '@/components/debobigego/button.vue';
|
import FormButton from '@/components/debobigego/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -36,7 +37,7 @@ export default defineComponent({
|
||||||
icon: 'fas fa-clipboard-list',
|
icon: 'fas fa-clipboard-list',
|
||||||
bg: 'var(--bg)',
|
bg: 'var(--bg)',
|
||||||
},
|
},
|
||||||
connection: markRaw(os.stream.useChannel('queueStats')),
|
connection: markRaw(stream.useChannel('queueStats')),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<FormBase>
|
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<FormLink to="/admin/bot-protection">
|
<div class="_formRoot">
|
||||||
|
<FormLink to="/admin/bot-protection" class="_formBlock">
|
||||||
<i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}
|
<i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}
|
||||||
<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
|
<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
|
||||||
<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
|
<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
|
||||||
<template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
|
<template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
|
||||||
</FormLink>
|
</FormLink>
|
||||||
|
</div>
|
||||||
<FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch>
|
|
||||||
|
|
||||||
<FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch>
|
|
||||||
|
|
||||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</FormBase>
|
</MkSpacer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||||
import FormLink from '@/components/debobigego/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
import FormSwitch from '@/components/form/switch.vue';
|
||||||
import FormButton from '@/components/debobigego/button.vue';
|
import FormInfo from '@/components/ui/info.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import FormGroup from '@/components/debobigego/group.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import FormInfo from '@/components/debobigego/info.vue';
|
|
||||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
import { fetchInstance } from '@/instance';
|
import { fetchInstance } from '@/instance';
|
||||||
|
@ -34,10 +28,8 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FormLink,
|
FormLink,
|
||||||
FormSwitch,
|
FormSwitch,
|
||||||
FormBase,
|
|
||||||
FormGroup,
|
|
||||||
FormButton,
|
|
||||||
FormInfo,
|
FormInfo,
|
||||||
|
FormSection,
|
||||||
FormSuspense,
|
FormSuspense,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -52,8 +44,6 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
enableHcaptcha: false,
|
enableHcaptcha: false,
|
||||||
enableRecaptcha: false,
|
enableRecaptcha: false,
|
||||||
enableRegistration: false,
|
|
||||||
emailRequiredForSignup: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -66,18 +56,7 @@ export default defineComponent({
|
||||||
const meta = await os.api('meta', { detail: true });
|
const meta = await os.api('meta', { detail: true });
|
||||||
this.enableHcaptcha = meta.enableHcaptcha;
|
this.enableHcaptcha = meta.enableHcaptcha;
|
||||||
this.enableRecaptcha = meta.enableRecaptcha;
|
this.enableRecaptcha = meta.enableRecaptcha;
|
||||||
this.enableRegistration = !meta.disableRegistration;
|
|
||||||
this.emailRequiredForSignup = meta.emailRequiredForSignup;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
save() {
|
|
||||||
os.apiWithDialog('admin/update-meta', {
|
|
||||||
disableRegistration: !this.enableRegistration,
|
|
||||||
emailRequiredForSignup: this.emailRequiredForSignup,
|
|
||||||
}).then(() => {
|
|
||||||
fetchInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,72 +1,113 @@
|
||||||
<template>
|
<template>
|
||||||
<FormBase>
|
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<FormInput v-model="name">
|
<div class="_formRoot">
|
||||||
<span>{{ $ts.instanceName }}</span>
|
<FormInput v-model="name" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.instanceName }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormTextarea v-model="description">
|
<FormTextarea v-model="description" class="_formBlock">
|
||||||
<span>{{ $ts.instanceDescription }}</span>
|
<template #label>{{ $ts.instanceDescription }}</template>
|
||||||
</FormTextarea>
|
</FormTextarea>
|
||||||
|
|
||||||
<FormInput v-model="iconUrl">
|
<FormInput v-model="iconUrl" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-link"></i></template>
|
<template #prefix><i class="fas fa-link"></i></template>
|
||||||
<span>{{ $ts.iconUrl }}</span>
|
<template #label>{{ $ts.iconUrl }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="bannerUrl">
|
<FormInput v-model="bannerUrl" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-link"></i></template>
|
<template #prefix><i class="fas fa-link"></i></template>
|
||||||
<span>{{ $ts.bannerUrl }}</span>
|
<template #label>{{ $ts.bannerUrl }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="backgroundImageUrl">
|
<FormInput v-model="backgroundImageUrl" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-link"></i></template>
|
<template #prefix><i class="fas fa-link"></i></template>
|
||||||
<span>{{ $ts.backgroundImageUrl }}</span>
|
<template #label>{{ $ts.backgroundImageUrl }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="tosUrl">
|
<FormInput v-model="tosUrl" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-link"></i></template>
|
<template #prefix><i class="fas fa-link"></i></template>
|
||||||
<span>{{ $ts.tosUrl }}</span>
|
<template #label>{{ $ts.tosUrl }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="maintainerName">
|
<FormSplit :min-width="300">
|
||||||
<span>{{ $ts.maintainerName }}</span>
|
<FormInput v-model="maintainerName" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.maintainerName }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormInput v-model="maintainerEmail" type="email">
|
<FormInput v-model="maintainerEmail" type="email" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-envelope"></i></template>
|
<template #prefix><i class="fas fa-envelope"></i></template>
|
||||||
<span>{{ $ts.maintainerEmail }}</span>
|
<template #label>{{ $ts.maintainerEmail }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
</FormSplit>
|
||||||
|
|
||||||
<FormTextarea v-model="pinnedUsers">
|
<FormTextarea v-model="pinnedUsers" class="_formBlock">
|
||||||
<span>{{ $ts.pinnedUsers }}</span>
|
<template #label>{{ $ts.pinnedUsers }}</template>
|
||||||
<template #desc>{{ $ts.pinnedUsersDescription }}</template>
|
<template #caption>{{ $ts.pinnedUsersDescription }}</template>
|
||||||
</FormTextarea>
|
</FormTextarea>
|
||||||
|
|
||||||
<FormInput v-model="maxNoteTextLength" type="number">
|
<FormInput v-model="maxNoteTextLength" type="number" class="_formBlock">
|
||||||
<template #prefix><i class="fas fa-pencil-alt"></i></template>
|
<template #prefix><i class="fas fa-pencil-alt"></i></template>
|
||||||
<span>{{ $ts.maxNoteTextLength }}</span>
|
<template #label>{{ $ts.maxNoteTextLength }}</template>
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
<FormSwitch v-model="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch>
|
<FormSection>
|
||||||
<FormSwitch v-model="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch>
|
<FormSwitch v-model="enableRegistration" class="_formBlock">
|
||||||
<FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo>
|
<template #label>{{ $ts.enableRegistration }}</template>
|
||||||
|
</FormSwitch>
|
||||||
|
|
||||||
<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
<FormSwitch v-model="emailRequiredForSignup" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.emailRequiredForSignup }}</template>
|
||||||
|
</FormSwitch>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
|
<FormSection>
|
||||||
|
<FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch>
|
||||||
|
<FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch>
|
||||||
|
<FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
|
<FormSection>
|
||||||
|
<template #label>{{ $ts.files }}</template>
|
||||||
|
|
||||||
|
<FormSwitch v-model="cacheRemoteFiles" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.cacheRemoteFiles }}</template>
|
||||||
|
<template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
|
||||||
|
</FormSwitch>
|
||||||
|
|
||||||
|
<FormSwitch v-model="proxyRemoteFiles" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.proxyRemoteFiles }}</template>
|
||||||
|
<template #caption>{{ $ts.proxyRemoteFilesDescription }}</template>
|
||||||
|
</FormSwitch>
|
||||||
|
|
||||||
|
<FormSplit :min-width="280">
|
||||||
|
<FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
|
||||||
|
<template #suffix>MB</template>
|
||||||
|
<template #caption>{{ $ts.inMb }}</template>
|
||||||
|
</FormInput>
|
||||||
|
|
||||||
|
<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock">
|
||||||
|
<template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template>
|
||||||
|
<template #suffix>MB</template>
|
||||||
|
<template #caption>{{ $ts.inMb }}</template>
|
||||||
|
</FormInput>
|
||||||
|
</FormSplit>
|
||||||
|
</FormSection>
|
||||||
|
</div>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</FormBase>
|
</MkSpacer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import FormSwitch from '@/components/debobigego/switch.vue';
|
import FormSwitch from '@/components/form/switch.vue';
|
||||||
import FormInput from '@/components/debobigego/input.vue';
|
import FormInput from '@/components/form/input.vue';
|
||||||
import FormButton from '@/components/debobigego/button.vue';
|
import FormTextarea from '@/components/form/textarea.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
import FormInfo from '@/components/ui/info.vue';
|
||||||
import FormGroup from '@/components/debobigego/group.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import FormTextarea from '@/components/debobigego/textarea.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import FormInfo from '@/components/debobigego/info.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import FormSuspense from '@/components/debobigego/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
import { fetchInstance } from '@/instance';
|
import { fetchInstance } from '@/instance';
|
||||||
|
@ -75,12 +116,11 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FormSwitch,
|
FormSwitch,
|
||||||
FormInput,
|
FormInput,
|
||||||
FormBase,
|
FormSuspense,
|
||||||
FormGroup,
|
|
||||||
FormButton,
|
|
||||||
FormTextarea,
|
FormTextarea,
|
||||||
FormInfo,
|
FormInfo,
|
||||||
FormSuspense,
|
FormSection,
|
||||||
|
FormSplit,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
@ -91,6 +131,12 @@ export default defineComponent({
|
||||||
title: this.$ts.general,
|
title: this.$ts.general,
|
||||||
icon: 'fas fa-cog',
|
icon: 'fas fa-cog',
|
||||||
bg: 'var(--bg)',
|
bg: 'var(--bg)',
|
||||||
|
actions: [{
|
||||||
|
asFullButton: true,
|
||||||
|
icon: 'fas fa-check',
|
||||||
|
text: this.$ts.save,
|
||||||
|
handler: this.save,
|
||||||
|
}],
|
||||||
},
|
},
|
||||||
name: null,
|
name: null,
|
||||||
description: null,
|
description: null,
|
||||||
|
@ -104,6 +150,12 @@ export default defineComponent({
|
||||||
enableLocalTimeline: false,
|
enableLocalTimeline: false,
|
||||||
enableGlobalTimeline: false,
|
enableGlobalTimeline: false,
|
||||||
pinnedUsers: '',
|
pinnedUsers: '',
|
||||||
|
cacheRemoteFiles: false,
|
||||||
|
proxyRemoteFiles: false,
|
||||||
|
localDriveCapacityMb: 0,
|
||||||
|
remoteDriveCapacityMb: 0,
|
||||||
|
enableRegistration: false,
|
||||||
|
emailRequiredForSignup: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -126,6 +178,12 @@ export default defineComponent({
|
||||||
this.enableLocalTimeline = !meta.disableLocalTimeline;
|
this.enableLocalTimeline = !meta.disableLocalTimeline;
|
||||||
this.enableGlobalTimeline = !meta.disableGlobalTimeline;
|
this.enableGlobalTimeline = !meta.disableGlobalTimeline;
|
||||||
this.pinnedUsers = meta.pinnedUsers.join('\n');
|
this.pinnedUsers = meta.pinnedUsers.join('\n');
|
||||||
|
this.cacheRemoteFiles = meta.cacheRemoteFiles;
|
||||||
|
this.proxyRemoteFiles = meta.proxyRemoteFiles;
|
||||||
|
this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
|
||||||
|
this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
|
||||||
|
this.enableRegistration = !meta.disableRegistration;
|
||||||
|
this.emailRequiredForSignup = meta.emailRequiredForSignup;
|
||||||
},
|
},
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
|
@ -142,6 +200,12 @@ export default defineComponent({
|
||||||
disableLocalTimeline: !this.enableLocalTimeline,
|
disableLocalTimeline: !this.enableLocalTimeline,
|
||||||
disableGlobalTimeline: !this.enableGlobalTimeline,
|
disableGlobalTimeline: !this.enableGlobalTimeline,
|
||||||
pinnedUsers: this.pinnedUsers.split('\n'),
|
pinnedUsers: this.pinnedUsers.split('\n'),
|
||||||
|
cacheRemoteFiles: this.cacheRemoteFiles,
|
||||||
|
proxyRemoteFiles: this.proxyRemoteFiles,
|
||||||
|
localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
|
||||||
|
remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
|
||||||
|
disableRegistration: !this.enableRegistration,
|
||||||
|
emailRequiredForSignup: this.emailRequiredForSignup,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<template #prefix><i class="fas fa-search"></i></template>
|
<template #prefix><i class="fas fa-search"></i></template>
|
||||||
<template #label>{{ $ts.host }}</template>
|
<template #label>{{ $ts.host }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<div class="_inputSplit" style="margin-top: var(--margin);">
|
<FormSplit style="margin-top: var(--margin);">
|
||||||
<MkSelect v-model="state">
|
<MkSelect v-model="state">
|
||||||
<template #label>{{ $ts.state }}</template>
|
<template #label>{{ $ts.state }}</template>
|
||||||
<option value="all">{{ $ts.all }}</option>
|
<option value="all">{{ $ts.all }}</option>
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
<option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
|
<option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
|
||||||
<option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
|
<option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
</div>
|
</FormSplit>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
||||||
|
@ -101,6 +101,7 @@ import MkButton from '@/components/ui/button.vue';
|
||||||
import MkInput from '@/components/form/input.vue';
|
import MkInput from '@/components/form/input.vue';
|
||||||
import MkSelect from '@/components/form/select.vue';
|
import MkSelect from '@/components/form/select.vue';
|
||||||
import MkPagination from '@/components/ui/pagination.vue';
|
import MkPagination from '@/components/ui/pagination.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
|
||||||
|
@ -110,6 +111,7 @@ export default defineComponent({
|
||||||
MkInput,
|
MkInput,
|
||||||
MkSelect,
|
MkSelect,
|
||||||
MkPagination,
|
MkPagination,
|
||||||
|
FormSplit,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
|
|
@ -44,6 +44,7 @@ import * as Acct from 'misskey-js/built/acct';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
import MkButton from '@/components/ui/button.vue';
|
||||||
import { acct } from '@/filters/user';
|
import { acct } from '@/filters/user';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -66,7 +67,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = markRaw(os.stream.useChannel('messagingIndex'));
|
this.connection = markRaw(stream.useChannel('messagingIndex'));
|
||||||
|
|
||||||
this.connection.on('message', this.onMessage);
|
this.connection.on('message', this.onMessage);
|
||||||
this.connection.on('read', this.onRead);
|
this.connection.on('read', this.onRead);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import * as autosize from 'autosize';
|
||||||
import { formatTimeString } from '@/scripts/format-time-string';
|
import { formatTimeString } from '@/scripts/format-time-string';
|
||||||
import { selectFile } from '@/scripts/select-file';
|
import { selectFile } from '@/scripts/select-file';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { Autocomplete } from '@/scripts/autocomplete';
|
import { Autocomplete } from '@/scripts/autocomplete';
|
||||||
import { throttle } from 'throttle-debounce';
|
import { throttle } from 'throttle-debounce';
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ export default defineComponent({
|
||||||
file: null,
|
file: null,
|
||||||
sending: false,
|
sending: false,
|
||||||
typing: throttle(3000, () => {
|
typing: throttle(3000, () => {
|
||||||
os.stream.send('typingOnMessaging', this.user ? { partner: this.user.id } : { group: this.group.id });
|
stream.send('typingOnMessaging', this.user ? { partner: this.user.id } : { group: this.group.id });
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,6 +43,7 @@ import XForm from './messaging-room.form.vue';
|
||||||
import * as Acct from 'misskey-js/built/acct';
|
import * as Acct from 'misskey-js/built/acct';
|
||||||
import { isBottom, onScrollBottom, scroll } from '@/scripts/scroll';
|
import { isBottom, onScrollBottom, scroll } from '@/scripts/scroll';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { popout } from '@/scripts/popout';
|
import { popout } from '@/scripts/popout';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
@ -141,7 +142,7 @@ const Component = defineComponent({
|
||||||
this.group = group;
|
this.group = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connection = markRaw(os.stream.useChannel('messaging', {
|
this.connection = markRaw(stream.useChannel('messaging', {
|
||||||
otherparty: this.user ? this.user.id : undefined,
|
otherparty: this.user ? this.user.id : undefined,
|
||||||
group: this.group ? this.group.id : undefined,
|
group: this.group ? this.group.id : undefined,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { defineComponent, markRaw } from 'vue';
|
||||||
import GameSetting from './game.setting.vue';
|
import GameSetting from './game.setting.vue';
|
||||||
import GameBoard from './game.board.vue';
|
import GameBoard from './game.board.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -61,7 +62,7 @@ export default defineComponent({
|
||||||
if (this.connection) {
|
if (this.connection) {
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
}
|
}
|
||||||
this.connection = markRaw(os.stream.useChannel('gamesReversiGame', {
|
this.connection = markRaw(stream.useChannel('gamesReversiGame', {
|
||||||
gameId: this.game.id
|
gameId: this.game.id
|
||||||
}));
|
}));
|
||||||
this.connection.on('started', this.onStarted);
|
this.connection.on('started', this.onStarted);
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, markRaw } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
import MkButton from '@/components/ui/button.vue';
|
||||||
import MkFolder from '@/components/ui/folder.vue';
|
import MkFolder from '@/components/ui/folder.vue';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
@ -92,7 +93,7 @@ export default defineComponent({
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection = markRaw(os.stream.useChannel('gamesReversi'));
|
this.connection = markRaw(stream.useChannel('gamesReversi'));
|
||||||
|
|
||||||
this.connection.on('invited', this.onInvited);
|
this.connection.on('invited', this.onInvited);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="_formBlock uawsfosz">
|
<div class="_formBlock uawsfosz">
|
||||||
<div class="meter"><div :style="meterStyle"></div></div>
|
<div class="meter"><div :style="meterStyle"></div></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="_inputSplit _formBlock">
|
<FormSplit>
|
||||||
<MkKeyValue class="_formBlock">
|
<MkKeyValue class="_formBlock">
|
||||||
<template #key>{{ $ts.capacity }}</template>
|
<template #key>{{ $ts.capacity }}</template>
|
||||||
<template #value>{{ bytes(capacity, 1) }}</template>
|
<template #value>{{ bytes(capacity, 1) }}</template>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<template #key>{{ $ts.inUse }}</template>
|
<template #key>{{ $ts.inUse }}</template>
|
||||||
<template #value>{{ bytes(usage, 1) }}</template>
|
<template #value>{{ bytes(usage, 1) }}</template>
|
||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</div>
|
</FormSplit>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
@ -38,6 +38,7 @@ import * as tinycolor from 'tinycolor2';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import MkKeyValue from '@/components/key-value.vue';
|
import MkKeyValue from '@/components/key-value.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import bytes from '@/filters/bytes';
|
import bytes from '@/filters/bytes';
|
||||||
import * as symbols from '@/symbols';
|
import * as symbols from '@/symbols';
|
||||||
|
@ -49,6 +50,7 @@ export default defineComponent({
|
||||||
FormLink,
|
FormLink,
|
||||||
FormSection,
|
FormSection,
|
||||||
MkKeyValue,
|
MkKeyValue,
|
||||||
|
FormSplit,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import FormTextarea from '@/components/form/textarea.vue';
|
import FormTextarea from '@/components/form/textarea.vue';
|
||||||
import FormRadios from '@/components/form/radios.vue';
|
import FormRadios from '@/components/form/radios.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
|
||||||
import FormButton from '@/components/ui/button.vue';
|
import FormButton from '@/components/ui/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { menuDef } from '@/menu';
|
import { menuDef } from '@/menu';
|
||||||
|
@ -31,7 +30,6 @@ import { unisonReload } from '@/scripts/unison-reload';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FormBase,
|
|
||||||
FormButton,
|
FormButton,
|
||||||
FormTextarea,
|
FormTextarea,
|
||||||
FormRadios,
|
FormRadios,
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import FormButton from '@/components/ui/button.vue';
|
import FormButton from '@/components/ui/button.vue';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormBase from '@/components/debobigego/base.vue';
|
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import { notificationTypes } from 'misskey-js';
|
import { notificationTypes } from 'misskey-js';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
@ -21,7 +20,6 @@ import * as symbols from '@/symbols';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FormBase,
|
|
||||||
FormLink,
|
FormLink,
|
||||||
FormButton,
|
FormButton,
|
||||||
FormSection,
|
FormSection,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { onUnmounted, Ref, ref, watch } from 'vue';
|
import { onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { $i } from './account';
|
import { $i } from './account';
|
||||||
import { api } from './os';
|
import { api } from './os';
|
||||||
|
import { stream } from './stream';
|
||||||
|
|
||||||
type StateDef = Record<string, {
|
type StateDef = Record<string, {
|
||||||
where: 'account' | 'device' | 'deviceAccount';
|
where: 'account' | 'device' | 'deviceAccount';
|
||||||
|
@ -9,6 +10,8 @@ type StateDef = Record<string, {
|
||||||
|
|
||||||
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
||||||
|
|
||||||
|
const connection = $i && stream.useChannel('main');
|
||||||
|
|
||||||
export class Storage<T extends StateDef> {
|
export class Storage<T extends StateDef> {
|
||||||
public readonly key: string;
|
public readonly key: string;
|
||||||
public readonly keyForLocalStorage: string;
|
public readonly keyForLocalStorage: string;
|
||||||
|
@ -69,8 +72,19 @@ export class Storage<T extends StateDef> {
|
||||||
localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache));
|
localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache));
|
||||||
});
|
});
|
||||||
}, 1);
|
}, 1);
|
||||||
|
// streamingのuser storage updateイベントを監視して更新
|
||||||
|
connection?.on('registryUpdated', ({ scope, key, value }: { scope: string[], key: keyof T, value: T[typeof key]['default'] }) => {
|
||||||
|
if (scope.length !== 2 || scope[0] !== 'client' || scope[1] !== this.key || this.state[key] === value) return;
|
||||||
|
|
||||||
// TODO: streamingのuser storage updateイベントを監視して更新
|
this.state[key] = value;
|
||||||
|
this.reactiveState[key].value = value;
|
||||||
|
|
||||||
|
const cache = JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::cache::' + $i.id) || '{}');
|
||||||
|
if (cache[key] !== value) {
|
||||||
|
cache[key] = value;
|
||||||
|
localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
import { DriveFile } from 'misskey-js/built/entities';
|
import { DriveFile } from 'misskey-js/built/entities';
|
||||||
|
@ -48,7 +49,7 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
|
||||||
|
|
||||||
const marker = Math.random().toString(); // TODO: UUIDとか使う
|
const marker = Math.random().toString(); // TODO: UUIDとか使う
|
||||||
|
|
||||||
const connection = os.stream.useChannel('main');
|
const connection = stream.useChannel('main');
|
||||||
connection.on('urlUploadFinished', data => {
|
connection.on('urlUploadFinished', data => {
|
||||||
if (data.marker === marker) {
|
if (data.marker === marker) {
|
||||||
res(multiple ? [data.file] : data.file);
|
res(multiple ? [data.file] : data.file);
|
||||||
|
|
6
packages/client/src/stream.ts
Normal file
6
packages/client/src/stream.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
import { $i } from '@/account';
|
||||||
|
import { url } from '@/config';
|
||||||
|
|
||||||
|
export const stream = markRaw(new Misskey.Stream(url, $i));
|
|
@ -386,16 +386,6 @@ hr {
|
||||||
backdrop-filter: var(--blur, blur(15px));
|
backdrop-filter: var(--blur, blur(15px));
|
||||||
}
|
}
|
||||||
|
|
||||||
._inputSplit {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
|
|
||||||
grid-gap: 12px;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
._formBlock {
|
._formBlock {
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||||
import { stream, popup, popups, uploads, pendingApiRequestsCount } from '@/os';
|
import { popup, popups, uploads, pendingApiRequestsCount } from '@/os';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { swInject } from './sw-inject';
|
import { swInject } from './sw-inject';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
|
@ -20,14 +21,14 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
stream() {
|
stream() {
|
||||||
return os.stream;
|
return stream;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
os.stream.on('_disconnected_', this.onDisconnected);
|
stream.on('_disconnected_', this.onDisconnected);
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
os.stream.off('_disconnected_', this.onDisconnected);
|
stream.off('_disconnected_', this.onDisconnected);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onDisconnected() {
|
onDisconnected() {
|
||||||
|
|
|
@ -118,6 +118,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||||
import { checkWordMute } from '@/scripts/check-word-mute';
|
import { checkWordMute } from '@/scripts/check-word-mute';
|
||||||
import { userPage } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { noteActions, noteViewInterruptors } from '@/store';
|
import { noteActions, noteViewInterruptors } from '@/store';
|
||||||
import { reactionPicker } from '@/scripts/reaction-picker';
|
import { reactionPicker } from '@/scripts/reaction-picker';
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
||||||
|
@ -243,7 +244,7 @@ export default defineComponent({
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
if (this.$i) {
|
if (this.$i) {
|
||||||
this.connection = os.stream;
|
this.connection = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.collapsed = this.appearNote.cw == null && this.appearNote.text && (
|
this.collapsed = this.appearNote.cw == null && this.appearNote.text && (
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { computed, defineComponent, markRaw } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import XNotes from '../notes.vue';
|
import XNotes from '../notes.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
import { scrollToBottom, getScrollPosition, getScrollContainer } from '@/scripts/scroll';
|
import { scrollToBottom, getScrollPosition, getScrollContainer } from '@/scripts/scroll';
|
||||||
import follow from '@/directives/follow-append';
|
import follow from '@/directives/follow-append';
|
||||||
|
@ -106,7 +107,7 @@ export default defineComponent({
|
||||||
sound.play(note.userId === this.$i.id ? 'noteMy' : 'note');
|
sound.play(note.userId === this.$i.id ? 'noteMy' : 'note');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.connection = markRaw(os.stream.useChannel('channel', {
|
this.connection = markRaw(stream.useChannel('channel', {
|
||||||
channelId: this.channelId
|
channelId: this.channelId
|
||||||
}));
|
}));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import { computed, defineComponent, markRaw } from 'vue';
|
import { computed, defineComponent, markRaw } from 'vue';
|
||||||
import XNotes from '../notes.vue';
|
import XNotes from '../notes.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
import { scrollToBottom, getScrollPosition, getScrollContainer } from '@/scripts/scroll';
|
import { scrollToBottom, getScrollPosition, getScrollContainer } from '@/scripts/scroll';
|
||||||
import follow from '@/directives/follow-append';
|
import follow from '@/directives/follow-append';
|
||||||
|
@ -90,23 +91,23 @@ export default defineComponent({
|
||||||
|
|
||||||
if (this.src == 'home') {
|
if (this.src == 'home') {
|
||||||
endpoint = 'notes/timeline';
|
endpoint = 'notes/timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('homeTimeline'));
|
this.connection = markRaw(stream.useChannel('homeTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
|
|
||||||
this.connection2 = markRaw(os.stream.useChannel('main'));
|
this.connection2 = markRaw(stream.useChannel('main'));
|
||||||
this.connection2.on('follow', onChangeFollowing);
|
this.connection2.on('follow', onChangeFollowing);
|
||||||
this.connection2.on('unfollow', onChangeFollowing);
|
this.connection2.on('unfollow', onChangeFollowing);
|
||||||
} else if (this.src == 'local') {
|
} else if (this.src == 'local') {
|
||||||
endpoint = 'notes/local-timeline';
|
endpoint = 'notes/local-timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('localTimeline'));
|
this.connection = markRaw(stream.useChannel('localTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
} else if (this.src == 'social') {
|
} else if (this.src == 'social') {
|
||||||
endpoint = 'notes/hybrid-timeline';
|
endpoint = 'notes/hybrid-timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('hybridTimeline'));
|
this.connection = markRaw(stream.useChannel('hybridTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
} else if (this.src == 'global') {
|
} else if (this.src == 'global') {
|
||||||
endpoint = 'notes/global-timeline';
|
endpoint = 'notes/global-timeline';
|
||||||
this.connection = markRaw(os.stream.useChannel('globalTimeline'));
|
this.connection = markRaw(stream.useChannel('globalTimeline'));
|
||||||
this.connection.on('note', prepend);
|
this.connection.on('note', prepend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ import * as Acct from 'misskey-js/built/acct';
|
||||||
import { formatTimeString } from '@/scripts/format-time-string';
|
import { formatTimeString } from '@/scripts/format-time-string';
|
||||||
import { Autocomplete } from '@/scripts/autocomplete';
|
import { Autocomplete } from '@/scripts/autocomplete';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import { selectFiles } from '@/scripts/select-file';
|
import { selectFiles } from '@/scripts/select-file';
|
||||||
import { notePostInterruptors, postFormActions } from '@/store';
|
import { notePostInterruptors, postFormActions } from '@/store';
|
||||||
import { throttle } from 'throttle-debounce';
|
import { throttle } from 'throttle-debounce';
|
||||||
|
@ -130,7 +131,7 @@ export default defineComponent({
|
||||||
imeText: '',
|
imeText: '',
|
||||||
typing: throttle(3000, () => {
|
typing: throttle(3000, () => {
|
||||||
if (this.channel) {
|
if (this.channel) {
|
||||||
os.stream.send('typingOnChannel', { channel: this.channel });
|
stream.send('typingOnChannel', { channel: this.channel });
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
postFormActions,
|
postFormActions,
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
import { defineComponent, markRaw } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import define from './define';
|
import define from './define';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
import number from '@/filters/number';
|
import number from '@/filters/number';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ export default defineComponent({
|
||||||
extends: widget,
|
extends: widget,
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
connection: markRaw(os.stream.useChannel('queueStats')),
|
connection: markRaw(stream.useChannel('queueStats')),
|
||||||
inbox: {
|
inbox: {
|
||||||
activeSincePrevTick: 0,
|
activeSincePrevTick: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import MkContainer from '@/components/ui/container.vue';
|
||||||
import define from './define';
|
import define from './define';
|
||||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
const widget = define({
|
const widget = define({
|
||||||
name: 'photos',
|
name: 'photos',
|
||||||
|
@ -48,7 +49,7 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = markRaw(os.stream.useChannel('main'));
|
this.connection = markRaw(stream.useChannel('main'));
|
||||||
|
|
||||||
this.connection.on('driveFileCreated', this.onDriveFileCreated);
|
this.connection.on('driveFileCreated', this.onDriveFileCreated);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import XCpu from './cpu.vue';
|
||||||
import XMemory from './mem.vue';
|
import XMemory from './mem.vue';
|
||||||
import XDisk from './disk.vue';
|
import XDisk from './disk.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { stream } from '@/stream';
|
||||||
|
|
||||||
const widget = define({
|
const widget = define({
|
||||||
name: 'serverMetric',
|
name: 'serverMetric',
|
||||||
|
@ -63,7 +64,7 @@ export default defineComponent({
|
||||||
os.api('server-info', {}).then(res => {
|
os.api('server-info', {}).then(res => {
|
||||||
this.meta = res;
|
this.meta = res;
|
||||||
});
|
});
|
||||||
this.connection = markRaw(os.stream.useChannel('serverStats'));
|
this.connection = markRaw(stream.useChannel('serverStats'));
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
|
|
|
@ -46,6 +46,7 @@ module.exports = {
|
||||||
loader: 'vue-loader',
|
loader: 'vue-loader',
|
||||||
options: {
|
options: {
|
||||||
cssSourceMap: false,
|
cssSourceMap: false,
|
||||||
|
reactivityTransform: true,
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
preserveWhitespace: false
|
preserveWhitespace: false
|
||||||
}
|
}
|
||||||
|
|
|
@ -6162,10 +6162,10 @@ vue-eslint-parser@^8.0.1:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
semver "^7.3.5"
|
semver "^7.3.5"
|
||||||
|
|
||||||
vue-loader@16.8.3:
|
vue-loader@17.0.0:
|
||||||
version "16.8.3"
|
version "17.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-16.8.3.tgz#d43e675def5ba9345d6c7f05914c13d861997087"
|
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.0.tgz#2eaa80aab125b19f00faa794b5bd867b17f85acb"
|
||||||
integrity sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==
|
integrity sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^4.1.0"
|
chalk "^4.1.0"
|
||||||
hash-sum "^2.0.0"
|
hash-sum "^2.0.0"
|
||||||
|
|
Loading…
Reference in a new issue