* feat(#8149): respect nsfw settings on gallery list * ci(#10336): use pull_request * test(#8149): add interaction tests * test(#10336): use `waitFor` * chore: transition
This commit is contained in:
parent
516a791bf4
commit
3b3f683f8c
|
@ -1,54 +1,116 @@
|
||||||
import type { entities } from 'misskey-js'
|
import type { entities } from 'misskey-js'
|
||||||
|
|
||||||
export const userDetailed = {
|
export function abuseUserReport() {
|
||||||
id: 'someuserid',
|
return {
|
||||||
username: 'miskist',
|
id: 'someabusereportid',
|
||||||
host: 'misskey-hub.net',
|
createdAt: '2016-12-28T22:49:51.000Z',
|
||||||
name: 'Misskey User',
|
comment: 'This user is a spammer!',
|
||||||
onlineStatus: 'unknown',
|
resolved: false,
|
||||||
avatarUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
|
reporterId: 'reporterid',
|
||||||
avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
|
targetUserId: 'targetuserid',
|
||||||
emojis: [],
|
assigneeId: 'assigneeid',
|
||||||
bannerBlurhash: 'eQA^IW^-MH8w9tE8I=S^o{$*R4RikXtSxutRozjEnNR.RQadoyozog',
|
reporter: userDetailed('reporterid', 'reporter', 'misskey-hub.net', 'Reporter'),
|
||||||
bannerColor: '#000000',
|
targetUser: userDetailed('targetuserid', 'target', 'misskey-hub.net', 'Target'),
|
||||||
bannerUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
|
assignee: userDetailed('assigneeid', 'assignee', 'misskey-hub.net', 'Assignee'),
|
||||||
birthday: '2014-06-20',
|
me: null,
|
||||||
createdAt: '2016-12-28T22:49:51.000Z',
|
forwarded: false,
|
||||||
description: 'I am a cool user!',
|
};
|
||||||
ffVisibility: 'public',
|
}
|
||||||
fields: [
|
|
||||||
{
|
export function galleryPost(isSensitive = false) {
|
||||||
name: 'Website',
|
return {
|
||||||
value: 'https://misskey-hub.net',
|
id: 'somepostid',
|
||||||
|
createdAt: '2016-12-28T22:49:51.000Z',
|
||||||
|
updatedAt: '2016-12-28T22:49:51.000Z',
|
||||||
|
userid: 'someuserid',
|
||||||
|
user: userDetailed(),
|
||||||
|
title: 'Some post title',
|
||||||
|
description: 'Some post description',
|
||||||
|
fileIds: ['somefileid'],
|
||||||
|
files: [
|
||||||
|
file(isSensitive),
|
||||||
|
],
|
||||||
|
isSensitive,
|
||||||
|
likedCount: 0,
|
||||||
|
isLiked: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function file(isSensitive = false) {
|
||||||
|
return {
|
||||||
|
id: 'somefileid',
|
||||||
|
createdAt: '2016-12-28T22:49:51.000Z',
|
||||||
|
name: 'somefile.jpg',
|
||||||
|
type: 'image/jpeg',
|
||||||
|
md5: 'f6fc51c73dc21b1fb85ead2cdf57530a',
|
||||||
|
size: 77752,
|
||||||
|
isSensitive,
|
||||||
|
blurhash: 'eQAmoa^-MH8w9ZIvNLSvo^$*MwRPbwtSxutRozjEiwR.RjWBoeozog',
|
||||||
|
properties: {
|
||||||
|
width: 1024,
|
||||||
|
height: 270
|
||||||
},
|
},
|
||||||
],
|
url: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
|
||||||
followersCount: 1024,
|
thumbnailUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
|
||||||
followingCount: 16,
|
comment: null,
|
||||||
hasPendingFollowRequestFromYou: false,
|
folderId: null,
|
||||||
hasPendingFollowRequestToYou: false,
|
folder: null,
|
||||||
isAdmin: false,
|
userId: null,
|
||||||
isBlocked: false,
|
user: null,
|
||||||
isBlocking: false,
|
};
|
||||||
isBot: false,
|
}
|
||||||
isCat: false,
|
|
||||||
isFollowed: false,
|
export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed {
|
||||||
isFollowing: false,
|
return {
|
||||||
isLocked: false,
|
id,
|
||||||
isModerator: false,
|
username,
|
||||||
isMuted: false,
|
host,
|
||||||
isSilenced: false,
|
name,
|
||||||
isSuspended: false,
|
onlineStatus: 'unknown',
|
||||||
lang: 'en',
|
avatarUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
|
||||||
location: 'Fediverse',
|
avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
|
||||||
notesCount: 65536,
|
emojis: [],
|
||||||
pinnedNoteIds: [],
|
bannerBlurhash: 'eQA^IW^-MH8w9tE8I=S^o{$*R4RikXtSxutRozjEnNR.RQadoyozog',
|
||||||
pinnedNotes: [],
|
bannerColor: '#000000',
|
||||||
pinnedPage: null,
|
bannerUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
|
||||||
pinnedPageId: null,
|
birthday: '2014-06-20',
|
||||||
publicReactions: false,
|
createdAt: '2016-12-28T22:49:51.000Z',
|
||||||
securityKeys: false,
|
description: 'I am a cool user!',
|
||||||
twoFactorEnabled: false,
|
ffVisibility: 'public',
|
||||||
updatedAt: null,
|
fields: [
|
||||||
uri: null,
|
{
|
||||||
url: null,
|
name: 'Website',
|
||||||
} satisfies entities.UserDetailed
|
value: 'https://misskey-hub.net',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
followersCount: 1024,
|
||||||
|
followingCount: 16,
|
||||||
|
hasPendingFollowRequestFromYou: false,
|
||||||
|
hasPendingFollowRequestToYou: false,
|
||||||
|
isAdmin: false,
|
||||||
|
isBlocked: false,
|
||||||
|
isBlocking: false,
|
||||||
|
isBot: false,
|
||||||
|
isCat: false,
|
||||||
|
isFollowed: false,
|
||||||
|
isFollowing: false,
|
||||||
|
isLocked: false,
|
||||||
|
isModerator: false,
|
||||||
|
isMuted: false,
|
||||||
|
isSilenced: false,
|
||||||
|
isSuspended: false,
|
||||||
|
lang: 'en',
|
||||||
|
location: 'Fediverse',
|
||||||
|
notesCount: 65536,
|
||||||
|
pinnedNoteIds: [],
|
||||||
|
pinnedNotes: [],
|
||||||
|
pinnedPage: null,
|
||||||
|
pinnedPageId: null,
|
||||||
|
publicReactions: false,
|
||||||
|
securityKeys: false,
|
||||||
|
twoFactorEnabled: false,
|
||||||
|
updatedAt: null,
|
||||||
|
uri: null,
|
||||||
|
url: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -394,13 +394,13 @@ function toStories(component: string): string {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// glob('src/{components,pages,ui,widgets}/**/*.vue').then(
|
// glob('src/{components,pages,ui,widgets}/**/*.vue')
|
||||||
glob('src/components/global/**/*.vue').then(
|
Promise.all([
|
||||||
(components) =>
|
glob('src/components/global/*.vue'),
|
||||||
Promise.all(
|
glob('src/components/MkGalleryPostPreview.vue'),
|
||||||
components.map((component) => {
|
])
|
||||||
const stories = component.replace(/\.vue$/, '.stories.ts');
|
.then((globs) => globs.flat())
|
||||||
return writeFile(stories, toStories(component));
|
.then((components) => Promise.all(components.map((component) => {
|
||||||
})
|
const stories = component.replace(/\.vue$/, '.stories.ts');
|
||||||
)
|
return writeFile(stories, toStories(component));
|
||||||
);
|
})));
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
|
import { StoryObj } from '@storybook/vue3';
|
||||||
|
import { galleryPost } from '../../.storybook/fakes';
|
||||||
|
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
||||||
|
export const Default = {
|
||||||
|
render(args) {
|
||||||
|
return {
|
||||||
|
components: {
|
||||||
|
MkGalleryPostPreview,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
args,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
props() {
|
||||||
|
return {
|
||||||
|
...this.args,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<MkGalleryPostPreview v-bind="props" />',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async play({ canvasElement }) {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
const links = canvas.getAllByRole('link');
|
||||||
|
await expect(links).toHaveLength(2);
|
||||||
|
await expect(links[0]).toHaveAttribute('href', `/gallery/${galleryPost().id}`);
|
||||||
|
await expect(links[1]).toHaveAttribute('href', `/@${galleryPost().user.username}@${galleryPost().user.host}`);
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
post: galleryPost(),
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
() => ({
|
||||||
|
template: '<div style="width:260px"><story /></div>',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkGalleryPostPreview>;
|
||||||
|
export const Hover = {
|
||||||
|
...Default,
|
||||||
|
async play(context) {
|
||||||
|
await Default.play(context);
|
||||||
|
const canvas = within(context.canvasElement);
|
||||||
|
const links = canvas.getAllByRole('link');
|
||||||
|
await waitFor(() => userEvent.hover(links[0]));
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkGalleryPostPreview>;
|
||||||
|
export const HoverThenUnhover = {
|
||||||
|
...Default,
|
||||||
|
async play(context) {
|
||||||
|
await Hover.play(context);
|
||||||
|
const canvas = within(context.canvasElement);
|
||||||
|
const links = canvas.getAllByRole('link');
|
||||||
|
await waitFor(() => userEvent.unhover(links[0]));
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkGalleryPostPreview>;
|
||||||
|
export const Sensitive = {
|
||||||
|
...Default,
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
post: galleryPost(true),
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkGalleryPostPreview>;
|
||||||
|
export const SensitiveHover = {
|
||||||
|
...Hover,
|
||||||
|
args: {
|
||||||
|
...Hover.args,
|
||||||
|
post: galleryPost(true),
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkGalleryPostPreview>;
|
||||||
|
export const SensitiveHoverThenUnhover = {
|
||||||
|
...HoverThenUnhover,
|
||||||
|
args: {
|
||||||
|
...HoverThenUnhover.args,
|
||||||
|
post: galleryPost(true),
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkGalleryPostPreview>;
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel" tabindex="-1">
|
<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel" tabindex="-1" @pointerenter="enterHover" @pointerleave="leaveHover">
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<ImgWithBlurhash class="img" :src="post.files[0].thumbnailUrl" :hash="post.files[0].blurhash"/>
|
<ImgWithBlurhash class="img" :hash="post.files[0].blurhash"/>
|
||||||
|
<Transition>
|
||||||
|
<ImgWithBlurhash v-if="show" class="img layered" :src="post.files[0].thumbnailUrl" :hash="post.files[0].blurhash"/>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<header>
|
||||||
|
@ -15,12 +18,25 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import * as misskey from 'misskey-js';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||||
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
post: any;
|
post: misskey.entities.GalleryPost;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const hover = ref(false);
|
||||||
|
const show = computed(() => defaultStore.state.nsfw === 'ignore' || defaultStore.state.nsfw === 'respect' && !props.post.isSensitive || hover.value);
|
||||||
|
|
||||||
|
function enterHover(): void {
|
||||||
|
hover.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function leaveHover(): void {
|
||||||
|
hover.value = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -56,6 +72,21 @@ const props = defineProps<{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
|
||||||
|
&.layered {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
&.v-enter-active,
|
||||||
|
&.v-leave-active {
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.v-enter-from,
|
||||||
|
&.v-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const Default = {
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
user: {
|
user: {
|
||||||
...userDetailed,
|
...userDetailed(),
|
||||||
host: null,
|
host: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -37,7 +37,7 @@ export const Detail = {
|
||||||
...Default,
|
...Default,
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
user: userDetailed,
|
user: userDetailed(),
|
||||||
detail: true,
|
detail: true,
|
||||||
},
|
},
|
||||||
} satisfies StoryObj<typeof MkAcct>;
|
} satisfies StoryObj<typeof MkAcct>;
|
||||||
|
|
|
@ -24,7 +24,7 @@ const common = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
user: userDetailed,
|
user: userDetailed(),
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story, context) => ({
|
(Story, context) => ({
|
||||||
|
@ -49,7 +49,7 @@ export const ProfilePageCat = {
|
||||||
args: {
|
args: {
|
||||||
...ProfilePage.args,
|
...ProfilePage.args,
|
||||||
user: {
|
user: {
|
||||||
...userDetailed,
|
...userDetailed(),
|
||||||
isCat: true,
|
isCat: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks';
|
import { commonHandlers } from '../../../.storybook/mocks';
|
||||||
|
@ -30,7 +30,7 @@ export const Default = {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
const a = canvas.getByRole<HTMLAnchorElement>('link');
|
const a = canvas.getByRole<HTMLAnchorElement>('link');
|
||||||
await expect(a).toHaveAttribute('href', 'https://misskey-hub.net/');
|
await expect(a).toHaveAttribute('href', 'https://misskey-hub.net/');
|
||||||
await userEvent.hover(a);
|
await waitFor(() => userEvent.hover(a));
|
||||||
/*
|
/*
|
||||||
await tick(); // FIXME: wait for network request
|
await tick(); // FIXME: wait for network request
|
||||||
const anchors = canvas.getAllByRole<HTMLAnchorElement>('link');
|
const anchors = canvas.getAllByRole<HTMLAnchorElement>('link');
|
||||||
|
@ -44,7 +44,7 @@ export const Default = {
|
||||||
await expect(icon).toBeInTheDocument();
|
await expect(icon).toBeInTheDocument();
|
||||||
await expect(icon).toHaveAttribute('src', 'https://misskey-hub.net/favicon.ico');
|
await expect(icon).toHaveAttribute('src', 'https://misskey-hub.net/favicon.ico');
|
||||||
*/
|
*/
|
||||||
await userEvent.unhover(a);
|
await waitFor(() => userEvent.unhover(a));
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
url: 'https://misskey-hub.net/',
|
url: 'https://misskey-hub.net/',
|
||||||
|
|
|
@ -26,10 +26,10 @@ export const Default = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async play({ canvasElement }) {
|
async play({ canvasElement }) {
|
||||||
await expect(canvasElement).toHaveTextContent(userDetailed.name);
|
await expect(canvasElement).toHaveTextContent(userDetailed().name);
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
user: userDetailed,
|
user: userDetailed(),
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'centered',
|
layout: 'centered',
|
||||||
|
@ -38,12 +38,12 @@ export const Default = {
|
||||||
export const Anonymous = {
|
export const Anonymous = {
|
||||||
...Default,
|
...Default,
|
||||||
async play({ canvasElement }) {
|
async play({ canvasElement }) {
|
||||||
await expect(canvasElement).toHaveTextContent(userDetailed.username);
|
await expect(canvasElement).toHaveTextContent(userDetailed().username);
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
...Default.args,
|
...Default.args,
|
||||||
user: {
|
user: {
|
||||||
...userDetailed,
|
...userDetailed(),
|
||||||
name: null,
|
name: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue