Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

複数エンジン対応:複数の辞書に対する操作を追加 #980

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 63 additions & 13 deletions src/components/DictionaryManageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@
<div v-if="loadingDict" class="loading-dict">
<div>
<q-spinner color="primary" size="2.5rem" />
<div class="q-mt-xs">読み込み中・・・</div>
<div class="q-mt-xs">
<template v-if="loadingType === 'loading'"
>読み込み中・・・</template
>
<template v-if="loadingType === 'syncing'"
>同期中・・・</template
>
</div>
</div>
</div>
<div class="col-4 word-list-col">
Expand Down Expand Up @@ -175,6 +182,17 @@
</div>
</div>
</div>
<q-select
dense
v-model="engineToUseForPreview"
v-if="engineIds.length > 1"
label="プレビューに使うエンジン"
:options="engineIds"
:option-label="(id) => engineNames[id]"
class="q-px-md col-7"
>
</q-select>

<div class="row q-pl-md q-pt-lg text-h6">単語優先度</div>
<div class="row q-pl-md desc-row">
単語を登録しても反映されないと感じた場合、優先度の数値を上げてみてください。
Expand Down Expand Up @@ -263,8 +281,6 @@ export default defineComponent({
const store = useStore();
const $q = useQuasar();

const engineIdComputed = computed(() => store.state.engineIds[0]); // TODO: 複数エンジン対応

const dictionaryManageDialogOpenedComputed = computed({
get: () => props.modelValue,
set: (val) => emit("update:modelValue", val),
Expand All @@ -274,8 +290,27 @@ export default defineComponent({
const nowPlaying = ref(false);

const loadingDict = ref(false);
const loadingType = ref<"loading" | "syncing">("loading");
const userDict = ref<Record<string, UserDictWord>>({});

// FIXME: もっといい実装があるはず。
const engineToUseForPreviewRef = ref<string | null>(null);
const engineToUseForPreview = computed({
get: () => engineToUseForPreviewRef.value ?? store.state.engineIds[0],
set: (val) => {
engineToUseForPreviewRef.value = val;
},
});
const engineIds = computed(() => store.state.engineIds);
const engineNames = computed(() =>
Object.fromEntries(
store.state.engineIds.map((id) => [
id,
store.state.engineInfos[id].name,
])
)
);

const createUILockAction = function <T>(action: Promise<T>) {
uiLocked.value = true;
return action.finally(() => {
Expand All @@ -284,16 +319,13 @@ export default defineComponent({
};

const loadingDictProcess = async () => {
const engineId = engineIdComputed.value;
if (engineId === undefined)
throw new Error(`assert engineId !== undefined`);

if (engineIds.value.length === 0)
throw new Error(`assert engineIds.length > 0`);
loadingDict.value = true;
loadingType.value = "loading";
try {
userDict.value = await createUILockAction(
store.dispatch("LOAD_USER_DICT", {
engineId,
})
store.dispatch("LOAD_ALL_USER_DICT")
);
} catch {
$q.dialog({
Expand All @@ -308,6 +340,20 @@ export default defineComponent({
dictionaryManageDialogOpenedComputed.value = false;
});
}
loadingType.value = "syncing";
try {
await createUILockAction(store.dispatch("SYNC_ALL_USER_DICT"));
} catch {
$q.dialog({
title: "辞書の同期に失敗しました",
message: "エンジンの再起動をお試しください。",
ok: {
label: "閉じる",
flat: true,
textColor: "display",
},
});
}
loadingDict.value = false;
};
watch(dictionaryManageDialogOpenedComputed, async (newValue) => {
Expand Down Expand Up @@ -364,7 +410,7 @@ export default defineComponent({
surface.value = convertHankakuToZenkaku(text);
};
const setYomi = async (text: string, changeWord?: boolean) => {
const engineId = engineIdComputed.value;
const engineId = engineToUseForPreview.value;
if (engineId === undefined)
throw new Error(`assert engineId !== undefined`);

Expand Down Expand Up @@ -409,7 +455,7 @@ export default defineComponent({
};

const changeAccent = async (_: number, accent: number) => {
const engineId = engineIdComputed.value;
const engineId = engineToUseForPreview.value;
if (engineId === undefined)
throw new Error(`assert engineId !== undefined`);

Expand All @@ -431,7 +477,7 @@ export default defineComponent({
audioElem.pause();

const play = async () => {
const engineId = engineIdComputed.value;
const engineId = engineToUseForPreview.value;
if (engineId === undefined)
throw new Error(`assert engineId !== undefined`);

Expand Down Expand Up @@ -709,8 +755,12 @@ export default defineComponent({
uiLocked,
nowGenerating,
nowPlaying,
engineToUseForPreview,
engineIds,
engineNames,
userDict,
loadingDict,
loadingType,
wordEditing,
surfaceInput,
yomiInput,
Expand Down
128 changes: 103 additions & 25 deletions src/store/dictionary.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserDictWord } from "@/openapi";
import { UserDictWord, UserDictWordToJSON } from "@/openapi";
import { DictionaryStoreState, DictionaryStoreTypes } from "@/store/type";
import { createPartialStore } from "./vuex";

Expand Down Expand Up @@ -30,12 +30,43 @@ export const dictionaryStore = createPartialStore<DictionaryStoreTypes>({
},
},

LOAD_ALL_USER_DICT: {
async action({ dispatch, state }) {
const allDict = await Promise.all(
state.engineIds.map((engineId) => {
return dispatch("LOAD_USER_DICT", { engineId });
})
);
const mergedDictMap = new Map<string, [string, UserDictWord]>();
for (const dict of allDict) {
for (const [id, dictItem] of Object.entries(dict)) {
mergedDictMap.set(`${dictItem.yomi}-${dictItem.surface}`, [
id,
dictItem,
]);
}
}
const mergedDict = [...mergedDictMap.values()];
mergedDict.sort((a, b) => {
if (a[1].yomi > b[1].yomi) {
return 1;
} else {
return -1;
}
});
return Object.fromEntries(mergedDict);
},
},

ADD_WORD: {
async action(
{ state, dispatch },
{ surface, pronunciation, accentType, priority }
) {
const engineId: string | undefined = state.engineIds[0]; // TODO: 複数エンジン対応
// 同期処理により、一つのエンジンだけに登録しても、他のエンジンにも反映される。
// むしろ全てのエンジンに登録処理をするとUUIDが合わない。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このコメントは、ADD_WARDをしたあとにSYNCすると、という意味ですよね。
ADD_WARDはただ単語を追加する処理でSYNCとは独立しているため、SYNCを実行した場合のコメントをここに書くと、たぶんSYNCのことを知らない他の開発者の方が混乱すると思います。

このコメントを書くなら、辞書UIでADD_WARDを実行しているところのが適切かもです。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SYNCをADD_WORDにも書く、でも解決できそうな感じがしました。

const engineId: string | undefined = state.engineIds[0];

if (engineId === undefined)
throw new Error(`No such engine registered: index == 0`);
await dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
Expand All @@ -56,35 +87,82 @@ export const dictionaryStore = createPartialStore<DictionaryStoreTypes>({
{ state, dispatch },
{ wordUuid, surface, pronunciation, accentType, priority }
) {
const engineId: string | undefined = state.engineIds[0]; // TODO: 複数エンジン対応
if (engineId === undefined)
throw new Error(`No such engine registered: index == 0`);
await dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
}).then((instance) =>
instance.invoke("rewriteUserDictWordUserDictWordWordUuidPut")({
wordUuid,
surface,
pronunciation,
accentType,
priority,
})
);
if (state.engineIds.length === 0)
throw new Error(`At least one engine must be registered`);
for (const engineId of state.engineIds) {
await dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
}).then((instance) =>
instance.invoke("rewriteUserDictWordUserDictWordWordUuidPut")({
wordUuid,
surface,
pronunciation,
accentType,
priority,
})
);
}
},
},

DELETE_WORD: {
async action({ state, dispatch }, { wordUuid }) {
const engineId: string | undefined = state.engineIds[0]; // TODO: 複数エンジン対応
if (engineId === undefined)
throw new Error(`No such engine registered: index == 0`);
await dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
}).then((instance) =>
instance.invoke("deleteUserDictWordUserDictWordWordUuidDelete")({
wordUuid,
})
if (state.engineIds.length === 0)
throw new Error(`At least one engine must be registered`);
for (const engineId of state.engineIds) {
await dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
}).then((instance) =>
instance.invoke("deleteUserDictWordUserDictWordWordUuidDelete")({
wordUuid,
})
);
}
},
},

SYNC_ALL_USER_DICT: {
async action({ dispatch, state }) {
const allDict = Object.fromEntries(
await Promise.all(
state.engineIds.map(async (engineId) => {
return [engineId, await dispatch("LOAD_USER_DICT", { engineId })];
})
)
);
const mergedDict = await dispatch("LOAD_ALL_USER_DICT");
for (const engineId of state.engineIds) {
const dictIdSet = new Set(Object.keys(allDict[engineId]));
for (const [id] of Object.entries(mergedDict)) {
if (dictIdSet.has(id)) {
dictIdSet.delete(id);
}
}

await dispatch("INSTANTIATE_ENGINE_CONNECTOR", { engineId }).then(
(instance) => {
Promise.all([
instance.invoke("importUserDictWordsImportUserDictPost")({
override: true,
requestBody: Object.fromEntries(
Object.entries(mergedDict).map(([k, v]) => [
k,
UserDictWordToJSON(v),
])
),
}),
// yomi、surfaceが被っていて削除された単語をエンジンの辞書から削除する。
...[...dictIdSet].map((wordUuid) => {
return instance.invoke(
"deleteUserDictWordUserDictWordWordUuidDelete"
)({
wordUuid,
});
}),
]);
}
);
}
},
},
});
6 changes: 6 additions & 0 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,9 @@ export type DictionaryStoreTypes = {
engineId: string;
}): Promise<Record<string, UserDictWord>>;
};
LOAD_ALL_USER_DICT: {
action(): Promise<Record<string, UserDictWord>>;
};
ADD_WORD: {
action(payload: {
surface: string;
Expand All @@ -1164,6 +1167,9 @@ export type DictionaryStoreTypes = {
DELETE_WORD: {
action(payload: { wordUuid: string }): Promise<void>;
};
SYNC_ALL_USER_DICT: {
action(): Promise<void>;
};
};

/*
Expand Down
3 changes: 3 additions & 0 deletions src/views/EditorHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ export default defineComponent({
await store.dispatch("LOAD_USER_CHARACTER_ORDER");
await store.dispatch("LOAD_DEFAULT_STYLE_IDS");
// 辞書を同期
await store.dispatch("SYNC_ALL_USER_DICT");
// 新キャラが追加されている場合はキャラ並び替えダイアログを表示
const newCharacters = await store.dispatch("GET_NEW_CHARACTERS");
isCharacterOrderDialogOpenComputed.value = newCharacters.length > 0;
Expand Down