merge: Better beta emoji panel (!1146)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1146 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Hazelnoot <acomputerdog@gmail.com>
This commit is contained in:
commit
018b3d3dee
3 changed files with 130 additions and 67 deletions
|
|
@ -90,6 +90,8 @@ import MkPagingButtons from '@/components/MkPagingButtons.vue';
|
|||
import { selectFile } from '@/utility/select-file.js';
|
||||
import { copyGridDataToClipboard, removeDataFromGrid } from '@/components/grid/grid-utils.js';
|
||||
import { useLoading } from '@/components/hook/useLoading.js';
|
||||
import { retryOnThrottled } from '@@/js/retry-on-throttled';
|
||||
import promiseLimit from "promise-limit";
|
||||
|
||||
type GridItem = {
|
||||
checked: boolean;
|
||||
|
|
@ -326,28 +328,39 @@ async function onUpdateButtonClicked() {
|
|||
return;
|
||||
}
|
||||
|
||||
const action = () => {
|
||||
return updatedItems.map(item =>
|
||||
misskeyApi(
|
||||
'admin/emoji/update',
|
||||
{
|
||||
// eslint-disable-next-line
|
||||
id: item.id!,
|
||||
name: item.name,
|
||||
category: emptyStrToNull(item.category),
|
||||
aliases: emptyStrToEmptyArray(item.aliases),
|
||||
license: emptyStrToNull(item.license),
|
||||
isSensitive: item.isSensitive,
|
||||
localOnly: item.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: item.roleIdsThatCanBeUsedThisEmojiAsReaction.map(it => it.id),
|
||||
fileId: item.fileId,
|
||||
})
|
||||
.then(() => ({ item, success: true, err: undefined }))
|
||||
.catch(err => ({ item, success: false, err })),
|
||||
);
|
||||
const delay = (ms: number) => new Promise(resolve => window.setTimeout(resolve, ms));
|
||||
|
||||
type ApiResponse = {
|
||||
item: any;
|
||||
success: boolean;
|
||||
err?: unknown;
|
||||
};
|
||||
|
||||
const result = await os.promiseDialog(Promise.all(action()));
|
||||
const execute = async (item: any): Promise<ApiResponse> => {
|
||||
try {
|
||||
await retryOnThrottled(() => misskeyApi('admin/emoji/update', {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
category: emptyStrToNull(item.category),
|
||||
aliases: emptyStrToEmptyArray(item.aliases),
|
||||
license: emptyStrToNull(item.license),
|
||||
isSensitive: item.isSensitive,
|
||||
localOnly: item.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: item.roleIdsThatCanBeUsedThisEmojiAsReaction.map((it: any) => it.id),
|
||||
fileId: item.fileId,
|
||||
}));
|
||||
return { item, success: true };
|
||||
} catch (error) {
|
||||
return { item, success: false, err: error };
|
||||
}
|
||||
};
|
||||
|
||||
const action = async (): Promise<ApiResponse[]> => {
|
||||
const limit = promiseLimit<ApiResponse>(2);
|
||||
return await Promise.all(updatedItems.map(async it => limit(() => execute(it))));
|
||||
};
|
||||
|
||||
const result = await os.promiseDialog(action());
|
||||
const failedItems = result.filter(it => !it.success);
|
||||
|
||||
if (failedItems.length > 0) {
|
||||
|
|
|
|||
|
|
@ -75,17 +75,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { onMounted, ref, useCssModule } from 'vue';
|
||||
import { retryOnThrottled } from '@@/js/retry-on-throttled';
|
||||
import promiseLimit from 'promise-limit';
|
||||
import type { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
|
||||
import { emptyStrToEmptyArray, emptyStrToNull, roleIdsParser } from '@/pages/admin/custom-emojis-manager.impl.js';
|
||||
import type { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
|
||||
import type { DroppedFile } from '@/utility/file-drop.js';
|
||||
import { extractDroppedItems, flattenDroppedFiles } from '@/utility/file-drop.js';
|
||||
import type { GridSetting } from '@/components/grid/grid.js';
|
||||
import type { GridRow } from '@/components/grid/row.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import {
|
||||
emptyStrToEmptyArray,
|
||||
emptyStrToNull,
|
||||
roleIdsParser,
|
||||
} from '@/pages/admin/custom-emojis-manager.impl.js';
|
||||
import MkGrid from '@/components/grid/MkGrid.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
|
|
@ -96,7 +95,6 @@ import * as os from '@/os.js';
|
|||
import { validators } from '@/components/grid/cell-validators.js';
|
||||
import { chooseFileFromDrive, chooseFileFromPc } from '@/utility/select-file.js';
|
||||
import { uploadFile } from '@/utility/upload.js';
|
||||
import { extractDroppedItems, flattenDroppedFiles } from '@/utility/file-drop.js';
|
||||
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue';
|
||||
import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js';
|
||||
|
||||
|
|
@ -247,7 +245,56 @@ const registerButtonDisabled = ref<boolean>(false);
|
|||
const requestLogs = ref<RequestLogItem[]>([]);
|
||||
const isDragOver = ref<boolean>(false);
|
||||
|
||||
async function onRegistryClicked() {
|
||||
type ApiResponse = {
|
||||
item: any;
|
||||
success: boolean;
|
||||
err?: unknown;
|
||||
};
|
||||
|
||||
const execute = async (item: any, apiEndpoint: string, payload: any): Promise<ApiResponse> => {
|
||||
try {
|
||||
await retryOnThrottled(() => misskeyApi(apiEndpoint, payload));
|
||||
return { item, success: true };
|
||||
} catch (err) {
|
||||
return { item, success: false, err };
|
||||
}
|
||||
};
|
||||
|
||||
const importEmojis = async (targets: any[]): Promise<void> => {
|
||||
const confirm = await os.confirm({
|
||||
type: 'info',
|
||||
title: i18n.ts._customEmojisManager._remote.confirmImportEmojisTitle,
|
||||
text: i18n.tsx._customEmojisManager._remote.confirmImportEmojisDescription({ count: targets.length }),
|
||||
});
|
||||
|
||||
if (confirm.canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function action(): Promise<ApiResponse[]> {
|
||||
const limit = promiseLimit<ApiResponse>(3);
|
||||
return await Promise.all(targets.map(item => limit(() => execute(item, 'admin/emoji/copy', { emojiId: item.id }))));
|
||||
}
|
||||
|
||||
const result = await os.promiseDialog(action());
|
||||
const failedItems = result.filter(it => !it.success);
|
||||
if (failedItems.length > 0) {
|
||||
await os.alert({
|
||||
type: 'error',
|
||||
title: i18n.ts.somethingHappened,
|
||||
text: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedDescription,
|
||||
});
|
||||
}
|
||||
|
||||
requestLogs.value = result.map(it => ({
|
||||
failed: !it.success,
|
||||
url: it.item.url,
|
||||
name: it.item.name,
|
||||
error: it.err ? JSON.stringify(it.err) : undefined,
|
||||
}));
|
||||
};
|
||||
|
||||
const onRegistryClicked = async (): Promise<void> => {
|
||||
const dialogSelection = await os.confirm({
|
||||
type: 'info',
|
||||
text: i18n.tsx._customEmojisManager._local._register.confirmRegisterEmojisDescription({ count: MAXIMUM_EMOJI_REGISTER_COUNT }),
|
||||
|
|
@ -257,29 +304,24 @@ async function onRegistryClicked() {
|
|||
return;
|
||||
}
|
||||
|
||||
const items = gridItems.value;
|
||||
const upload = () => {
|
||||
return items.slice(0, MAXIMUM_EMOJI_REGISTER_COUNT)
|
||||
.map(item =>
|
||||
misskeyApi(
|
||||
'admin/emoji/add', {
|
||||
name: item.name,
|
||||
category: emptyStrToNull(item.category),
|
||||
aliases: emptyStrToEmptyArray(item.aliases),
|
||||
license: emptyStrToNull(item.license),
|
||||
isSensitive: item.isSensitive,
|
||||
localOnly: item.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: item.roleIdsThatCanBeUsedThisEmojiAsReaction.map(it => it.id),
|
||||
fileId: item.fileId!,
|
||||
})
|
||||
.then(() => ({ item, success: true, err: undefined }))
|
||||
.catch(err => ({ item, success: false, err })),
|
||||
);
|
||||
};
|
||||
const items = gridItems.value.slice(0, MAXIMUM_EMOJI_REGISTER_COUNT);
|
||||
|
||||
const result = await os.promiseDialog(Promise.all(upload()));
|
||||
async function action(): Promise<ApiResponse[]> {
|
||||
const limit = promiseLimit<ApiResponse>(2);
|
||||
return await Promise.all(items.map(item => limit(() => execute(item, 'admin/emoji/add', {
|
||||
name: item.name,
|
||||
category: emptyStrToNull(item.category),
|
||||
aliases: emptyStrToEmptyArray(item.aliases),
|
||||
license: emptyStrToNull(item.license),
|
||||
isSensitive: item.isSensitive,
|
||||
localOnly: item.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: item.roleIdsThatCanBeUsedThisEmojiAsReaction.map((it: any) => it.id),
|
||||
fileId: item.fileId!,
|
||||
}))));
|
||||
}
|
||||
|
||||
const result = await os.promiseDialog(action());
|
||||
const failedItems = result.filter(it => !it.success);
|
||||
|
||||
if (failedItems.length > 0) {
|
||||
await os.alert({
|
||||
type: 'error',
|
||||
|
|
@ -295,10 +337,10 @@ async function onRegistryClicked() {
|
|||
error: it.err ? JSON.stringify(it.err) : undefined,
|
||||
}));
|
||||
|
||||
// 登録に成功したものは一覧から除く
|
||||
// Remove successfully registered items from the list
|
||||
const successItems = result.filter(it => it.success).map(it => it.item);
|
||||
gridItems.value = gridItems.value.filter(it => !successItems.includes(it));
|
||||
}
|
||||
};
|
||||
|
||||
async function onClearClicked() {
|
||||
const result = await os.confirm({
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, useCssModule } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { retryOnThrottled } from '@@/js/retry-on-throttled';
|
||||
import type { GridSortOrderKey, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
|
||||
import type { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
|
||||
import type { GridSetting } from '@/components/grid/grid.js';
|
||||
|
|
@ -160,6 +161,7 @@ import { deviceKind } from '@/utility/device-kind.js';
|
|||
import MkPagingButtons from '@/components/MkPagingButtons.vue';
|
||||
import MkSortOrderEditor from '@/components/MkSortOrderEditor.vue';
|
||||
import { useLoading } from '@/components/hook/useLoading.js';
|
||||
import promiseLimit from "promise-limit";
|
||||
|
||||
type GridItem = {
|
||||
checked: boolean;
|
||||
|
|
@ -310,7 +312,24 @@ function onGridCellValueChange(event: GridCellValueChangeEvent) {
|
|||
}
|
||||
}
|
||||
|
||||
async function importEmojis(targets: GridItem[]) {
|
||||
type ApiResponse = {
|
||||
item: any;
|
||||
success: boolean;
|
||||
err?: unknown;
|
||||
};
|
||||
|
||||
const execute = async (item: any): Promise<ApiResponse> => {
|
||||
try {
|
||||
await retryOnThrottled(() => misskeyApi('admin/emoji/copy', {
|
||||
emojiId: item.id,
|
||||
}));
|
||||
return { item, success: true };
|
||||
} catch (err) {
|
||||
return { item, success: false, err };
|
||||
}
|
||||
};
|
||||
|
||||
const importEmojis = async (targets: any[]): Promise<void> => {
|
||||
const confirm = await os.confirm({
|
||||
type: 'info',
|
||||
title: i18n.ts._customEmojisManager._remote.confirmImportEmojisTitle,
|
||||
|
|
@ -321,21 +340,10 @@ async function importEmojis(targets: GridItem[]) {
|
|||
return;
|
||||
}
|
||||
|
||||
const result = await os.promiseDialog(
|
||||
Promise.all(
|
||||
targets.map(item =>
|
||||
misskeyApi(
|
||||
'admin/emoji/copy',
|
||||
{
|
||||
emojiId: item.id!,
|
||||
})
|
||||
.then(() => ({ item, success: true, err: undefined }))
|
||||
.catch(err => ({ item, success: false, err })),
|
||||
),
|
||||
),
|
||||
);
|
||||
const failedItems = result.filter(it => !it.success);
|
||||
const limit = promiseLimit<ApiResponse>(2);
|
||||
const results = await Promise.all(targets.map(it => limit(() => execute(it))));
|
||||
|
||||
const failedItems = results.filter(it => !it.success);
|
||||
if (failedItems.length > 0) {
|
||||
await os.alert({
|
||||
type: 'error',
|
||||
|
|
@ -344,7 +352,7 @@ async function importEmojis(targets: GridItem[]) {
|
|||
});
|
||||
}
|
||||
|
||||
requestLogs.value = result.map(it => ({
|
||||
requestLogs.value = results.map(it => ({
|
||||
failed: !it.success,
|
||||
url: it.item.url,
|
||||
name: it.item.name,
|
||||
|
|
@ -352,7 +360,7 @@ async function importEmojis(targets: GridItem[]) {
|
|||
}));
|
||||
|
||||
await refreshCustomEmojis();
|
||||
}
|
||||
};
|
||||
|
||||
async function refreshCustomEmojis() {
|
||||
const query: Misskey.entities.V2AdminEmojiListRequest['query'] = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue