Merge remote-tracking branch 'misskey-dev/develop' into io

This commit is contained in:
まっちゃとーにゅ
2024-02-09 03:17:31 +09:00
75 changed files with 1904 additions and 1367 deletions

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { type SharedOptions, rest } from 'msw';
import { type SharedOptions, http, HttpResponse } from 'msw';
export const onUnhandledRequest = ((req, print) => {
if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
@@ -13,19 +13,31 @@ export const onUnhandledRequest = ((req, print) => {
}) satisfies SharedOptions['onUnhandledRequest'];
export const commonHandlers = [
rest.get('/fluent-emoji/:codepoints.png', async (req, res, ctx) => {
const { codepoints } = req.params;
http.get('/fluent-emoji/:codepoints.png', async ({ params }) => {
const { codepoints } = params;
const value = await fetch(`https://raw.githubusercontent.com/MisskeyIO/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
return new HttpResponse(value, {
headers: {
'Content-Type': 'image/png',
},
});
}),
rest.get('/fluent-emojis/:codepoints.png', async (req, res, ctx) => {
const { codepoints } = req.params;
http.get('/fluent-emojis/:codepoints.png', async ({ params }) => {
const { codepoints } = params;
const value = await fetch(`https://raw.githubusercontent.com/MisskeyIO/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
return new HttpResponse(value, {
headers: {
'Content-Type': 'image/png',
},
});
}),
rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => {
const { codepoints } = req.params;
http.get('/twemoji/:codepoints.svg', async ({ params }) => {
const { codepoints } = params;
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
return res(ctx.set('Content-Type', 'image/svg+xml'), ctx.body(value));
return new HttpResponse(value, {
headers: {
'Content-Type': 'image/svg+xml',
},
});
}),
];

View File

@@ -29,8 +29,8 @@
"@tabler/icons-webfont": "2.46.0",
"@twemoji/parser": "15.0.0",
"@vitejs/plugin-vue": "5.0.3",
"@vue/compiler-sfc": "3.4.15",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
"@vue/compiler-sfc": "3.4.16",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
"astring": "1.8.6",
"broadcast-channel": "7.0.0",
"buraha": "0.0.1",
@@ -61,10 +61,10 @@
"rollup": "4.9.6",
"sanitize-html": "2.11.0",
"sass": "1.70.0",
"shiki": "1.0.0-beta.3",
"shiki": "1.0.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.160.1",
"three": "0.161.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.8",
@@ -72,39 +72,39 @@
"typescript": "5.3.3",
"uuid": "9.0.1",
"v-code-diff": "1.7.2",
"vite": "5.0.12",
"vue": "3.4.15",
"vite": "5.1.0",
"vue": "3.4.16",
"vuedraggable": "next"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0",
"@misskey-dev/summaly": "5.0.3",
"@storybook/addon-actions": "7.6.10",
"@storybook/addon-essentials": "7.6.10",
"@storybook/addon-interactions": "7.6.10",
"@storybook/addon-links": "7.6.10",
"@storybook/addon-storysource": "7.6.10",
"@storybook/addons": "7.6.10",
"@storybook/blocks": "7.6.10",
"@storybook/core-events": "7.6.10",
"@storybook/addon-actions": "7.6.13",
"@storybook/addon-essentials": "7.6.13",
"@storybook/addon-interactions": "7.6.13",
"@storybook/addon-links": "7.6.13",
"@storybook/addon-storysource": "7.6.13",
"@storybook/addons": "7.6.13",
"@storybook/blocks": "7.6.13",
"@storybook/core-events": "7.6.13",
"@storybook/jest": "0.2.3",
"@storybook/manager-api": "7.6.10",
"@storybook/preview-api": "7.6.10",
"@storybook/react": "7.6.10",
"@storybook/react-vite": "7.6.10",
"@storybook/manager-api": "7.6.13",
"@storybook/preview-api": "7.6.13",
"@storybook/react": "7.6.13",
"@storybook/react-vite": "7.6.13",
"@storybook/testing-library": "0.2.2",
"@storybook/theming": "7.6.10",
"@storybook/types": "7.6.10",
"@storybook/vue3": "7.6.10",
"@storybook/vue3-vite": "7.6.10",
"@testing-library/vue": "8.0.1",
"@storybook/theming": "7.6.13",
"@storybook/types": "7.6.13",
"@storybook/vue3": "7.6.13",
"@storybook/vue3-vite": "7.6.13",
"@testing-library/vue": "8.0.2",
"@types/escape-regexp": "0.0.3",
"@types/estree": "1.0.5",
"@types/matter-js": "0.19.6",
"@types/micromatch": "4.0.6",
"@types/node": "20.11.10",
"@types/node": "20.11.16",
"@types/punycode": "2.1.3",
"@types/sanitize-html": "2.9.5",
"@types/sanitize-html": "2.11.0",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/uuid": "9.0.8",
@@ -112,25 +112,25 @@
"@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.18.1",
"@vitest/coverage-v8": "0.34.6",
"@vue/runtime-core": "3.4.15",
"@vue/runtime-core": "3.4.16",
"acorn": "8.11.3",
"cross-env": "7.0.3",
"cypress": "13.6.3",
"cypress": "13.6.4",
"eslint": "8.56.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-vue": "9.21.0",
"eslint-plugin-vue": "9.21.1",
"fast-glob": "3.3.2",
"happy-dom": "10.0.3",
"intersection-observer": "0.12.2",
"micromatch": "4.0.5",
"msw": "2.1.5",
"msw-storybook-addon": "1.10.0",
"msw": "2.1.7",
"msw-storybook-addon": "2.0.0-beta.1",
"nodemon": "3.0.3",
"prettier": "3.2.4",
"prettier": "3.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.3",
"storybook": "7.6.10",
"storybook": "7.6.13",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6",

View File

@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { abuseUserReport } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAbuseReport from './MkAbuseReport.vue';
@@ -44,9 +44,9 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => {
action('POST /api/admin/resolve-abuse-user-report')(await req.json());
return res(ctx.json({}));
http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => {
action('POST /api/admin/resolve-abuse-user-report')(await request.json());
return HttpResponse.json({});
}),
],
},

View File

@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
@@ -44,9 +44,9 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/report-abuse', async (req, res, ctx) => {
action('POST /api/users/report-abuse')(await req.json());
return res(ctx.json({}));
http.post('/api/users/report-abuse', async ({ request }) => {
action('POST /api/users/report-abuse')(await request.json());
return HttpResponse.json({});
}),
],
},

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAchievements from './MkAchievements.vue';
@@ -39,8 +39,8 @@ export const Empty = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/achievements', (req, res, ctx) => {
return res(ctx.json([]));
http.post('/api/users/achievements', () => {
return HttpResponse.json([]);
}),
],
},
@@ -52,8 +52,8 @@ export const All = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/achievements', (req, res, ctx) => {
return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))));
http.post('/api/users/achievements', () => {
return HttpResponse.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 })));
}),
],
},

