This commit is contained in:
samunohito 2024-02-27 08:20:04 +09:00
parent 9a6ee0370c
commit 0aee64ca40
10 changed files with 379 additions and 84 deletions

224
locales/index.d.ts vendored
View file

@ -36,6 +36,10 @@ export interface Locale extends ILocale {
* *
*/ */
"search": string; "search": string;
/**
*
*/
"reset": string;
/** /**
* *
*/ */
@ -9732,6 +9736,226 @@ export interface Locale extends ILocale {
*/ */
"header": string; "header": string;
}; };
"_gridComponent": {
"_error": {
/**
*
*/
"requiredValue": string;
/**
* 正規表現によるバリデーションはtype:textのカラムのみサポートします
*/
"columnTypeNotSupport": string;
/**
* {pattern}
*/
"patternNotMatch": ParameterizedString<"pattern">;
/**
*
*/
"notUnique": string;
};
};
"_roleSelectDialog": {
/**
*
*/
"notSelected": string;
};
"_customEmojisManager": {
"_gridCommon": {
/**
*
*/
"copySelectionRows": string;
/**
*
*/
"copySelectionRanges": string;
/**
*
*/
"deleteSelectionRows": string;
/**
*
*/
"deleteSelectionRanges": string;
/**
*
*/
"searchSettings": string;
/**
*
*/
"searchSettingCaption": string;
/**
*
*/
"sortOrder": string;
/**
*
*/
"registrationLogs": string;
/**
*
*/
"registrationLogsCaption": string;
/**
*
*/
"alertEmojisRegisterFailedTitle": string;
/**
*
*/
"alertEmojisRegisterFailedDescription": string;
};
"_logs": {
/**
*
*/
"showSuccessLogSwitch": string;
/**
*
*/
"failureLogNothing": string;
/**
*
*/
"logNothing": string;
};
"_remote": {
/**
*
*/
"importSelectionRows": string;
/**
*
*/
"importSelectionRangesRows": string;
/**
*
*/
"importEmojisButton": string;
/**
*
*/
"confirmImportEmojisTitle": string;
/**
* {count}
*/
"confirmImportEmojisDescription": ParameterizedString<"count">;
};
"_local": {
/**
*
*/
"tabTitleList": string;
/**
*
*/
"tabTitleRegister": string;
"_list": {
/**
*
*/
"emojisNothing": string;
/**
*
*/
"markAsDeleteTargetRows": string;
/**
*
*/
"markAsDeleteTargetRanges": string;
/**
*
*/
"alertUpdateEmojisNothingDescription": string;
/**
*
*/
"alertDeleteEmojisNothingDescription": string;
/**
*
*/
"confirmUpdateEmojisTitle": string;
/**
* {count}
*/
"confirmUpdateEmojisDescription": ParameterizedString<"count">;
/**
*
*/
"confirmDeleteEmojisTitle": string;
/**
* {count}
*/
"confirmDeleteEmojisDescription": ParameterizedString<"count">;
/**
*
*/
"dialogSelectRoleTitle": string;
};
"_register": {
/**
*
*/
"uploadSettingTitle": string;
/**
*
*/
"uploadSettingDescription": string;
/**
* "category"
*/
"directoryToCategoryLabel": string;
/**
* "category"
*/
"directoryToCategoryCaption": string;
/**
*
*/
"emojiInputAreaCaption": string;
/**
*
*/
"emojiInputAreaList1": string;
/**
* PCから選択する
*/
"emojiInputAreaList2": string;
/**
*
*/
"emojiInputAreaList3": string;
/**
*
*/
"confirmRegisterEmojisTitle": string;
/**
* {count}
*/
"confirmRegisterEmojisDescription": ParameterizedString<"count">;
/**
*
*/
"confirmClearEmojisTitle": string;
/**
*
*/
"confirmClearEmojisDescription": string;
/**
*
*/
"confirmUploadEmojisTitle": string;
/**
* {count}
*/
"confirmUploadEmojisDescription": ParameterizedString<"count">;
};
};
};
} }
declare const locales: { declare const locales: {
[lang: string]: Locale; [lang: string]: Locale;

View file

@ -5,6 +5,7 @@ introMisskey: "ようこそMisskeyは、オープンソースの分散型マ
poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>Misskey</b>のサーバーのひとつです。" poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>Misskey</b>のサーバーのひとつです。"
monthAndDay: "{month}月 {day}日" monthAndDay: "{month}月 {day}日"
search: "検索" search: "検索"
reset: "リセット"
notifications: "通知" notifications: "通知"
username: "ユーザー名" username: "ユーザー名"
password: "パスワード" password: "パスワード"
@ -2593,3 +2594,65 @@ _offlineScreen:
title: "オフライン - サーバーに接続できません" title: "オフライン - サーバーに接続できません"
header: "サーバーに接続できません" header: "サーバーに接続できません"
_gridComponent:
_error:
requiredValue: "この値は必須項目です"
columnTypeNotSupport: "正規表現によるバリデーションはtype:textのカラムのみサポートします。"
patternNotMatch: "この値は{pattern}のパターンに一致しません"
notUnique: "この値は一意である必要があります"
_roleSelectDialog:
notSelected: "選択されていません"
_customEmojisManager:
_gridCommon:
copySelectionRows: "選択行をコピー"
copySelectionRanges: "選択範囲をコピー"
deleteSelectionRows: "選択行を削除"
deleteSelectionRanges: "選択範囲の行を削除"
searchSettings: "検索設定"
searchSettingCaption: "検索条件を詳細に設定します。"
sortOrder: "並び順"
registrationLogs: "登録ログ"
registrationLogsCaption: "絵文字更新・削除時のログが表示されます。更新・削除操作を行ったり、ページを遷移・リロードすると消えます。"
alertEmojisRegisterFailedTitle: "エラー"
alertEmojisRegisterFailedDescription: "絵文字の更新・削除に失敗しました。詳細は登録ログをご確認ください。"
_logs:
showSuccessLogSwitch: "成功ログを表示"
failureLogNothing: "失敗ログはありません。"
logNothing: "ログはありません。"
_remote:
importSelectionRows: "選択行をインポート"
importSelectionRangesRows: "選択範囲の行をインポート"
importEmojisButton: "チェックがついた絵文字をインポート"
confirmImportEmojisTitle: "絵文字のインポート"
confirmImportEmojisDescription: "リモートから受信した{count}個の絵文字のインポートを行います。絵文字のライセンスに十分な注意を払ってください。実行しますか?"
_local:
tabTitleList: "登録済み絵文字一覧"
tabTitleRegister: "絵文字の登録"
_list:
emojisNothing: "登録された絵文字はありません。"
markAsDeleteTargetRows: "選択行を削除対象にする"
markAsDeleteTargetRanges: "選択範囲の行を削除対象にする"
alertUpdateEmojisNothingDescription: "変更された絵文字はありません。"
alertDeleteEmojisNothingDescription: "削除対象の絵文字はありません。"
confirmUpdateEmojisTitle: "確認"
confirmUpdateEmojisDescription: "{count}個の絵文字を更新します。実行しますか?"
confirmDeleteEmojisTitle: "確認"
confirmDeleteEmojisDescription: "チェックがつけられた{count}個の絵文字を削除します。実行しますか?"
dialogSelectRoleTitle: "絵文字に設定されたロールで検索"
_register:
uploadSettingTitle: "アップロード設定"
uploadSettingDescription: "この画面で絵文字アップロードを行う際の動作を設定できます。"
directoryToCategoryLabel: "ディレクトリ名を\"category\"に入力する"
directoryToCategoryCaption: "ディレクトリをドラッグ・ドロップした時に、ディレクトリ名を\"category\"に入力します。"
emojiInputAreaCaption: "いずれかの方法で登録する絵文字を選択してください。"
emojiInputAreaList1: "この枠に画像ファイルまたはディレクトリをドラッグ&ドロップ"
emojiInputAreaList2: "このリンクをクリックしてPCから選択する"
emojiInputAreaList3: "このリンクをクリックしてドライブから選択する"
confirmRegisterEmojisTitle: "確認"
confirmRegisterEmojisDescription: "リストに表示されている絵文字を新たなカスタム絵文字として登録します。よろしいですか?(負荷を避けるため、一度の操作で登録可能な絵文字は{count}件までです)"
confirmClearEmojisTitle: "確認"
confirmClearEmojisDescription: "編集内容を破棄し、リストに表示されている絵文字をクリアします。よろしいですか?"
confirmUploadEmojisTitle: "確認"
confirmUploadEmojisDescription: "ドラッグ&ドロップされた{count}個のファイルをドライブにアップロードします。実行しますか?"

View file

@ -24,7 +24,7 @@
</div> </div>
</div> </div>
<div v-else :class="$style.roleItemArea" style="text-align: center"> <div v-else :class="$style.roleItemArea" style="text-align: center">
何も選択されていません {{ i18n.ts._roleSelectDialog.notSelected }}
</div> </div>
<MkInfo v-if="infoMessage">{{ infoMessage }}</MkInfo> <MkInfo v-if="infoMessage">{{ infoMessage }}</MkInfo>

View file

@ -1,6 +1,7 @@
import { CellValue, GridCell } from '@/components/grid/cell.js'; import { CellValue, GridCell } from '@/components/grid/cell.js';
import { GridColumn } from '@/components/grid/column.js'; import { GridColumn } from '@/components/grid/column.js';
import { GridRow } from '@/components/grid/row.js'; import { GridRow } from '@/components/grid/row.js';
import { i18n } from '@/i18n.js';
export type ValidatorParams = { export type ValidatorParams = {
column: GridColumn; column: GridColumn;
@ -66,7 +67,7 @@ class ValidatorPreset {
validate: ({ value }): ValidatorResult => { validate: ({ value }): ValidatorResult => {
return { return {
valid: value !== null && value !== undefined && value !== '', valid: value !== null && value !== undefined && value !== '',
message: 'This field is required.', message: i18n.ts._gridComponent._error.requiredValue,
}; };
}, },
}; };
@ -79,13 +80,13 @@ class ValidatorPreset {
if (column.setting.type !== 'text') { if (column.setting.type !== 'text') {
return { return {
valid: false, valid: false,
message: 'Regex validation is only available for text type.', message: i18n.ts._gridComponent._error.columnTypeNotSupport,
}; };
} }
return { return {
valid: pattern.test(value?.toString() ?? ''), valid: pattern.test(value?.toString() ?? ''),
message: 'Not an allowed format. Please check the input. (Allowed format: ' + pattern.source + ')', message: i18n.tsx._gridComponent._error.patternNotMatch({ pattern: pattern.source }),
}; };
}, },
}; };
@ -101,7 +102,7 @@ class ValidatorPreset {
.every(cell => cell.value !== value); .every(cell => cell.value !== value);
return { return {
valid: isUnique, valid: isUnique,
message: 'This value is already used.', message: i18n.ts._gridComponent._error.notUnique,
}; };
}, },
}; };

