Merge branch 'develop' into feat-1714
This commit is contained in:
commit
a252151d5d
79 changed files with 483 additions and 413 deletions
|
|
@ -131,7 +131,7 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
|
|||
res.json().then(done2, fail2);
|
||||
}))
|
||||
.then(async res => {
|
||||
if (res.error) {
|
||||
if ('error' in res) {
|
||||
if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') {
|
||||
// SUSPENDED
|
||||
if (forceShowDialog || $i && (token === $i.token || id === $i.id)) {
|
||||
|
|
|
|||
|
|
@ -6,52 +6,11 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable import/no-default-export */
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw';
|
||||
import seedrandom from 'seedrandom';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { http } from 'msw';
|
||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||
import { getChartResolver } from '../../.storybook/charts.js';
|
||||
import MkChart from './MkChart.vue';
|
||||
|
||||
function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] {
|
||||
const rng = seedrandom(seed);
|
||||
const max = Math.floor(option?.mul ?? 250 * rng());
|
||||
let accumulation = 0;
|
||||
const array: number[] = [];
|
||||
for (let i = 0; i < limit; i++) {
|
||||
const num = Math.floor((max + 1) * rng());
|
||||
if (option?.accumulate) {
|
||||
accumulation += num;
|
||||
array.unshift(accumulation);
|
||||
} else {
|
||||
array.push(num);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
|
||||
return ({ request }) => {
|
||||
action(`GET ${request.url}`)();
|
||||
const limitParam = new URL(request.url).searchParams.get('limit');
|
||||
const limit = limitParam ? parseInt(limitParam) : 30;
|
||||
const res = {};
|
||||
for (const field of fields) {
|
||||
const layers = field.split('.');
|
||||
let current = res;
|
||||
while (layers.length > 1) {
|
||||
const currentKey = layers.shift()!;
|
||||
if (current[currentKey] == null) current[currentKey] = {};
|
||||
current = current[currentKey];
|
||||
}
|
||||
current[layers[0]] = getChartArray(field, limit, {
|
||||
accumulate: option?.accumulate,
|
||||
mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined,
|
||||
});
|
||||
}
|
||||
return HttpResponse.json(res);
|
||||
};
|
||||
}
|
||||
|
||||
const Base = {
|
||||
render(args) {
|
||||
return {
|
||||
|
|
@ -76,6 +35,7 @@ const Base = {
|
|||
args: {
|
||||
src: 'federation',
|
||||
span: 'hour',
|
||||
nowForChromatic: 1716263640000,
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
|
@ -100,18 +60,21 @@ const Base = {
|
|||
export const FederationChart = {
|
||||
...Base,
|
||||
args: {
|
||||
...Base.args,
|
||||
src: 'federation',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkChart>;
|
||||
export const NotesTotalChart = {
|
||||
...Base,
|
||||
args: {
|
||||
...Base.args,
|
||||
src: 'notes-total',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkChart>;
|
||||
export const DriveChart = {
|
||||
...Base,
|
||||
args: {
|
||||
...Base.args,
|
||||
src: 'drive',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkChart>;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ const props = withDefaults(defineProps<{
|
|||
stacked?: boolean;
|
||||
bar?: boolean;
|
||||
aspectRatio?: number | null;
|
||||
nowForChromatic?: number;
|
||||
}>(), {
|
||||
args: undefined,
|
||||
limit: 90,
|
||||
|
|
@ -84,6 +85,13 @@ const props = withDefaults(defineProps<{
|
|||
stacked: false,
|
||||
bar: false,
|
||||
aspectRatio: null,
|
||||
|
||||
/**
|
||||
* @desc Overwrites current date to fix background lines of chart.
|
||||
* @ignore Only used for Chromatic. Don't use this for production.
|
||||
* @see https://github.com/misskey-dev/misskey/pull/13830#issuecomment-2155886151
|
||||
*/
|
||||
nowForChromatic: undefined,
|
||||
});
|
||||
|
||||
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
|
||||
|
|
@ -106,7 +114,8 @@ const getColor = (i) => {
|
|||
return colorSets[i % colorSets.length];
|
||||
};
|
||||
|
||||
const now = new Date();
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const now = props.nowForChromatic != null ? new Date(props.nowForChromatic) : new Date();
|
||||
let chartInstance: Chart | null = null;
|
||||
let chartData: {
|
||||
series: {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ export default defineComponent({
|
|||
el.style.left = '';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const classes = {
|
||||
[$style['date-separated-list']]: true,
|
||||
[$style['date-separated-list-nogap']]: props.noGap,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { HttpResponse, http } from 'msw';
|
||||
import { federationInstance } from '../../.storybook/fakes.js';
|
||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||
import { getChartResolver } from '../../.storybook/charts.js';
|
||||
import MkInstanceCardMini from './MkInstanceCardMini.vue';
|
||||
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkInstanceCardMini,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkInstanceCardMini v-bind="props" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
instance: federationInstance(),
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
http.get('/undefined/preview.webp', async ({ request }) => {
|
||||
const urlStr = new URL(request.url).searchParams.get('url');
|
||||
if (urlStr == null) {
|
||||
return new HttpResponse(null, { status: 404 });
|
||||
}
|
||||
const url = new URL(urlStr);
|
||||
|
||||
if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) {
|
||||
const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
|
||||
return new HttpResponse(image, {
|
||||
headers: {
|
||||
'Content-Type': 'image/jpeg',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return new HttpResponse(null, { status: 404 });
|
||||
}
|
||||
}),
|
||||
http.get('/api/charts/instance', getChartResolver(['requests.received'])),
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkInstanceCardMini>;
|
||||
|
|
@ -29,8 +29,8 @@ const chartValues = ref<number[] | null>(null);
|
|||
|
||||
misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res['requests.received'].splice(0, 1);
|
||||
chartValues.value = res['requests.received'];
|
||||
res.requests.received.splice(0, 1);
|
||||
chartValues.value = res.requests.received;
|
||||
});
|
||||
|
||||
function getInstanceIcon(instance): string {
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ function hasFocus() {
|
|||
const playerEl = shallowRef<HTMLDivElement>();
|
||||
const audioEl = shallowRef<HTMLAudioElement>();
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore'));
|
||||
|
||||
// Menu
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ function hasFocus() {
|
|||
return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
|
||||
|
||||
// Menu
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div :class="$style.root">
|
||||
<div :class="$style.head">
|
||||
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
|
||||
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
||||
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
||||
<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
|
||||
<MkAvatar v-else-if="notification.user" :class="$style.icon" :user="notification.user" link preview/>
|
||||
<img v-else-if="notification.icon" :class="[$style.icon, $style.icon_app]" :src="notification.icon" alt=""/>
|
||||
<MkAvatar v-else-if="'user' in notification" :class="$style.icon" :user="notification.user" link preview/>
|
||||
<img v-else-if="'icon' in notification" :class="[$style.icon, $style.icon_app]" :src="notification.icon" alt=""/>
|
||||
<div
|
||||
:class="[$style.subIcon, {
|
||||
[$style.t_follow]: notification.type === 'follow',
|
||||
|
|
@ -164,13 +164,13 @@ const props = withDefaults(defineProps<{
|
|||
const followRequestDone = ref(false);
|
||||
|
||||
const acceptFollowRequest = () => {
|
||||
if (props.notification.user == null) return;
|
||||
if (!('user' in props.notification)) return;
|
||||
followRequestDone.value = true;
|
||||
misskeyApi('following/requests/accept', { userId: props.notification.user.id });
|
||||
};
|
||||
|
||||
const rejectFollowRequest = () => {
|
||||
if (props.notification.user == null) return;
|
||||
if (!('user' in props.notification)) return;
|
||||
followRequestDone.value = true;
|
||||
misskeyApi('following/requests/reject', { userId: props.notification.user.id });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ const emit = defineEmits<{
|
|||
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const page = ref(props.initialPage ?? 0);
|
||||
|
||||
watch(page, (to) => {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ const emit = defineEmits<{
|
|||
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const page = ref(defaultStore.state.accountSetupWizard);
|
||||
|
||||
watch(page, () => {
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@ function getDateSafe(n: Date | string | number) {
|
|||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const _time = props.time == null ? NaN : getDateSafe(props.time).getTime();
|
||||
const invalid = Number.isNaN(_time);
|
||||
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const now = ref(props.origin?.getTime() ?? Date.now());
|
||||
const ago = computed(() => (now.value - _time) / 1000/*ms*/);
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,15 @@ definePageMetadata(() => ({
|
|||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.fadeEnterActive,
|
||||
.fadeLeaveActive {
|
||||
transition: opacity 0.125s ease;
|
||||
}
|
||||
.fadeEnterFrom,
|
||||
.fadeLeaveTo {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.announcement {
|
||||
padding: 16px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ const props = defineProps<{
|
|||
const showBoardLabels = ref<boolean>(false);
|
||||
const useAvatarAsStone = ref<boolean>(true);
|
||||
const autoplaying = ref<boolean>(false);
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const game = ref<Misskey.entities.ReversiGameDetailed & { logs: Reversi.Serializer.SerializedLog[] }>(deepClone(props.game));
|
||||
const logPos = ref<number>(game.value.logs.length);
|
||||
const engine = shallowRef<Reversi.Game>(Reversi.Serializer.restoreGame({
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { useStream } from '@/stream.js';
|
|||
import { signinRequired } from '@/account.js';
|
||||
import { useRouter } from '@/router/supplier.js';
|
||||
import * as os from '@/os.js';
|
||||
import { url } from '@/config.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { useInterval } from '@/scripts/use-interval.js';
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ function start(_game: Misskey.entities.ReversiGameDetailed) {
|
|||
|
||||
if (shareWhenStart.value) {
|
||||
misskeyApi('notes/create', {
|
||||
text: i18n.ts._reversi.iStartedAGame + '\n' + location.href,
|
||||
text: `${i18n.ts._reversi.iStartedAGame}\n${url}/reversi/g/${props.gameId}`,
|
||||
visibility: 'home',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,11 +392,12 @@ onUnmounted(() => {
|
|||
|
||||
> .name {
|
||||
display: block;
|
||||
margin: 0;
|
||||
margin: -10px;
|
||||
padding: 10px;
|
||||
line-height: 32px;
|
||||
font-weight: bold;
|
||||
font-size: 1.8em;
|
||||
text-shadow: 0 0 8px #000;
|
||||
filter: drop-shadow(0 0 4px #000);
|
||||
}
|
||||
|
||||
> .bottom {
|
||||
|
|
|
|||
|
|
@ -81,16 +81,19 @@ defineExpose<WidgetComponentExpose>({
|
|||
.body {
|
||||
text-overflow: ellipsis;
|
||||
overflow: clip;
|
||||
margin-left: -10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #fff;
|
||||
filter: drop-shadow(0 0 4px #000);
|
||||
filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.host {
|
||||
color: #fff;
|
||||
filter: drop-shadow(0 0 4px #000);
|
||||
filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -82,16 +82,19 @@ defineExpose<WidgetComponentExpose>({
|
|||
.body {
|
||||
text-overflow: ellipsis;
|
||||
overflow: clip;
|
||||
margin-left: -10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #fff;
|
||||
filter: drop-shadow(0 0 4px #000);
|
||||
filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.username {
|
||||
color: #fff;
|
||||
filter: drop-shadow(0 0 4px #000);
|
||||
filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue