Merge branch 'develop' into swn

This commit is contained in:
tamaina
2021-10-24 21:43:13 +09:00
77 changed files with 1223 additions and 1388 deletions

View File

@@ -1,5 +1,10 @@
<template>
<canvas ref="chartEl"></canvas>
<div class="cbbedffa">
<canvas ref="chartEl"></canvas>
<div v-if="fetching" class="fetching">
<MkLoading/>
</div>
</div>
</template>
<script lang="ts">
@@ -84,6 +89,16 @@ export default defineComponent({
required: false,
default: false
},
stacked: {
type: Boolean,
required: false,
default: false
},
aspectRatio: {
type: Number,
required: false,
default: null
},
},
setup(props) {
@@ -152,7 +167,7 @@ export default defineComponent({
})),
},
options: {
aspectRatio: 2.5,
aspectRatio: props.aspectRatio || 2.5,
layout: {
padding: {
left: 16,
@@ -169,7 +184,6 @@ export default defineComponent({
unit: props.span === 'day' ? 'month' : 'day',
},
grid: {
display: props.detailed,
color: gridColor,
borderColor: 'rgb(0, 0, 0, 0)',
},
@@ -185,6 +199,7 @@ export default defineComponent({
},
y: {
position: 'left',
stacked: props.stacked,
grid: {
color: gridColor,
borderColor: 'rgb(0, 0, 0, 0)',
@@ -199,6 +214,7 @@ export default defineComponent({
},
plugins: {
legend: {
display: props.detailed,
position: 'bottom',
labels: {
boxWidth: 16,
@@ -578,6 +594,30 @@ export default defineComponent({
};
};
const fetchPerUserNotesChart = async (): Promise<typeof data> => {
const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
return {
series: [...(props.args.withoutAll ? [] : [{
name: 'All',
type: 'line',
borderDash: [5, 5],
data: format(sum(raw.inc, negate(raw.dec))),
}]), {
name: 'Renotes',
type: 'area',
data: format(raw.diffs.renote),
}, {
name: 'Replies',
type: 'area',
data: format(raw.diffs.reply),
}, {
name: 'Normal',
type: 'area',
data: format(raw.diffs.normal),
}],
};
};
const fetchAndRender = async () => {
const fetchData = () => {
switch (props.src) {
@@ -606,6 +646,8 @@ export default defineComponent({
case 'instance-drive-usage-total': return fetchInstanceDriveUsageChart(true);
case 'instance-drive-files': return fetchInstanceDriveFilesChart(false);
case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true);
case 'per-user-notes': return fetchPerUserNotesChart();
}
};
fetching.value = true;
@@ -622,7 +664,28 @@ export default defineComponent({
return {
chartEl,
fetching,
};
},
});
</script>
<style lang="scss" scoped>
.cbbedffa {
position: relative;
> .fetching {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-backdrop-filter: var(--blur, blur(12px));
backdrop-filter: var(--blur, blur(12px));
display: flex;
justify-content: center;
align-items: center;
cursor: wait;
}
}
</style>

View File

@@ -24,7 +24,7 @@
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode } from 'vue';
import MkButton from '@client/components/ui/button.vue';
import * as os from '@client/os';
@@ -140,6 +140,16 @@ export default defineComponent({
const menu = [];
let options = context.slots.default();
const pushOption = (option: VNode) => {
menu.push({
text: option.children,
active: v.value === option.props.value,
action: () => {
v.value = option.props.value;
},
});
};
for (const optionOrOptgroup of options) {
if (optionOrOptgroup.type === 'optgroup') {
const optgroup = optionOrOptgroup;
@@ -148,23 +158,16 @@ export default defineComponent({
text: optgroup.props.label,
});
for (const option of optgroup.children) {
menu.push({
text: option.children,
active: v.value === option.props.value,
action: () => {
v.value = option.props.value;
},
});
pushOption(option);
}
} else if (Array.isArray(optionOrOptgroup.children)) { // 何故かフラグメントになってくることがある
const fragment = optionOrOptgroup;
for (const option of fragment.children) {
pushOption(option);
}
} else {
const option = optionOrOptgroup;
menu.push({
text: option.children,
active: v.value === option.props.value,
action: () => {
v.value = option.props.value;
},
});
pushOption(option);
}
}

View File

@@ -2,8 +2,8 @@
<div class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick" ref="el">
<template v-if="info">
<div class="titleContainer" @click="showTabsPopup" v-if="!hideTitle">
<i v-if="info.icon" class="icon" :class="info.icon"></i>
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
<MkAvatar v-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
<i v-else-if="info.icon" class="icon" :class="info.icon"></i>
<div class="title">
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/>
@@ -162,11 +162,6 @@ export default defineComponent({
onUnmounted(() => {
ro.disconnect();
});
setTimeout(() => {
const currentStickyTop = getComputedStyle(el.value.parentElement).getPropertyValue('--stickyTop') || '0px';
el.value.style.setProperty('--stickyTop', currentStickyTop);
el.value.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${el.value.offsetHeight}px)`);
}, 100); // レンダリング順序の関係で親のstickyTopの設定が少し遅れることがあるため
}
});

View File

@@ -0,0 +1,74 @@
<template>
<div ref="rootEl">
<slot name="header"></slot>
<div ref="bodyEl">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
export default defineComponent({
props: {
autoSticky: {
type: Boolean,
required: false,
default: false,
},
},
setup(props, context) {
const rootEl = ref<HTMLElement>(null);
const bodyEl = ref<HTMLElement>(null);
const calc = () => {
const currentStickyTop = getComputedStyle(rootEl.value).getPropertyValue('--stickyTop') || '0px';
const header = rootEl.value.children[0];
if (header === bodyEl.value) {
bodyEl.value.style.setProperty('--stickyTop', currentStickyTop);
} else {
bodyEl.value.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${header.offsetHeight}px)`);
if (props.autoSticky) {
header.style.setProperty('--stickyTop', currentStickyTop);
header.style.position = 'sticky';
header.style.top = 'var(--stickyTop)';
header.style.zIndex = '1';
}
}
};
onMounted(() => {
calc();
const observer = new MutationObserver(() => {
setTimeout(() => {
calc();
}, 100);
});
observer.observe(rootEl.value, {
attributes: false,
childList: true,
subtree: false,
});
onUnmounted(() => {
observer.disconnect();
});
});
return {
rootEl,
bodyEl,
};
},
});
</script>
<style lang="scss" module>
</style>

View File

@@ -15,6 +15,7 @@ import error from './global/error.vue';
import ad from './global/ad.vue';
import header from './global/header.vue';
import spacer from './global/spacer.vue';
import stickyContainer from './global/sticky-container.vue';
export default function(app: App) {
app.component('I18n', i18n);
@@ -32,4 +33,5 @@ export default function(app: App) {
app.component('MkAd', ad);
app.component('MkHeader', header);
app.component('MkSpacer', spacer);
app.component('MkStickyContainer', stickyContainer);
}

View File

@@ -1,6 +1,7 @@
<template>
<MkA class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<span class="me" v-if="isMe">{{ $ts.you }}</span>
<img class="icon" :src="`/avatar/@${username}@${host}`" alt="">
<span class="main">
<span class="username">@{{ username }}</span>
<span class="host" v-if="(host != localHost) || $store.state.showFullAcct">@{{ toUnicode(host) }}</span>
@@ -76,6 +77,13 @@ export default defineComponent({
vertical-align: top;
}
> .icon {
width: 1.5em;
margin: 0 0.2em;
vertical-align: bottom;
border-radius: 100%;
}
> .main {
> .host {
opacity: 0.5;

View File

@@ -10,10 +10,13 @@
</span>
<button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button>
</div>
<div class="body _fitSide_">
<keep-alive>
<component :is="component" v-bind="props" :ref="changePage"/>
</keep-alive>
<div class="body">
<MkStickyContainer>
<template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template>
<keep-alive>
<component :is="component" v-bind="props" :ref="changePage"/>
</keep-alive>
</MkStickyContainer>
</div>
</div>
</MkModal>

View File

@@ -16,8 +16,11 @@
<template #headerLeft>
<button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button>
</template>
<div class="yrolvcoq _fitSide_">
<component :is="component" v-bind="props" :ref="changePage"/>
<div class="yrolvcoq">
<MkStickyContainer>
<template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template>
<component :is="component" v-bind="props" :ref="changePage"/>
</MkStickyContainer>
</div>
</XWindow>
</template>

View File

@@ -79,6 +79,7 @@ export default defineComponent({
pointRadius: 0,
tension: 0,
borderWidth: 2,
borderJoinStyle: 'round',
borderColor: '#00E396',
backgroundColor: alpha('#00E396', 0.1),
data: []
@@ -87,6 +88,7 @@ export default defineComponent({
pointRadius: 0,
tension: 0,
borderWidth: 2,
borderJoinStyle: 'round',
borderColor: '#00BCD4',
backgroundColor: alpha('#00BCD4', 0.1),
data: []
@@ -95,17 +97,21 @@ export default defineComponent({
pointRadius: 0,
tension: 0,
borderWidth: 2,
borderJoinStyle: 'round',
borderColor: '#FFB300',
backgroundColor: alpha('#FFB300', 0.1),
yAxisID: 'y2',
data: []
}, {
label: 'Delayed',
pointRadius: 0,
tension: 0,
borderWidth: 2,
borderJoinStyle: 'round',
borderColor: '#E53935',
borderDash: [5, 5],
fill: false,
yAxisID: 'y2',
data: []
}],
},
@@ -122,15 +128,29 @@ export default defineComponent({
scales: {
x: {
grid: {
display: false,
display: true,
color: gridColor,
borderColor: 'rgb(0, 0, 0, 0)',
},
ticks: {
display: false,
maxTicksLimit: 10
},
},
y: {
min: 0,
stack: 'queue',
stackWeight: 2,
grid: {
color: gridColor,
borderColor: 'rgb(0, 0, 0, 0)',
},
},
y2: {
min: 0,
offset: true,
stack: 'queue',
stackWeight: 1,
grid: {
color: gridColor,
borderColor: 'rgb(0, 0, 0, 0)',

View File

@@ -1,23 +1,25 @@
<template>
<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')">
<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')" :max-width="340">
<div class="bqxuuuey">
<div class="info">
<div>{{ reaction.replace('@.', '') }}</div>
<div class="reaction">
<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/>
<div class="name">{{ reaction.replace('@.', '') }}</div>
</div>
<div class="users">
<template v-if="users.length <= 10">
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
</b>
</template>
<template v-if="10 < users.length">
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
</b>
<span slot="omitted">+{{ count - 10 }}</span>
</template>
</div>
<template v-if="users.length <= 10">
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
</b>
</template>
<template v-if="10 < users.length">
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
</b>
<span slot="omitted">+{{ count - 10 }}</span>
</template>
</div>
</MkTooltip>
</template>
@@ -59,8 +61,11 @@ export default defineComponent({
<style lang="scss" scoped>
.bqxuuuey {
> .info {
padding: 0 0 8px 0;
display: flex;
> .reaction {
flex: 1;
max-width: 100px;
text-align: center;
> .icon {
@@ -68,6 +73,19 @@ export default defineComponent({
width: 60px;
margin: 0 auto;
}
> .name {
font-size: 0.9em;
}
}
> .users {
flex: 1;
min-width: 0;
font-size: 0.9em;
border-left: solid 0.5px var(--divider);
padding-left: 10px;
margin-left: 10px;
}
}
</style>

View File

@@ -177,6 +177,7 @@ export default defineComponent({
> span {
font-size: 0.9em;
line-height: 32px;
margin: 0 0 0 4px;
}
}
</style>

View File

@@ -153,10 +153,4 @@ export default defineComponent({
}
}
}
._fitSide_ .ssazuxis {
> header {
padding: 0 16px;
}
}
</style>

View File

@@ -42,8 +42,4 @@ export default defineComponent({
margin-right: 4px;
}
}
._fitSide_ .fpezltsf {
border-radius: 0;
}
</style>

View File

@@ -50,9 +50,6 @@ export default defineComponent({
border-top: solid 0.5px var(--divider);
}
margin-left: 16px;
margin-right: 16px;
> .title {
font-size: 0.9em;
opacity: 0.7;
@@ -120,7 +117,7 @@ export default defineComponent({
> .items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
grid-gap: 8px;
padding: 0 16px;

View File

@@ -1,6 +1,6 @@
<template>
<transition name="tooltip" appear @after-leave="$emit('closed')">
<div class="buebdbiu _acrylic _shadow" v-show="showing" ref="content">
<div class="buebdbiu _acrylic _shadow" v-show="showing" ref="content" :style="{ maxWidth: maxWidth + 'px' }">
<slot>{{ text }}</slot>
</div>
</transition>
@@ -21,7 +21,12 @@ export default defineComponent({
text: {
type: String,
required: false
}
},
maxWidth: {
type: Number,
required: false,
default: 250,
},
},
emits: ['closed'],
@@ -75,11 +80,12 @@ export default defineComponent({
.buebdbiu {
position: absolute;
z-index: 11000;
max-width: 240px;
font-size: 0.8em;
padding: 8px 12px;
box-sizing: border-box;
text-align: center;
border-radius: 4px;
border: solid 0.5px var(--divider);
pointer-events: none;
transform-origin: center bottom;
}