summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/global
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/components/global')
-rw-r--r--packages/frontend/src/components/global/MkA.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/global/MkA.vue2
-rw-r--r--packages/frontend/src/components/global/MkAd.stories.impl.ts87
-rw-r--r--packages/frontend/src/components/global/MkCustomEmoji.vue6
-rw-r--r--packages/frontend/src/components/global/MkEmoji.vue2
-rw-r--r--packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts2
-rw-r--r--packages/frontend/src/components/global/MkStickyContainer.vue6
-rw-r--r--packages/frontend/src/components/global/MkTime.vue4
-rw-r--r--packages/frontend/src/components/global/MkUrl.vue6
-rw-r--r--packages/frontend/src/components/global/RouterView.vue4
10 files changed, 52 insertions, 69 deletions
diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts
index c1d8cf0ca6..02e5a7f98c 100644
--- a/packages/frontend/src/components/global/MkA.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkA.stories.impl.ts
@@ -35,12 +35,10 @@ export const Default = {
// FIXME: 通るけどその後落ちるのでコメントアウト
// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
await userEvent.pointer({ keys: '[MouseRight]', target: a });
- await tick();
const menu = canvas.getByRole('menu');
await expect(menu).toBeInTheDocument();
await userEvent.click(a);
a.blur();
- await tick();
await expect(menu).not.toBeInTheDocument();
},
args: {
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 6cb948d3dd..e0303dbb27 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -16,7 +16,7 @@ export type MkABehavior = 'window' | 'browser' | null;
<script lang="ts" setup>
import { computed, inject, shallowRef } from 'vue';
import * as os from '@/os.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js';
import { i18n } from '@/i18n.js';
import { useRouter } from '@/router/supplier.js';
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index aef26ab92d..8c0b7ef52f 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -9,12 +9,6 @@ import { StoryObj } from '@storybook/vue3';
import MkAd from './MkAd.vue';
import { i18n } from '@/i18n.js';
-let lock: Promise<undefined> | undefined;
-
-function sleep(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
-}
-
const common = {
render(args) {
return {
@@ -37,56 +31,41 @@ const common = {
};
},
async play({ canvasElement, args }) {
- if (lock) {
- console.warn('This test is unexpectedly running twice in parallel, fix it!');
- console.warn('See also: https://github.com/misskey-dev/misskey/issues/11267');
- await lock;
+ const canvas = within(canvasElement);
+ const a = canvas.getByRole<HTMLAnchorElement>('link');
+ // FIXME: 通るけどその後落ちるのでコメントアウト
+ // await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
+ const img = within(a).getByRole('img');
+ await expect(img).toBeInTheDocument();
+ let buttons = canvas.getAllByRole<HTMLButtonElement>('button');
+ await expect(buttons).toHaveLength(1);
+ const i = buttons[0];
+ await expect(i).toBeInTheDocument();
+ await userEvent.click(i);
+ await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
+ await expect(a).not.toBeInTheDocument();
+ await expect(i).not.toBeInTheDocument();
+ buttons = canvas.getAllByRole<HTMLButtonElement>('button');
+ const hasReduceFrequency = args.specify?.ratio !== 0;
+ await expect(buttons).toHaveLength(hasReduceFrequency ? 2 : 1);
+ const reduce = hasReduceFrequency ? buttons[0] : null;
+ const back = buttons[hasReduceFrequency ? 1 : 0];
+ if (reduce) {
+ await expect(reduce).toBeInTheDocument();
+ await expect(reduce).toHaveTextContent(i18n.ts._ad.reduceFrequencyOfThisAd);
}
-
- let resolve: (value?: any) => void;
- lock = new Promise(r => resolve = r);
-
- try {
- // NOTE: sleep しないと何故か落ちる
- await sleep(100);
- const canvas = within(canvasElement);
- const a = canvas.getByRole<HTMLAnchorElement>('link');
- // await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
- const img = within(a).getByRole('img');
- await expect(img).toBeInTheDocument();
- let buttons = canvas.getAllByRole<HTMLButtonElement>('button');
- await expect(buttons).toHaveLength(1);
- const i = buttons[0];
- await expect(i).toBeInTheDocument();
- await userEvent.click(i);
- await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
- await expect(a).not.toBeInTheDocument();
- await expect(i).not.toBeInTheDocument();
- buttons = canvas.getAllByRole<HTMLButtonElement>('button');
- const hasReduceFrequency = args.specify?.ratio !== 0;
- await expect(buttons).toHaveLength(hasReduceFrequency ? 2 : 1);
- const reduce = hasReduceFrequency ? buttons[0] : null;
- const back = buttons[hasReduceFrequency ? 1 : 0];
- if (reduce) {
- await expect(reduce).toBeInTheDocument();
- await expect(reduce).toHaveTextContent(i18n.ts._ad.reduceFrequencyOfThisAd);
- }
- await expect(back).toBeInTheDocument();
- await expect(back).toHaveTextContent(i18n.ts._ad.back);
- await userEvent.click(back);
- await waitFor(() => expect(canvas.queryByRole('img')).toBeTruthy());
- if (reduce) {
- await expect(reduce).not.toBeInTheDocument();
- }
- await expect(back).not.toBeInTheDocument();
- const aAgain = canvas.getByRole<HTMLAnchorElement>('link');
- await expect(aAgain).toBeInTheDocument();
- const imgAgain = within(aAgain).getByRole('img');
- await expect(imgAgain).toBeInTheDocument();
- } finally {
- resolve!();
- lock = undefined;
+ await expect(back).toBeInTheDocument();
+ await expect(back).toHaveTextContent(i18n.ts._ad.back);
+ await userEvent.click(back);
+ await waitFor(() => expect(canvas.queryByRole('img')).toBeTruthy());
+ if (reduce) {
+ await expect(reduce).not.toBeInTheDocument();
}
+ await expect(back).not.toBeInTheDocument();
+ const aAgain = canvas.getByRole<HTMLAnchorElement>('link');
+ await expect(aAgain).toBeInTheDocument();
+ const imgAgain = within(aAgain).getByRole('img');
+ await expect(imgAgain).toBeInTheDocument();
},
args: {
prefer: [],
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 1f7f09335e..4cacc7b292 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -31,7 +31,7 @@ import { defaultStore } from '@/store.js';
import { customEmojisMap } from '@/custom-emojis.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as sound from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
@@ -107,12 +107,12 @@ function onClick(ev: MouseEvent) {
text: i18n.ts.info,
icon: 'ti ti-info-circle',
action: async () => {
- os.popup(MkCustomEmojiDetailedDialog, {
+ const { dispose } = os.popup(MkCustomEmojiDetailedDialog, {
emoji: await misskeyApiGet('emoji', {
name: customEmojiName.value,
}),
}, {
- anchor: ev.target,
+ closed: () => dispose(),
});
},
}], ev.currentTarget ?? ev.target);
diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue
index 9cf7dab06d..c485305376 100644
--- a/packages/frontend/src/components/global/MkEmoji.vue
+++ b/packages/frontend/src/components/global/MkEmoji.vue
@@ -14,7 +14,7 @@ import { char2fluentEmojiFilePath, char2twemojiFilePath, char2tossfaceFilePath }
import { defaultStore } from '@/store.js';
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
import * as os from '@/os.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as sound from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index ac563ffaf5..a3a2b9f319 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -68,7 +68,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
const validTime = (t: string | boolean | null | undefined) => {
if (t == null) return null;
if (typeof t === 'boolean') return null;
- return t.match(/^[0-9.]+s$/) ? t : null;
+ return t.match(/^\-?[0-9.]+s$/) ? t : null;
};
const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : props.isAnim ? true : false;
diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue
index 89993e1b8e..b12dc8cb31 100644
--- a/packages/frontend/src/components/global/MkStickyContainer.vue
+++ b/packages/frontend/src/components/global/MkStickyContainer.vue
@@ -8,7 +8,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<div ref="headerEl">
<slot name="header"></slot>
</div>
- <div ref="bodyEl" :data-sticky-container-header-height="headerHeight">
+ <div
+ ref="bodyEl"
+ :data-sticky-container-header-height="headerHeight"
+ :data-sticky-container-footer-height="footerHeight"
+ >
<slot></slot>
</div>
<div ref="footerEl">
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 23fe99bd9c..027b226f3f 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -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*/);
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 083e163630..15595ba515 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -51,11 +51,13 @@ const el = ref();
if (props.showUrlPreview && isEnabledUrlPreview.value) {
useTooltip(el, (showing) => {
- os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
showing,
url: props.url,
source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
- }, {}, 'closed');
+ }, {
+ closed: () => dispose(),
+ });
});
}
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 06cb30eff1..19bd794a5d 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -53,14 +53,14 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
const current = resolveNested(router.current)!;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props);
-const key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
+const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props)));
function onChange({ resolved, key: newKey }) {
const current = resolveNested(resolved);
if (current == null || 'redirect' in current.route) return;
currentPageComponent.value = current.route.component;
currentPageProps.value = current.props;
- key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
+ key.value = newKey + JSON.stringify(Object.fromEntries(current.props));
nextTick(() => {
// ページ遷移完了後に再びキャッシュを有効化