View File

@@ -8,7 +8,7 @@ import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAutocomplete from './MkAutocomplete.vue';
@@ -99,11 +99,11 @@ export const User = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/users/search-by-username-and-host', () => {
return HttpResponse.json([
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
]));
]);
}),
],
},
@@ -132,12 +132,12 @@ export const Hashtag = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/hashtags/search', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/hashtags/search', () => {
return HttpResponse.json([
'気象警報注意報',
'気象警報',
'気象情報',
]));
]);
}),
],
},

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAvatars from './MkAvatars.vue';
@@ -38,12 +38,12 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/show', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/users/show', () => {
return HttpResponse.json([
userDetailed('17'),
userDetailed('20'),
userDetailed('18'),
]));
]);
}),
],
},

View File

@@ -77,6 +77,7 @@ watch(() => props.lang, (to) => {
overflow: auto;
border-radius: 8px;
border: 1px solid var(--divider);
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
color: var(--shiki-fallback);
background-color: var(--shiki-fallback-bg);

View File

@@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
</div>
</template>
<script lang="ts" setup>
import { ref, shallowRef, computed, nextTick, watch } from 'vue';
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
import { defaultStore } from '@/store.js';
import { isHorizontalSwipeSwiping as isSwiping } from '@/scripts/touch.js';
const rootEl = shallowRef<HTMLDivElement>();
@@ -49,16 +49,16 @@ const shouldAnimate = computed(() => defaultStore.reactiveState.enableHorizontal
// ▼ しきい値 ▼ //
// スワイプと判定される最小の距離
const MIN_SWIPE_DISTANCE = 50;
const MIN_SWIPE_DISTANCE = 20;
// スワイプ時の動作を発火する最小の距離
const SWIPE_DISTANCE_THRESHOLD = 125;
const SWIPE_DISTANCE_THRESHOLD = 70;
// スワイプを中断するY方向の移動距離
const SWIPE_ABORT_Y_THRESHOLD = 75;
// スワイプできる最大の距離
const MAX_SWIPE_DISTANCE = 150;
const MAX_SWIPE_DISTANCE = 120;
// ▲ しきい値 ▲ //
@@ -68,7 +68,6 @@ let startScreenY: number | null = null;
const currentTabIndex = computed(() => props.tabs.findIndex(tab => tab.key === tabModel.value));
const pullDistance = ref(0);
const isSwiping = ref(false);
const isSwipingForClass = ref(false);
let swipeAborted = false;
@@ -77,6 +76,8 @@ function touchStart(event: TouchEvent) {
if (event.touches.length !== 1) return;
if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return;
startScreenX = event.touches[0].screenX;
startScreenY = event.touches[0].screenY;
}
@@ -90,6 +91,8 @@ function touchMove(event: TouchEvent) {
if (swipeAborted) return;
if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return;
let distanceX = event.touches[0].screenX - startScreenX;
let distanceY = event.touches[0].screenY - startScreenY;
@@ -139,6 +142,8 @@ function touchEnd(event: TouchEvent) {
if (!isSwiping.value) return;
if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return;
const distance = event.changedTouches[0].screenX - startScreenX;
if (Math.abs(distance) > SWIPE_DISTANCE_THRESHOLD) {
@@ -162,6 +167,24 @@ function touchEnd(event: TouchEvent) {
}, 400);
}
/** 横スワイプに関与する可能性のある要素を調べる */
function hasSomethingToDoWithXSwipe(el: HTMLElement) {
if (['INPUT', 'TEXTAREA'].includes(el.tagName)) return true;
if (el.isContentEditable) return true;
if (el.scrollWidth > el.clientWidth) return true;
const style = window.getComputedStyle(el);
if (['absolute', 'fixed', 'sticky'].includes(style.position)) return true;
if (['scroll', 'auto'].includes(style.overflowX)) return true;
if (style.touchAction === 'pan-x') return true;
if (el.parentElement && el.parentElement !== rootEl.value) {
return hasSomethingToDoWithXSwipe(el.parentElement);
} else {
return false;
}
}
const transitionName = ref<'swipeAnimationLeft' | 'swipeAnimationRight' | undefined>(undefined);
watch(tabModel, (newTab, oldTab) => {
@@ -182,6 +205,7 @@ watch(tabModel, (newTab, oldTab) => {
<style lang="scss" module>
.transitionRoot {
touch-action: pan-y pinch-zoom;
display: grid;
grid-template-columns: 100%;
overflow: clip;

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkInviteCode from './MkInviteCode.vue';
@@ -39,8 +39,8 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/show', (req, res, ctx) => {
return res(ctx.json(userDetailed(req.params.userId as string)));
http.post('/api/users/show', ({ params }) => {
return HttpResponse.json(userDetailed(params.userId as string));
}),
],
},

View File

@@ -27,6 +27,7 @@ import MkLoading from '@/components/global/MkLoading.vue';
import { onMounted, onUnmounted, onActivated, onDeactivated, ref, shallowRef } from 'vue';
import { i18n } from '@/i18n.js';
import { getScrollContainer } from '@/scripts/scroll.js';
import { isHorizontalSwipeSwiping } from '@/scripts/touch.js';
const SCROLL_STOP = 10;
const MAX_PULL_DISTANCE = Infinity;
@@ -144,7 +145,7 @@ function moving(event: TouchEvent | PointerEvent) {
if (!isPullStart.value && scrollEl?.scrollTop === 0) moveStart(event);
if (!isPullStart.value || isRefreshing.value || disabled) return;
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) {
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value) || isHorizontalSwipeSwiping.value) {
pullDistance.value = 0;
isPullEnd.value = false;
moveEnd();
@@ -167,6 +168,10 @@ function moving(event: TouchEvent | PointerEvent) {
if (event.cancelable) event.preventDefault();
}
if (pullDistance.value > SCROLL_STOP) {
event.stopPropagation();
}
isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD && moveRatio.value > FIRE_THRESHOLD_RATIO;
}

View File

@@ -17,9 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import Misskey from 'misskey-js';
import { computed, watch, onUnmounted, provide, shallowRef } from 'vue';
import { Connection } from 'misskey-js/streaming.js';
import * as Misskey from 'misskey-js';
import MkNotes from '@/components/MkNotes.vue';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
import { useStream } from '@/stream.js';
@@ -87,8 +86,8 @@ function prepend(note) {
}
}
let connection: Connection;
let connection2: Connection;
let connection: Misskey.ChannelConnection | null = null;
let connection2: Misskey.ChannelConnection | null = null;
let paginationQuery: Paging | null = null;
const stream = useStream();
@@ -157,7 +156,7 @@ function connectChannel() {
roleId: props.role,
});
}
if (props.src !== 'directs' && props.src !== 'mentions') connection.on('note', prepend);
if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend);
}
function disconnectChannel() {

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin"
scrolling="no"
:allow="player.allow.join(';')"
:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
:class="$style.playerIframe"
:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
:style="{ border: 0 }"

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js';
import { userDetailed } from '../../.storybook/fakes.js';
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
@@ -38,17 +38,17 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/users', () => {
return HttpResponse.json([
userDetailed('44'),
userDetailed('49'),
]));
]);
}),
rest.post('/api/pinned-users', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/pinned-users', () => {
return HttpResponse.json([
userDetailed('44'),
userDetailed('49'),
]));
]);
}),
],
},

View File

@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import Misskey from 'misskey-js';
import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
import XUser from '@/components/MkUserSetupDialog.User.vue';

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../.storybook/mocks.js';
import { userDetailed } from '../../.storybook/fakes.js';
import MkUserSetupDialog from './MkUserSetupDialog.vue';
@@ -38,17 +38,17 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/users', () => {
return HttpResponse.json([
userDetailed('44'),
userDetailed('49'),
]));
]);
}),
rest.post('/api/pinned-users', (req, res, ctx) => {
return res(ctx.json([
http.post('/api/pinned-users', () => {
return HttpResponse.json([
userDetailed('44'),
userDetailed('49'),
]));
]);
}),
],
},

View File

@@ -7,7 +7,7 @@
import { expect } from '@storybook/jest';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../../.storybook/mocks.js';
import MkUrl from './MkUrl.vue';
export const Default = {
@@ -59,8 +59,8 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.get('/url', (req, res, ctx) => {
return res(ctx.json({
http.get('/url', () => {
return HttpResponse.json({
title: 'Misskey Hub',
icon: 'https://misskey-hub.net/favicon.ico',
description: 'Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。',
@@ -74,7 +74,7 @@ export const Default = {
sitename: 'misskey-hub.net',
sensitive: false,
url: 'https://misskey-hub.net/',
}));
});
}),
],
},

View File

@@ -213,13 +213,13 @@ const patronsWithIcon = [{
icon: 'https://assets.misskey-hub.net/patrons/302dce2898dd457ba03c3f7dc037900b.jpg',
}, {
name: 'taichan',
icon: 'https://assets.misskey-hub.net/patrons/f981ab0159fb4e2c998e05f7263e1cd9.png',
icon: 'https://assets.misskey-hub.net/patrons/f981ab0159fb4e2c998e05f7263e1cd9.jpg',
}, {
name: '猫吉よりお',
icon: 'https://assets.misskey-hub.net/patrons/a11518b3b34b4536a4bdd7178ba76a7b.png',
icon: 'https://assets.misskey-hub.net/patrons/a11518b3b34b4536a4bdd7178ba76a7b.jpg',
}, {
name: '有栖かずみ',
icon: 'https://assets.misskey-hub.net/patrons/9240e8e0ba294a8884143e99ac7ed6a0.png',
icon: 'https://assets.misskey-hub.net/patrons/9240e8e0ba294a8884143e99ac7ed6a0.jpg',
}];
const patrons = [

View File

@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../../.storybook/fakes.js';
import { commonHandlers } from '../../../.storybook/mocks.js';
import home_ from './home.vue';
@@ -39,12 +39,13 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
rest.post('/api/users/notes', (req, res, ctx) => {
return res(ctx.json([]));
http.post('/api/users/notes', () => {
return HttpResponse.json([]);
}),
rest.get('/api/charts/user/notes', (req, res, ctx) => {
const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300);
return res(ctx.json({
http.get('/api/charts/user/notes', ({ request }) => {
const url = new URL(request.url);
const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300);
return HttpResponse.json({
total: Array.from({ length }, () => 0),
inc: Array.from({ length }, () => 0),
dec: Array.from({ length }, () => 0),
@@ -54,11 +55,12 @@ export const Default = {
renote: Array.from({ length }, () => 0),
withFile: Array.from({ length }, () => 0),
},
}));
});
}),
rest.get('/api/charts/user/pv', (req, res, ctx) => {
const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300);
return res(ctx.json({
http.get('/api/charts/user/pv', ({ request }) => {
const url = new URL(request.url);
const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300);
return HttpResponse.json({
upv: {
user: Array.from({ length }, () => 0),
visitor: Array.from({ length }, () => 0),
@@ -67,7 +69,7 @@ export const Default = {
user: Array.from({ length }, () => 0),
visitor: Array.from({ length }, () => 0),
},
}));
});
}),
],
},

View File

@@ -68,10 +68,7 @@ export async function initHighlighter() {
themes,
langs: [
import('shiki/langs/javascript.mjs'),
{
aliases: ['is', 'ais'],
...aiScriptGrammar.default,
} as unknown as LanguageRegistration,
aiScriptGrammar.default as unknown as LanguageRegistration,
],
});

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ref } from 'vue';
import { deviceKind } from '@/scripts/device-kind.js';
const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
@@ -16,3 +17,6 @@ if (isTouchSupported && !isTouchUsing) {
isTouchUsing = true;
}, { passive: true });
}
/** (MkHorizontalSwipe) 横スワイプ中か? */
export const isHorizontalSwipeSwiping = ref(false);

View File

@@ -116,6 +116,34 @@ describe('MkUrlPreview', () => {
assert.strictEqual(iframe?.allow, 'fullscreen;web-share');
});
test('A Summaly proxy response without allow falls back to the default', async () => {
const iframe = await renderAndOpenPreview({
url: 'https://example.local',
player: {
url: 'https://example.local/player',
width: null,
height: null,
allow: undefined as any,
},
});
assert.exists(iframe, 'iframe should exist');
assert.strictEqual(iframe?.allow, 'autoplay;encrypted-media;fullscreen');
});
test('Filtering the allow list from the Summaly proxy', async () => {
const iframe = await renderAndOpenPreview({
url: 'https://example.local',
player: {
url: 'https://example.local/player',
width: null,
height: null,
allow: ['autoplay', 'camera', 'fullscreen'],
},
});
assert.exists(iframe, 'iframe should exist');
assert.strictEqual(iframe?.allow, 'autoplay;fullscreen');
});
test('Having a player width should keep the fixed aspect ratio', async () => {
const iframe = await renderAndOpenPreview({
url: 'https://example.local',