View file

@ -1,14 +1,14 @@
<template> <template>
<div> <div>
<div v-if="gridItems.length === 0" style="text-align: center"> <div v-if="gridItems.length === 0" style="text-align: center">
登録された絵文字はありません {{ i18n.ts._customEmojisManager._local._list.emojisNothing }}
</div> </div>
<div v-else class="_gaps"> <div v-else class="_gaps">
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-search"></i></template> <template #icon><i class="ti ti-search"></i></template>
<template #label>検索設定</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.searchSettings }}</template>
<template #caption> <template #caption>
検索条件を詳細に設定します {{ i18n.ts._customEmojisManager._gridCommon.searchSettingCaption }}
</template> </template>
<div class="_gaps"> <div class="_gaps">
@ -120,7 +120,7 @@
<MkFolder :spacerMax="8" :spacerMin="8"> <MkFolder :spacerMax="8" :spacerMin="8">
<template #icon><i class="ti ti-arrows-sort"></i></template> <template #icon><i class="ti ti-arrows-sort"></i></template>
<template #label>ソート順</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.sortOrder }}</template>
<div :class="$style.sortOrderArea"> <div :class="$style.sortOrderArea">
<div :class="$style.sortOrderAreaTags"> <div :class="$style.sortOrderAreaTags">
<MkTagItem <MkTagItem
@ -144,7 +144,7 @@
{{ i18n.ts.search }} {{ i18n.ts.search }}
</MkButton> </MkButton>
<MkButton @click="onQueryResetButtonClicked"> <MkButton @click="onQueryResetButtonClicked">
リセット {{ i18n.ts.reset }}
</MkButton> </MkButton>
</div> </div>
</div> </div>
@ -152,9 +152,9 @@
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-notes"></i></template> <template #icon><i class="ti ti-notes"></i></template>
<template #label>登録ログ</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #caption> <template #caption>
絵文字更新削除時のログが表示されます更新削除操作を行ったりページをリロードすると消えます {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
</template> </template>
<XRegisterLogs :logs="requestLogs"/> <XRegisterLogs :logs="requestLogs"/>
@ -174,7 +174,7 @@
i18n.ts.update i18n.ts.update
}} }}
</MkButton> </MkButton>
<MkButton @click="onGridResetButtonClicked">リセット</MkButton> <MkButton @click="onGridResetButtonClicked">{{ i18n.ts.reset }}</MkButton>
</div> </div>
</div> </div>
</div> </div>
@ -272,13 +272,13 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択行をコピー', text: i18n.ts._customEmojisManager._gridCommon.copySelectionRows,
icon: 'ti ti-copy', icon: 'ti ti-copy',
action: () => copyGridDataToClipboard(gridItems, context), action: () => copyGridDataToClipboard(gridItems, context),
}, },
{ {
type: 'button', type: 'button',
text: '選択行を削除対象とする', text: i18n.ts._customEmojisManager._local._list.markAsDeleteTargetRows,
icon: 'ti ti-trash', icon: 'ti ti-trash',
action: () => { action: () => {
for (const rangedRow of context.rangedRows) { for (const rangedRow of context.rangedRows) {
@ -362,7 +362,7 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択範囲をコピー', text: i18n.ts._customEmojisManager._gridCommon.copySelectionRanges,
icon: 'ti ti-copy', icon: 'ti ti-copy',
action: () => { action: () => {
return copyGridDataToClipboard(gridItems, context); return copyGridDataToClipboard(gridItems, context);
@ -370,7 +370,7 @@ function setupGrid(): GridSetting {
}, },
{ {
type: 'button', type: 'button',
text: '選択範囲を削除', text: i18n.ts._customEmojisManager._gridCommon.deleteSelectionRanges,
icon: 'ti ti-trash', icon: 'ti ti-trash',
action: () => { action: () => {
removeDataFromGrid(context, (cell) => { removeDataFromGrid(context, (cell) => {
@ -380,7 +380,7 @@ function setupGrid(): GridSetting {
}, },
{ {
type: 'button', type: 'button',
text: '選択行を削除対象とする', text: i18n.ts._customEmojisManager._local._list.markAsDeleteTargetRanges,
icon: 'ti ti-trash', icon: 'ti ti-trash',
action: () => { action: () => {
for (const rowIdx of [...new Set(context.rangedCells.map(it => it.row.index)).values()]) { for (const rowIdx of [...new Set(context.rangedCells.map(it => it.row.index)).values()]) {
@ -426,24 +426,24 @@ async function onUpdateButtonClicked() {
throw new Error('The number of items has been changed. Please refresh the page and try again.'); throw new Error('The number of items has been changed. Please refresh the page and try again.');
} }
const confirm = await os.confirm({
type: 'info',
title: '確認',
text: '絵文字の変更を保存します。よろしいですか?',
});
if (confirm.canceled) {
return;
}
const updatedItems = _items.filter((it, idx) => !it.checked && JSON.stringify(it) !== JSON.stringify(_originItems[idx])); const updatedItems = _items.filter((it, idx) => !it.checked && JSON.stringify(it) !== JSON.stringify(_originItems[idx]));
if (updatedItems.length === 0) { if (updatedItems.length === 0) {
await os.alert({ await os.alert({
type: 'info', type: 'info',
text: '変更された絵文字はありません。', text: i18n.ts._customEmojisManager._local._list.alertUpdateEmojisNothingDescription,
}); });
return; return;
} }
const confirm = await os.confirm({
type: 'info',
title: i18n.ts._customEmojisManager._local._list.confirmUpdateEmojisTitle,
text: i18n.tsx._customEmojisManager._local._list.confirmUpdateEmojisDescription({ count: updatedItems.length }),
});
if (confirm.canceled) {
return;
}
const action = () => { const action = () => {
return updatedItems.map(item => return updatedItems.map(item =>
misskeyApi( misskeyApi(
@ -471,8 +471,8 @@ async function onUpdateButtonClicked() {
if (failedItems.length > 0) { if (failedItems.length > 0) {
await os.alert({ await os.alert({
type: 'error', type: 'error',
title: 'エラー', title: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedTitle,
text: '絵文字の更新・削除に失敗しました。詳細は登録ログをご確認ください。', text: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedDescription,
}); });
} }
@ -493,24 +493,24 @@ async function onDeleteButtonClicked() {
throw new Error('The number of items has been changed. Please refresh the page and try again.'); throw new Error('The number of items has been changed. Please refresh the page and try again.');
} }
const confirm = await os.confirm({
type: 'info',
title: '確認',
text: 'チェックをつけられた絵文字を削除します。よろしいですか?',
});
if (confirm.canceled) {
return;
}
const deleteItems = _items.filter((it) => it.checked); const deleteItems = _items.filter((it) => it.checked);
if (deleteItems.length === 0) { if (deleteItems.length === 0) {
await os.alert({ await os.alert({
type: 'info', type: 'info',
text: '削除対象の絵文字はありません。', text: i18n.ts._customEmojisManager._local._list.alertDeleteEmojisNothingDescription,
}); });
return; return;
} }
const confirm = await os.confirm({
type: 'info',
title: i18n.ts._customEmojisManager._local._list.confirmDeleteEmojisTitle,
text: i18n.tsx._customEmojisManager._local._list.confirmDeleteEmojisDescription({ count: deleteItems.length }),
});
if (confirm.canceled) {
return;
}
async function action() { async function action() {
const deleteIds = deleteItems.map(it => it.id!); const deleteIds = deleteItems.map(it => it.id!);
await misskeyApi('admin/emoji/delete-bulk', { ids: deleteIds }); await misskeyApi('admin/emoji/delete-bulk', { ids: deleteIds });
@ -528,7 +528,7 @@ function onGridResetButtonClicked() {
async function onQueryRolesEditClicked() { async function onQueryRolesEditClicked() {
const result = await os.selectRole({ const result = await os.selectRole({
initialRoleIds: queryRoles.value.map(it => it.id), initialRoleIds: queryRoles.value.map(it => it.id),
title: '絵文字に設定されたロールで検索', title: i18n.ts._customEmojisManager._local._list.dialogSelectRoleTitle,
publicOnly: true, publicOnly: true,
}); });
if (result.canceled) { if (result.canceled) {

View file

@ -2,8 +2,8 @@
<div class="_gaps"> <div class="_gaps">
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-settings"></i></template> <template #icon><i class="ti ti-settings"></i></template>
<template #label>アップロード設定</template> <template #label>{{ i18n.ts._customEmojisManager._local._register.uploadSettingTitle }}</template>
<template #caption>この画面で絵文字アップロードを行う際の動作を設定できます</template> <template #caption>{{ i18n.ts._customEmojisManager._local._register.uploadSettingDescription }}</template>
<div class="_gaps"> <div class="_gaps">
<MkSelect v-model="selectedFolderId"> <MkSelect v-model="selectedFolderId">
@ -19,17 +19,17 @@
</MkSwitch> </MkSwitch>
<MkSwitch v-model="directoryToCategory"> <MkSwitch v-model="directoryToCategory">
<template #label>ディレクトリ名を"category"に入力する</template> <template #label>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryLabel }}</template>
<template #caption>ディレクトリをドラッグドロップした時にディレクトリ名を"category"に入力します</template> <template #caption>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryCaption }}</template>
</MkSwitch> </MkSwitch>
</div> </div>
</MkFolder> </MkFolder>
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-notes"></i></template> <template #icon><i class="ti ti-notes"></i></template>
<template #label>登録ログ</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #caption> <template #caption>
絵文字登録時のログが表示されます登録操作を行ったりページをリロードすると消えます {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
</template> </template>
<XRegisterLogs :logs="requestLogs"/> <XRegisterLogs :logs="requestLogs"/>
@ -42,12 +42,12 @@
@drop.prevent.stop="onDrop" @drop.prevent.stop="onDrop"
> >
<div style="margin-top: 1em"> <div style="margin-top: 1em">
いずれかの方法で登録する絵文字を選択してください {{ i18n.ts._customEmojisManager._local._register.emojiInputAreaCaption }}
</div> </div>
<ul> <ul>
<li>この枠に画像ファイルまたはディレクトリをドラッグドロップ</li> <li>{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList1 }}</li>
<li><a @click.prevent="onFileSelectClicked">このリンクをクリックしてPCから選択する</a></li> <li><a @click.prevent="onFileSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList2 }}</a></li>
<li><a @click.prevent="onDriveSelectClicked">このリンクをクリックしてドライブから選択する</a></li> <li><a @click.prevent="onDriveSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList3 }}</a></li>
</ul> </ul>
</div> </div>
@ -146,13 +146,13 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択行をコピー', text: i18n.ts._customEmojisManager._gridCommon.copySelectionRows,
icon: 'ti ti-copy', icon: 'ti ti-copy',
action: () => copyGridDataToClipboard(gridItems, context), action: () => copyGridDataToClipboard(gridItems, context),
}, },
{ {
type: 'button', type: 'button',
text: '選択行を削除', text: i18n.ts._customEmojisManager._gridCommon.deleteSelectionRows,
icon: 'ti ti-trash', icon: 'ti ti-trash',
action: () => removeRows(context.rangedRows), action: () => removeRows(context.rangedRows),
}, },
@ -216,13 +216,13 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択範囲をコピー', text: i18n.ts._customEmojisManager._gridCommon.copySelectionRanges,
icon: 'ti ti-copy', icon: 'ti ti-copy',
action: () => copyGridDataToClipboard(gridItems, context), action: () => copyGridDataToClipboard(gridItems, context),
}, },
{ {
type: 'button', type: 'button',
text: '選択行を削除', text: i18n.ts._customEmojisManager._gridCommon.deleteSelectionRanges,
icon: 'ti ti-trash', icon: 'ti ti-trash',
action: () => removeRows(context.rangedCells.map(it => it.row)), action: () => removeRows(context.rangedCells.map(it => it.row)),
}, },
@ -244,8 +244,8 @@ const isDragOver = ref<boolean>(false);
async function onRegistryClicked() { async function onRegistryClicked() {
const dialogSelection = await os.confirm({ const dialogSelection = await os.confirm({
type: 'info', type: 'info',
title: '確認', title: i18n.ts._customEmojisManager._local._register.confirmRegisterEmojisTitle,
text: `リストに表示されている絵文字を新たなカスタム絵文字として登録します。よろしいですか?(負荷を避けるため、一度の操作で登録可能な絵文字は${MAXIMUM_EMOJI_REGISTER_COUNT}件までです)`, text: i18n.tsx._customEmojisManager._local._register.confirmRegisterEmojisDescription({ count: MAXIMUM_EMOJI_REGISTER_COUNT }),
}); });
if (dialogSelection.canceled) { if (dialogSelection.canceled) {
@ -278,8 +278,8 @@ async function onRegistryClicked() {
if (failedItems.length > 0) { if (failedItems.length > 0) {
await os.alert({ await os.alert({
type: 'error', type: 'error',
title: 'エラー', title: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedTitle,
text: '絵文字の登録に失敗しました。詳細は登録ログをご確認ください。', text: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedDescription,
}); });
} }
@ -298,8 +298,8 @@ async function onRegistryClicked() {
async function onClearClicked() { async function onClearClicked() {
const result = await os.confirm({ const result = await os.confirm({
type: 'warning', type: 'warning',
title: '確認', title: i18n.ts._customEmojisManager._local._register.confirmClearEmojisTitle,
text: '編集内容を破棄し、リストに表示されている絵文字をクリアします。よろしいですか?', text: i18n.ts._customEmojisManager._local._register.confirmClearEmojisDescription,
}); });
if (!result.canceled) { if (!result.canceled) {
@ -313,8 +313,8 @@ async function onDrop(ev: DragEvent) {
const droppedFiles = await extractDroppedItems(ev).then(it => flattenDroppedFiles(it)); const droppedFiles = await extractDroppedItems(ev).then(it => flattenDroppedFiles(it));
const confirm = await os.confirm({ const confirm = await os.confirm({
type: 'info', type: 'info',
title: '確認', title: i18n.ts._customEmojisManager._local._register.confirmUploadEmojisTitle,
text: `ドラッグ&ドロップされた${droppedFiles.length}個のファイルをドライブにアップロードします。実行しますか?`, text: i18n.tsx._customEmojisManager._local._register.confirmUploadEmojisDescription({ count: droppedFiles.length }),
}); });
if (confirm.canceled) { if (confirm.canceled) {
return; return;

View file

@ -1,8 +1,8 @@
<template> <template>
<div class="_gaps" :class="$style.root"> <div class="_gaps" :class="$style.root">
<MkTab v-model="modeTab" style="margin-bottom: var(--margin);"> <MkTab v-model="modeTab" style="margin-bottom: var(--margin);">
<option value="list">登録済み絵文字一覧</option> <option value="list">{{ i18n.ts._customEmojisManager._local.tabTitleList }}</option>
<option value="register">新規登録</option> <option value="register">{{ i18n.ts._customEmojisManager._local.tabTitleRegister }}</option>
</MkTab> </MkTab>
<div> <div>
@ -14,6 +14,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { i18n } from '@/i18n.js';
import MkTab from '@/components/MkTab.vue'; import MkTab from '@/components/MkTab.vue';
import XListComponent from '@/pages/admin/custom-emojis-manager.local.list.vue'; import XListComponent from '@/pages/admin/custom-emojis-manager.local.list.vue';
import XRegisterComponent from '@/pages/admin/custom-emojis-manager.local.register.vue'; import XRegisterComponent from '@/pages/admin/custom-emojis-manager.local.register.vue';

View file

@ -2,7 +2,7 @@
<div> <div>
<div v-if="logs.length > 0" style="display:flex; flex-direction: column; overflow-y: scroll; gap: 16px;"> <div v-if="logs.length > 0" style="display:flex; flex-direction: column; overflow-y: scroll; gap: 16px;">
<MkSwitch v-model="showingSuccessLogs"> <MkSwitch v-model="showingSuccessLogs">
<template #label>成功ログを表示する</template> <template #label>{{ i18n.ts._customEmojisManager._logs.showSuccessLogSwitch }}</template>
</MkSwitch> </MkSwitch>
<div> <div>
<div v-if="filteredLogs.length > 0"> <div v-if="filteredLogs.length > 0">
@ -12,12 +12,12 @@
/> />
</div> </div>
<div v-else> <div v-else>
失敗ログはありません {{ i18n.ts._customEmojisManager._logs.failureLogNothing }}
</div> </div>
</div> </div>
</div> </div>
<div v-else> <div v-else>
ログはありません {{ i18n.ts._customEmojisManager._logs.logNothing }}
</div> </div>
</div> </div>
</template> </template>
@ -25,6 +25,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, toRefs } from 'vue'; import { computed, ref, toRefs } from 'vue';
import { i18n } from '@/i18n.js';
import { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js'; import { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
import MkGrid from '@/components/grid/MkGrid.vue'; import MkGrid from '@/components/grid/MkGrid.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
@ -40,7 +41,7 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択行をコピー', text: i18n.ts._customEmojisManager._gridCommon.copySelectionRows,
icon: 'ti ti-copy', icon: 'ti ti-copy',
action: () => copyGridDataToClipboard(logs, context), action: () => copyGridDataToClipboard(logs, context),
}, },
@ -58,7 +59,7 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択範囲をコピー', text: i18n.ts._customEmojisManager._gridCommon.copySelectionRanges,
icon: 'ti ti-copy', icon: 'ti ti-copy',
action: () => copyGridDataToClipboard(logs, context), action: () => copyGridDataToClipboard(logs, context),
}, },

View file

@ -3,9 +3,9 @@
<div class="_gaps"> <div class="_gaps">
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-search"></i></template> <template #icon><i class="ti ti-search"></i></template>
<template #label>検索設定</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.searchSettings }}</template>
<template #caption> <template #caption>
検索条件を詳細に設定します {{ i18n.ts._customEmojisManager._gridCommon.searchSettingCaption }}
</template> </template>
<div class="_gaps"> <div class="_gaps">
@ -54,7 +54,7 @@
<MkFolder :spacerMax="8" :spacerMin="8"> <MkFolder :spacerMax="8" :spacerMin="8">
<template #icon><i class="ti ti-arrows-sort"></i></template> <template #icon><i class="ti ti-arrows-sort"></i></template>
<template #label>ソート順</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.sortOrder }}</template>
<div :class="$style.sortOrderArea"> <div :class="$style.sortOrderArea">
<div :class="$style.sortOrderAreaTags"> <div :class="$style.sortOrderAreaTags">
<MkTagItem <MkTagItem
@ -78,7 +78,7 @@
{{ i18n.ts.search }} {{ i18n.ts.search }}
</MkButton> </MkButton>
<MkButton @click="onQueryResetButtonClicked"> <MkButton @click="onQueryResetButtonClicked">
リセット {{ i18n.ts.reset }}
</MkButton> </MkButton>
</div> </div>
</div> </div>
@ -86,9 +86,9 @@
<MkFolder> <MkFolder>
<template #icon><i class="ti ti-notes"></i></template> <template #icon><i class="ti ti-notes"></i></template>
<template #label>登録ログ</template> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #caption> <template #caption>
絵文字更新削除時のログが表示されます更新削除操作を行ったりページをリロードすると消えます {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
</template> </template>
<XRegisterLogs :logs="requestLogs"/> <XRegisterLogs :logs="requestLogs"/>
@ -101,7 +101,7 @@
<MkPagingButtons :current="currentPage" :max="allPages" :buttonCount="5" @pageChanged="onPageChanged"/> <MkPagingButtons :current="currentPage" :max="allPages" :buttonCount="5" @pageChanged="onPageChanged"/>
<div v-if="gridItems.length > 0" class="_gaps" :class="$style.buttons"> <div v-if="gridItems.length > 0" class="_gaps" :class="$style.buttons">
<MkButton primary @click="onImportClicked">チェックがついた絵文字をインポート</MkButton> <MkButton primary @click="onImportClicked">{{ i18n.ts._customEmojisManager._remote.importEmojisButton }}</MkButton>
</div> </div>
</div> </div>
</div> </div>
@ -154,7 +154,7 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択行をインポート', text: i18n.ts._customEmojisManager._remote.importSelectionRows,
icon: 'ti ti-download', icon: 'ti ti-download',
action: async () => { action: async () => {
const targets = context.rangedRows.map(it => gridItems.value[it.index]); const targets = context.rangedRows.map(it => gridItems.value[it.index]);
@ -177,7 +177,7 @@ function setupGrid(): GridSetting {
return [ return [
{ {
type: 'button', type: 'button',
text: '選択範囲の行をインポート', text: i18n.ts._customEmojisManager._remote.importSelectionRangesRows,
icon: 'ti ti-download', icon: 'ti ti-download',
action: async () => { action: async () => {
const targets = context.rangedCells.map(it => gridItems.value[it.row.index]); const targets = context.rangedCells.map(it => gridItems.value[it.row.index]);
@ -246,6 +246,11 @@ function onQueryResetButtonClicked() {
queryPublicUrl.value = null; queryPublicUrl.value = null;
} }
async function onPageChanged(pageNumber: number) {
currentPage.value = pageNumber;
await refreshCustomEmojis();
}
async function onImportClicked() { async function onImportClicked() {
const targets = gridItems.value.filter(it => it.checked); const targets = gridItems.value.filter(it => it.checked);
await importEmojis(targets); await importEmojis(targets);
@ -281,8 +286,8 @@ async function importEmojis(targets: GridItem[]) {
const confirm = await os.confirm({ const confirm = await os.confirm({
type: 'info', type: 'info',
title: '絵文字のインポート', title: i18n.ts._customEmojisManager._remote.confirmImportEmojisTitle,
text: `リモートから受信した${targets.length}個の絵文字のインポートを行います。絵文字のライセンスに十分な注意を払ってください。実行しますか?`, text: i18n.tsx._customEmojisManager._remote.confirmImportEmojisDescription({ count: targets.length }),
}); });
if (confirm.canceled) { if (confirm.canceled) {
@ -295,8 +300,8 @@ async function importEmojis(targets: GridItem[]) {
if (failedItems.length > 0) { if (failedItems.length > 0) {
await os.alert({ await os.alert({
type: 'error', type: 'error',
title: 'エラー', title: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedTitle,
text: '絵文字の更新・削除に失敗しました。詳細は登録ログをご確認ください。', text: i18n.ts._customEmojisManager._gridCommon.alertEmojisRegisterFailedDescription,
}); });
} }

View file

@ -76,7 +76,7 @@ export async function readDataTransferItems(itemList: DataTransferItemList): Pro
} }
function readDirectory(fileSystemDirectoryEntry: FileSystemDirectoryEntry): Promise<DroppedItem[]> { function readDirectory(fileSystemDirectoryEntry: FileSystemDirectoryEntry): Promise<DroppedItem[]> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve) => {
const allEntries = Array.of<FileSystemEntry>(); const allEntries = Array.of<FileSystemEntry>();
const reader = fileSystemDirectoryEntry.createReader(); const reader = fileSystemDirectoryEntry.createReader();
while (true) { while (true) {