This commit is contained in:
samunohito 2024-01-21 11:39:52 +09:00
parent e47a2a52aa
commit 457a0a19ec
12 changed files with 517 additions and 21963 deletions

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<button v-for="item in items" class="_button item" :class="{ disabled: item.hidden }" @click="onClick(item)">
<span class="box" :style="{ background: chart.config.type === 'line' ? item.strokeStyle?.toString() : item.fillStyle?.toString() }"></span>
<span class="root" :style="{ background: chart.config.type === 'line' ? item.strokeStyle?.toString() : item.fillStyle?.toString() }"></span>
{{ item.text }}
</button>
</div>
@ -67,7 +67,7 @@ defineExpose({
opacity: 0.5;
}
> .box {
> .root {
display: inline-block;
width: 12px;
height: 12px;

View File

@ -20,6 +20,7 @@
worker-src 'self';
script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com;
style-src 'self' 'unsafe-inline';
font-src 'self' data:;
img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com;

View File

@ -1,6 +1,11 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<img :src="emojiUrl" :alt="emojiName"/>
<div class="root">
<img class="thumbnail" :src="emojiUrl" :alt="emojiName"/>
</div>
</template>
@ -17,13 +22,19 @@ const props = defineProps<{
const emojiUrl = computed(() => props.params.data?.url);
const emojiName = computed(() => props.params.data?.name);
console.log({ url: emojiUrl.value, name: emojiName.value });
</script>
<style lang="scss" module>
<style lang="scss">
.root {
display: inline-flex;
align-items: stretch;
display: flex;
align-items: center;
}
.thumbnail {
object-fit: cover;
width: 26px;
height: auto;
max-width: 100%;
max-height: 100%;
}
</style>

View File

@ -1,11 +1,64 @@
export type GridItem = {
id?: string;
aliases: string;
name: string;
category: string;
license: string;
isSensitive: boolean;
localOnly: boolean;
roleIdsThatCanBeUsedThisEmojiAsReaction: string;
url: string;
};
import * as Misskey from 'misskey-js';
export class GridItem {
readonly id?: string;
readonly url?: string;
readonly blob?: Blob;
public aliases: string;
public name: string;
public category: string;
public license: string;
public isSensitive: boolean;
public localOnly: boolean;
public roleIdsThatCanBeUsedThisEmojiAsReaction: string;
private readonly origin: string;
private constructor(
id: string | undefined,
url: string | undefined = undefined,
blob: Blob | undefined = undefined,
aliases: string,
name: string,
category: string,
license: string,
isSensitive: boolean,
localOnly: boolean,
roleIdsThatCanBeUsedThisEmojiAsReaction: string,
) {
this.id = id;
this.url = url;
this.blob = blob;
this.aliases = aliases;
this.name = name;
this.category = category;
this.license = license;
this.isSensitive = isSensitive;
this.localOnly = localOnly;
this.roleIdsThatCanBeUsedThisEmojiAsReaction = roleIdsThatCanBeUsedThisEmojiAsReaction;
this.origin = JSON.stringify(this);
}
static ofEmojiDetailed(it: Misskey.entities.EmojiDetailed): GridItem {
return new GridItem(
it.id,
it.url,
undefined,
it.aliases.join(', '),
it.name,
it.category ?? '',
it.license ?? '',
it.isSensitive,
it.localOnly,
it.roleIdsThatCanBeUsedThisEmojiAsReaction.join(', '),
);
}
public get edited(): boolean {
const { origin, ..._this } = this;
return JSON.stringify(_this) !== origin;
}
}

View File

@ -4,50 +4,63 @@
<template #header>
<MkPageHeader/>
</template>
<MkSpacer :contentMax="900">
<div class="_gaps">
<AgGridVue
:rowData="gridItems"
:columnDefs="colDefs"
style="height: 500px"
class="ag-theme-quartz"
>
</AgGridVue>
</div>
</MkSpacer>
<div class="_gaps" :class="$style.root">
<AgGridVue
:rowData="gridItems"
:columnDefs="colDefs"
:rowSelection="'multiple'"
:rowClassRules="rowClassRules"
style="height: 500px"
class="ag-theme-quartz-auto-dark"
@gridReady="onGridReady"
>
</AgGridVue>
</div>
</MkStickyContainer>
</div>
</template>
<script lang="ts">
import { markRaw, onMounted, ref, watch } from 'vue';
import 'ag-grid-community/styles/ag-grid.css'; // Core CSS
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';
import * as Misskey from 'misskey-js';
import { AgGridVue } from 'ag-grid-vue3';
import { ColDef } from 'ag-grid-community';
import { ColDef, GridApi, GridReadyEvent, RowClassRules } from 'ag-grid-community';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { GridItem } from '@/pages/admin/custom-emojis-grid.impl.js';
import CustomEmojiGridItem from '@/pages/admin/custom-emojis-grid-item.vue';
import CustomEmojisGridEmoji from '@/pages/admin/custom-emojis-grid-emoji.vue';
// eslint-disable-next-line import/no-default-export
export default {
components: {
AgGridVue,
// eslint-disable-next-line vue/no-unused-components
CustomEmojiGridItem,
customEmojisGridEmoji: CustomEmojisGridEmoji,
},
setup() {
const colDefs = markRaw<ColDef[]>([
{ field: 'img', cellRenderer: CustomEmojiGridItem },
{ field: 'name' },
{ field: 'category' },
{ field: 'aliases' },
{ field: 'license' },
{ field: 'isSensitive' },
{ field: 'localOnly' },
{ field: 'roleIdsThatCanBeUsedThisEmojiAsReaction' },
{
field: 'img',
headerName: '',
initialWidth: 90,
cellRenderer: 'customEmojisGridEmoji',
checkboxSelection: true,
},
{ field: 'name', headerName: 'name', initialWidth: 140, editable: true },
{ field: 'category', headerName: 'category', initialWidth: 140, editable: true },
{ field: 'aliases', headerName: 'aliases', initialWidth: 140, editable: true },
{ field: 'license', headerName: 'license', initialWidth: 140, editable: true },
{ field: 'isSensitive', headerName: 'sensitive', initialWidth: 90, editable: true },
{ field: 'localOnly', headerName: 'localOnly', initialWidth: 90, editable: true },
{ field: 'roleIdsThatCanBeUsedThisEmojiAsReaction', headerName: 'role', initialWidth: 140, editable: true },
]);
const rowClassRules = markRaw<RowClassRules<GridItem>>({
'emoji-grid-row-edited': params => params.data?.edited ?? false,
});
const gridApi = ref<GridApi>();
const customEmojis = ref<Misskey.entities.EmojiDetailed[]>([]);
const gridItems = ref<GridItem[]>([]);
@ -56,17 +69,8 @@ export default {
};
const refreshGridItems = () => {
gridItems.value = customEmojis.value.map(it => ({
id: it.id,
aliases: it.aliases.join(', '),
name: it.name,
category: it.category ?? '',
license: it.license ?? '',
isSensitive: it.isSensitive,
localOnly: it.localOnly,
roleIdsThatCanBeUsedThisEmojiAsReaction: it.roleIdsThatCanBeUsedThisEmojiAsReaction.join(', '),
url: it.url,
}));
console.log(customEmojis.value);
gridItems.value = customEmojis.value.map(it => GridItem.ofEmojiDetailed(it));
};
watch(customEmojis, refreshGridItems);
@ -76,15 +80,29 @@ export default {
refreshGridItems();
});
function onGridReady(params: GridReadyEvent) {
gridApi.value = params.api;
}
return {
colDefs,
rowClassRules,
customEmojis,
gridItems,
onGridReady,
};
},
};
</script>
<style scoped lang="scss">
<style lang="scss">
.emoji-grid-row-edited {
background-color: var(--ag-advanced-filter-column-pill-color);
}
</style>
<style module lang="scss">
.root {
padding: 16px
}
</style>

View File

@ -1,6 +1,6 @@
import { Endpoints as Gen } from './autogen/endpoint.js';
import { UserDetailed } from './autogen/models.js';
import { UsersShowRequest } from './autogen/entities.js';
import { EmojiDetailed, EmojiSimple, UserDetailed } from './autogen/models.js';
import { EmojisRequest, UsersShowRequest } from './autogen/entities.js';
import {
SigninRequest,
SigninResponse,

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
* version: 2023.12.2
* generatedAt: 2024-01-20T04:59:59.766Z
* generatedAt: 2024-01-20T09:06:01.853Z
*/
import type {
@ -366,6 +366,7 @@ import type {
InviteLimitResponse,
MetaRequest,
MetaResponse,
EmojisRequest,
EmojisResponse,
EmojiRequest,
EmojiResponse,
@ -544,16 +545,6 @@ import type {
BubbleGameRegisterResponse,
BubbleGameRankingRequest,
BubbleGameRankingResponse,
ReversiCancelMatchRequest,
ReversiCancelMatchResponse,
ReversiGamesRequest,
ReversiGamesResponse,
ReversiMatchRequest,
ReversiMatchResponse,
ReversiInvitationsResponse,
ReversiShowGameRequest,
ReversiShowGameResponse,
ReversiSurrenderRequest,
} from './entities.js';
export type Endpoints = {
@ -803,7 +794,7 @@ export type Endpoints = {
'invite/list': { req: InviteListRequest; res: InviteListResponse };
'invite/limit': { req: EmptyRequest; res: InviteLimitResponse };
'meta': { req: MetaRequest; res: MetaResponse };
'emojis': { req: EmptyRequest; res: EmojisResponse };
'emojis': { req: EmojisRequest; res: EmojisResponse };
'emoji': { req: EmojiRequest; res: EmojiResponse };
'miauth/gen-token': { req: MiauthGenTokenRequest; res: MiauthGenTokenResponse };
'mute/create': { req: MuteCreateRequest; res: EmptyResponse };
@ -917,10 +908,4 @@ export type Endpoints = {
'retention': { req: EmptyRequest; res: RetentionResponse };
'bubble-game/register': { req: BubbleGameRegisterRequest; res: BubbleGameRegisterResponse };
'bubble-game/ranking': { req: BubbleGameRankingRequest; res: BubbleGameRankingResponse };
'reversi/cancel-match': { req: ReversiCancelMatchRequest; res: ReversiCancelMatchResponse };
'reversi/games': { req: ReversiGamesRequest; res: ReversiGamesResponse };
'reversi/match': { req: ReversiMatchRequest; res: ReversiMatchResponse };
'reversi/invitations': { req: EmptyRequest; res: ReversiInvitationsResponse };
'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse };
'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse };
}

View File

@ -1,6 +1,6 @@
/*
* version: 2023.12.2
* generatedAt: 2024-01-20T04:59:59.765Z
* generatedAt: 2024-01-20T09:06:01.852Z
*/
import { operations } from './types.js';
@ -368,6 +368,7 @@ export type InviteListResponse = operations['invite/list']['responses']['200']['
export type InviteLimitResponse = operations['invite/limit']['responses']['200']['content']['application/json'];
export type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
export type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
export type EmojisRequest = operations['emojis']['requestBody']['content']['application/json'];
export type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
export type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
export type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
@ -546,13 +547,3 @@ export type BubbleGameRegisterRequest = operations['bubble-game/register']['requ
export type BubbleGameRegisterResponse = operations['bubble-game/register']['responses']['200']['content']['application/json'];
export type BubbleGameRankingRequest = operations['bubble-game/ranking']['requestBody']['content']['application/json'];
export type BubbleGameRankingResponse = operations['bubble-game/ranking']['responses']['200']['content']['application/json'];
export type ReversiCancelMatchRequest = operations['reversi/cancel-match']['requestBody']['content']['application/json'];
export type ReversiCancelMatchResponse = operations['reversi/cancel-match']['responses']['200']['content']['application/json'];
export type ReversiGamesRequest = operations['reversi/games']['requestBody']['content']['application/json'];
export type ReversiGamesResponse = operations['reversi/games']['responses']['200']['content']['application/json'];
export type ReversiMatchRequest = operations['reversi/match']['requestBody']['content']['application/json'];
export type ReversiMatchResponse = operations['reversi/match']['responses']['200']['content']['application/json'];
export type ReversiInvitationsResponse = operations['reversi/invitations']['responses']['200']['content']['application/json'];
export type ReversiShowGameRequest = operations['reversi/show-game']['requestBody']['content']['application/json'];
export type ReversiShowGameResponse = operations['reversi/show-game']['responses']['200']['content']['application/json'];
export type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json'];

View File

@ -1,6 +1,6 @@
/*
* version: 2023.12.2
* generatedAt: 2024-01-20T04:59:59.764Z
* generatedAt: 2024-01-20T09:06:01.851Z
*/
import { components } from './types.js';
@ -41,5 +41,3 @@ export type Flash = components['schemas']['Flash'];
export type Signin = components['schemas']['Signin'];
export type RoleLite = components['schemas']['RoleLite'];
export type Role = components['schemas']['Role'];
export type ReversiGameLite = components['schemas']['ReversiGameLite'];
export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];

View File

@ -3,7 +3,7 @@
/*
* version: 2023.12.2
* generatedAt: 2024-01-20T04:59:59.681Z
* generatedAt: 2024-01-20T09:06:01.771Z
*/
/**
@ -3472,60 +3472,6 @@ export type paths = {
*/
post: operations['bubble-game/ranking'];
};
'/reversi/cancel-match': {
/**
* reversi/cancel-match
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
post: operations['reversi/cancel-match'];
};
'/reversi/games': {
/**
* reversi/games
* @description No description provided.
*
* **Credential required**: *No*
*/
post: operations['reversi/games'];
};
'/reversi/match': {
/**
* reversi/match
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
post: operations['reversi/match'];
};
'/reversi/invitations': {
/**
* reversi/invitations
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *read:account*
*/
post: operations['reversi/invitations'];
};
'/reversi/show-game': {
/**
* reversi/show-game
* @description No description provided.
*
* **Credential required**: *No*
*/
post: operations['reversi/show-game'];
};
'/reversi/surrender': {
/**
* reversi/surrender
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
post: operations['reversi/surrender'];
};
};
export type webhooks = Record<string, never>;
@ -4458,68 +4404,6 @@ export type components = {
};
usersCount: number;
});
ReversiGameLite: {
/** Format: id */
id: string;
/** Format: date-time */
createdAt: string;
/** Format: date-time */
startedAt: string | null;
isStarted: boolean;
isEnded: boolean;
form1: Record<string, never> | null;
form2: Record<string, never> | null;
user1Ready: boolean;
user2Ready: boolean;
/** Format: id */
user1Id: string;
/** Format: id */
user2Id: string;
user1: components['schemas']['User'];
user2: components['schemas']['User'];
/** Format: id */
winnerId: string | null;
winner: components['schemas']['User'] | null;
/** Format: id */
surrendered: string | null;
black: number | null;
bw: string;
isLlotheo: boolean;
canPutEverywhere: boolean;
loopedBoard: boolean;
};
ReversiGameDetailed: {
/** Format: id */
id: string;
/** Format: date-time */
createdAt: string;
/** Format: date-time */
startedAt: string | null;
isStarted: boolean;
isEnded: boolean;
form1: Record<string, never> | null;
form2: Record<string, never> | null;
user1Ready: boolean;
user2Ready: boolean;
/** Format: id */
user1Id: string;
/** Format: id */
user2Id: string;
user1: components['schemas']['User'];
user2: components['schemas']['User'];
/** Format: id */
winnerId: string | null;
winner: components['schemas']['User'] | null;
/** Format: id */
surrendered: string | null;
black: number | null;
bw: string;
isLlotheo: boolean;
canPutEverywhere: boolean;
loopedBoard: boolean;
logs: unknown[][];
map: string[];
};
};
responses: never;
parameters: never;
@ -19012,12 +18896,19 @@ export type operations = {
* **Credential required**: *No*
*/
emojis: {
requestBody: {
content: {
'application/json': {
detail?: boolean | null;
};
};
};
responses: {
/** @description OK (with results) */
200: {
content: {
'application/json': {
emojis: components['schemas']['EmojiSimple'][];
emojis: (components['schemas']['EmojiSimple'] | components['schemas']['EmojiDetailed'])[];
};
};
};
@ -25658,325 +25549,5 @@ export type operations = {
};
};
};
/**
* reversi/cancel-match
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
'reversi/cancel-match': {
requestBody: {
content: {
'application/json': {
/** Format: misskey:id */
userId?: string | null;
};
};
};
responses: {
/** @description OK (with results) */
200: {
content: {
'application/json': unknown;
};
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/**
* reversi/games
* @description No description provided.
*
* **Credential required**: *No*
*/
'reversi/games': {
requestBody: {
content: {
'application/json': {
/** @default 10 */
limit?: number;
/** Format: misskey:id */
sinceId?: string;
/** Format: misskey:id */
untilId?: string;
/** @default false */
my?: boolean;
};
};
};
responses: {
/** @description OK (with results) */
200: {
content: {
'application/json': components['schemas']['ReversiGameLite'][];
};
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/**
* reversi/match
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
'reversi/match': {
requestBody: {
content: {
'application/json': {
/** Format: misskey:id */
userId?: string | null;
};
};
};
responses: {
/** @description OK (with results) */
200: {
content: {
'application/json': unknown;
};
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/**
* reversi/invitations
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *read:account*
*/
'reversi/invitations': {
responses: {
/** @description OK (with results) */
200: {
content: {
'application/json': components['schemas']['UserLite'][];
};
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/**
* reversi/show-game
* @description No description provided.
*
* **Credential required**: *No*
*/
'reversi/show-game': {
requestBody: {
content: {
'application/json': {
/** Format: misskey:id */
gameId: string;
};
};
};
responses: {
/** @description OK (with results) */
200: {
content: {
'application/json': components['schemas']['ReversiGameDetailed'];
};
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/**
* reversi/surrender
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
'reversi/surrender': {
requestBody: {
content: {
'application/json': {
/** Format: misskey:id */
gameId: string;
};
};
};
responses: {
/** @description OK (without any results) */
204: {
content: never;
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
};

File diff suppressed because it is too large Load Diff