mirror of
https://activitypub.software/TransFem-org/Sharkey.git
synced 2024-12-15 00:55:48 +01:00
Improve task manager etc
This commit is contained in:
parent
21b6e23e98
commit
7060625adf
7 changed files with 108 additions and 23 deletions
|
@ -169,15 +169,15 @@ export default defineComponent({
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
color: var(--accent);
|
color: var(--success);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.error {
|
&.error {
|
||||||
color: #ec4137;
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
color: #ecb637;
|
color: var(--warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
|
<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div v-if="tab === 'windows'" class="windows">
|
<div v-if="tab === 'windows'" class="windows" v-follow>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div>#ID</div>
|
<div>#ID</div>
|
||||||
<div>Component</div>
|
<div>Component</div>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<div><button class="_textButton" @click="killPopup(p)">Kill</button></div>
|
<div><button class="_textButton" @click="killPopup(p)">Kill</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tab === 'stream'" class="stream">
|
<div v-if="tab === 'stream'" class="stream" v-follow>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div>#ID</div>
|
<div>#ID</div>
|
||||||
<div>Ch</div>
|
<div>Ch</div>
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
<div>{{ c.out }}</div>
|
<div>{{ c.out }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tab === 'streamPool'" class="streamPool">
|
<div v-if="tab === 'streamPool'" class="streamPool" v-follow>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div>#ID</div>
|
<div>#ID</div>
|
||||||
<div>Ch</div>
|
<div>Ch</div>
|
||||||
|
@ -48,6 +48,18 @@
|
||||||
<div>{{ p.users }}</div>
|
<div>{{ p.users }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="tab === 'api'" class="api" v-follow>
|
||||||
|
<div class="header">
|
||||||
|
<div>#ID</div>
|
||||||
|
<div>Endpoint</div>
|
||||||
|
<div>State</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="req in apiRequests">
|
||||||
|
<div>#{{ req.id }}</div>
|
||||||
|
<div>{{ req.endpoint }}</div>
|
||||||
|
<div class="state" :class="req.state">{{ req.state }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -65,6 +77,7 @@ import { faTerminal } from '@fortawesome/free-solid-svg-icons';
|
||||||
import XWindow from '@/components/ui/window.vue';
|
import XWindow from '@/components/ui/window.vue';
|
||||||
import MkTab from '@/components/tab.vue';
|
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 * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -74,6 +87,10 @@ export default defineComponent({
|
||||||
MkButton,
|
MkButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
directives: {
|
||||||
|
follow
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -105,6 +122,7 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
tab: ref('stream'),
|
tab: ref('stream'),
|
||||||
popups: os.popups,
|
popups: os.popups,
|
||||||
|
apiRequests: os.apiRequests,
|
||||||
connections,
|
connections,
|
||||||
pools,
|
pools,
|
||||||
killPopup,
|
killPopup,
|
||||||
|
@ -125,9 +143,7 @@ export default defineComponent({
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
> .windows,
|
> div {
|
||||||
> .stream,
|
|
||||||
> .streamPool {
|
|
||||||
display: table;
|
display: table;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
@ -140,8 +156,31 @@ export default defineComponent({
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> div {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.api {
|
||||||
|
> div {
|
||||||
|
> .state {
|
||||||
|
&.pending {
|
||||||
|
color: var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.failed {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
src/client/directives/follow-append.ts
Normal file
25
src/client/directives/follow-append.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { Directive } from 'vue';
|
||||||
|
import { getScrollContainer, getScrollPosition } from '@/scripts/scroll';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mounted(src, binding, vn) {
|
||||||
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
|
const pos = getScrollPosition(src);
|
||||||
|
const container = getScrollContainer(src);
|
||||||
|
const viewHeight = container.clientHeight;
|
||||||
|
const height = container.scrollHeight;
|
||||||
|
if (pos + viewHeight > height - 32) {
|
||||||
|
container.scrollTop = height;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ro.observe(src);
|
||||||
|
|
||||||
|
// TODO: 新たにプロパティを作るのをやめMapを使う
|
||||||
|
src._ro_ = ro;
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted(src, binding, vn) {
|
||||||
|
src._ro_.unobserve(src);
|
||||||
|
}
|
||||||
|
} as Directive;
|
|
@ -2,7 +2,7 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vu
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import Stream from '@/scripts/stream';
|
import Stream from '@/scripts/stream';
|
||||||
import { store } from '@/store';
|
import { store } from '@/store';
|
||||||
import { apiUrl } from '@/config';
|
import { apiUrl, debug } from '@/config';
|
||||||
import MkPostFormDialog from '@/components/post-form-dialog.vue';
|
import MkPostFormDialog from '@/components/post-form-dialog.vue';
|
||||||
import MkWaitingDialog from '@/components/waiting-dialog.vue';
|
import MkWaitingDialog from '@/components/waiting-dialog.vue';
|
||||||
import { resolve } from '@/router';
|
import { resolve } from '@/router';
|
||||||
|
@ -13,28 +13,26 @@ export const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
||||||
export const stream = markRaw(new Stream());
|
export const stream = markRaw(new Stream());
|
||||||
|
|
||||||
export const pendingApiRequestsCount = ref(0);
|
export const pendingApiRequestsCount = ref(0);
|
||||||
|
export const apiRequests = ref([]); // for debug
|
||||||
|
|
||||||
export const windows = new Map();
|
export const windows = new Map();
|
||||||
|
|
||||||
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
|
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
|
||||||
pendingApiRequestsCount.value++;
|
pendingApiRequestsCount.value++;
|
||||||
|
|
||||||
if (_DEV_) {
|
|
||||||
performance.mark(_PERF_PREFIX_ + 'api:begin');
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFinally = () => {
|
const onFinally = () => {
|
||||||
pendingApiRequestsCount.value--;
|
pendingApiRequestsCount.value--;
|
||||||
|
|
||||||
if (_DEV_) {
|
|
||||||
performance.mark(_PERF_PREFIX_ + 'api:end');
|
|
||||||
|
|
||||||
performance.measure(_PERF_PREFIX_ + 'api',
|
|
||||||
_PERF_PREFIX_ + 'api:begin',
|
|
||||||
_PERF_PREFIX_ + 'api:end');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const log = debug ? reactive({
|
||||||
|
id: apiRequests.value.length,
|
||||||
|
endpoint,
|
||||||
|
state: 'pending'
|
||||||
|
}) : null;
|
||||||
|
if (debug) {
|
||||||
|
apiRequests.value.push(log);
|
||||||
|
}
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
// Append a credential
|
// Append a credential
|
||||||
if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
|
if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
|
||||||
|
@ -51,10 +49,19 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
resolve(body);
|
resolve(body);
|
||||||
|
if (debug) {
|
||||||
|
log.state = 'success';
|
||||||
|
}
|
||||||
} else if (res.status === 204) {
|
} else if (res.status === 204) {
|
||||||
resolve();
|
resolve();
|
||||||
|
if (debug) {
|
||||||
|
log.state = 'success';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reject(body.error);
|
reject(body.error);
|
||||||
|
if (debug) {
|
||||||
|
log.state = 'failed';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).catch(reject);
|
}).catch(reject);
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
<MkInput v-model:value="dialogBody">
|
<MkInput v-model:value="dialogBody">
|
||||||
<span>Body</span>
|
<span>Body</span>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
<MkRadio v-model="dialogType" value="info">Info</MkRadio>
|
||||||
|
<MkRadio v-model="dialogType" value="success">Success</MkRadio>
|
||||||
|
<MkRadio v-model="dialogType" value="warning">Warn</MkRadio>
|
||||||
|
<MkRadio v-model="dialogType" value="error">Error</MkRadio>
|
||||||
<MkSwitch v-model:value="dialogCancel">
|
<MkSwitch v-model:value="dialogCancel">
|
||||||
<span>With cancel button</span>
|
<span>With cancel button</span>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
@ -133,6 +137,7 @@ import MkButton from '@/components/ui/button.vue';
|
||||||
import MkInput from '@/components/ui/input.vue';
|
import MkInput from '@/components/ui/input.vue';
|
||||||
import MkSwitch from '@/components/ui/switch.vue';
|
import MkSwitch from '@/components/ui/switch.vue';
|
||||||
import MkTextarea from '@/components/ui/textarea.vue';
|
import MkTextarea from '@/components/ui/textarea.vue';
|
||||||
|
import MkRadio from '@/components/ui/radio.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -141,6 +146,7 @@ export default defineComponent({
|
||||||
MkInput,
|
MkInput,
|
||||||
MkSwitch,
|
MkSwitch,
|
||||||
MkTextarea,
|
MkTextarea,
|
||||||
|
MkRadio,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -153,6 +159,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
dialogTitle: 'Hello',
|
dialogTitle: 'Hello',
|
||||||
dialogBody: 'World!',
|
dialogBody: 'World!',
|
||||||
|
dialogType: 'info',
|
||||||
dialogCancel: false,
|
dialogCancel: false,
|
||||||
dialogCancelByBgClick: true,
|
dialogCancelByBgClick: true,
|
||||||
dialogInput: false,
|
dialogInput: false,
|
||||||
|
@ -192,6 +199,7 @@ export default defineComponent({
|
||||||
async showDialog() {
|
async showDialog() {
|
||||||
this.dialogResult = null;
|
this.dialogResult = null;
|
||||||
this.dialogResult = await os.dialog({
|
this.dialogResult = await os.dialog({
|
||||||
|
type: this.dialogType,
|
||||||
title: this.dialogTitle,
|
title: this.dialogTitle,
|
||||||
text: this.dialogBody,
|
text: this.dialogBody,
|
||||||
showCancelButton: this.dialogCancel,
|
showCancelButton: this.dialogCancel,
|
||||||
|
|
|
@ -56,6 +56,9 @@
|
||||||
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
||||||
badge: '#31b1ce',
|
badge: '#31b1ce',
|
||||||
messageBg: ':lighten<5<@bg',
|
messageBg: ':lighten<5<@bg',
|
||||||
|
success: '#86b300',
|
||||||
|
error: '#ec4137',
|
||||||
|
warn: '#ecb637',
|
||||||
htmlThemeColor: '@bg',
|
htmlThemeColor: '@bg',
|
||||||
X1: ':alpha<0<@bg',
|
X1: ':alpha<0<@bg',
|
||||||
X2: ':darken<2<@panel',
|
X2: ':darken<2<@panel',
|
||||||
|
|
|
@ -56,6 +56,9 @@
|
||||||
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
|
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
|
||||||
badge: '#31b1ce',
|
badge: '#31b1ce',
|
||||||
messageBg: '@panel',
|
messageBg: '@panel',
|
||||||
|
success: '#86b300',
|
||||||
|
error: '#ec4137',
|
||||||
|
warn: '#ecb637',
|
||||||
htmlThemeColor: '@bg',
|
htmlThemeColor: '@bg',
|
||||||
X1: ':alpha<0<@bg',
|
X1: ':alpha<0<@bg',
|
||||||
X2: ':darken<2<@panel',
|
X2: ':darken<2<@panel',
|
||||||
|
|
Loading…
Reference in a new issue