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

Piniaに部分的に移行する #1443

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
466 changes: 263 additions & 203 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@
"markdown-it": "12.0.4",
"move-file": "3.0.0",
"multistream": "4.1.0",
"pinia": "2.1.6",
"quasar": "2.11.6",
"semver": "7.3.5",
"shlex": "2.1.2",
"source-map-support": "0.5.19",
"systeminformation": "5.8.0",
"tree-kill": "1.2.2",
"uuid": "9.0.0",
"vue": "3.2.45",
"vue": "3.3.4",
"vue-router": "4.0.8",
"vuedraggable": "4.1.0",
"vuex": "4.0.2",
Expand Down Expand Up @@ -114,6 +115,7 @@
"sass": "1.32.13",
"sass-loader": "8.0.2",
"tmp": "0.2.1",
"ts-essentials": "9.3.2",
"ts-node": "10.9.1",
"tsconfig-paths": "4.1.2",
"typescript": "4.8.4",
Expand Down
12 changes: 7 additions & 5 deletions src/components/DictionaryManageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ import { computed, ref, watch } from "vue";
import { QInput } from "quasar";
import AudioAccent from "./AudioAccent.vue";
import { useStore } from "@/store";
import { useDictionary } from "@/pinia-stores";
import { AccentPhrase, UserDictWord } from "@/openapi";
import {
convertHiraToKana,
Expand All @@ -272,6 +273,7 @@ const emit =
}>();

const store = useStore();
const dictionaryStore = useDictionary();

const dictionaryManageDialogOpenedComputed = computed({
get: () => props.modelValue,
Expand All @@ -298,7 +300,7 @@ const loadingDictProcess = async () => {
loadingDictState.value = "loading";
try {
userDict.value = await createUILockAction(
store.dispatch("LOAD_ALL_USER_DICT")
dictionaryStore.loadAllUserDict()
);
} catch {
const result = await store.dispatch("SHOW_ALERT_DIALOG", {
Expand All @@ -311,7 +313,7 @@ const loadingDictProcess = async () => {
}
loadingDictState.value = "synchronizing";
try {
await createUILockAction(store.dispatch("SYNC_ALL_USER_DICT"));
await createUILockAction(dictionaryStore.syncAllUserDict());
} catch {
await store.dispatch("SHOW_ALERT_DIALOG", {
title: "辞書の同期に失敗しました",
Expand Down Expand Up @@ -526,7 +528,7 @@ const saveWord = async () => {
const accent = computeRegisteredAccent();
if (selectedId.value) {
try {
await store.dispatch("REWRITE_WORD", {
await dictionaryStore.rewriteWord({
wordUuid: selectedId.value,
surface: surface.value,
pronunciation: yomi.value,
Expand All @@ -543,7 +545,7 @@ const saveWord = async () => {
} else {
try {
await createUILockAction(
store.dispatch("ADD_WORD", {
dictionaryStore.addWord({
surface: surface.value,
pronunciation: yomi.value,
accentType: accent,
Expand Down Expand Up @@ -571,7 +573,7 @@ const deleteWord = async () => {
if (result === "OK") {
try {
await createUILockAction(
store.dispatch("DELETE_WORD", {
dictionaryStore.deleteWord({
wordUuid: selectedId.value,
})
);
Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import { createGtm } from "@gtm-support/vue-gtm";
import { Quasar, Dialog, Loading, Notify } from "quasar";
import iconSet from "quasar/icon-set/material-icons";
Expand All @@ -18,6 +19,7 @@ window.dataLayer = [];

createApp(App)
.use(store, storeKey)
.use(createPinia())
.use(router)
.use(
createGtm({
Expand Down
206 changes: 206 additions & 0 deletions src/pinia-stores/dictionary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { defineStore } from "pinia";

import { EngineId } from "@/type/preload";
import { UserDictWord, UserDictWordToJSON } from "@/openapi";
import { useStore as useVuexStore } from "@/store";

export const useDictionary = defineStore("dictionary", () => {
const vuexStore = useVuexStore();

const loadUserDict = async ({ engineId }: { engineId: EngineId }) => {
const engineDict = await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
})
.then((instance) => instance.invoke("getUserDictWordsUserDictGet")({}));

// 50音順にソートするために、一旦arrayにする
const dictArray = Object.keys(engineDict).map((k) => {
return { key: k, ...engineDict[k] };
});
dictArray.sort((a, b) => {
if (a.yomi > b.yomi) {
return 1;
} else {
return -1;
}
});
const dictEntries: [string, UserDictWord][] = dictArray.map((v) => {
const { key, ...newV } = v;
return [key, newV];
});
return Object.fromEntries(dictEntries);
};

const loadAllUserDict = async () => {
const allDict = await Promise.all(
vuexStore.state.engineIds.map((engineId) => {
return loadUserDict({ 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);
};

const addWord = async ({
surface,
pronunciation,
accentType,
priority,
}: {
surface: string;
pronunciation: string;
accentType: number;
priority: number;
}) => {
// 同じ単語IDで登録するために、1つのエンジンで登録したあと全エンジンに同期する。
const engineId: EngineId | undefined = vuexStore.state.engineIds[0];
if (engineId === undefined)
throw new Error(`No such engine registered: index == 0`);
await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
})
.then((instance) =>
instance.invoke("addUserDictWordUserDictWordPost")({
surface,
pronunciation,
accentType,
priority,
})
);

await syncAllUserDict();
};

const rewriteWord = async ({
wordUuid,
surface,
pronunciation,
accentType,
priority,
}: {
wordUuid: string;
surface: string;
pronunciation: string;
accentType: number;
priority: number;
}) => {
if (vuexStore.state.engineIds.length === 0)
throw new Error(`At least one engine must be registered`);
for (const engineId of vuexStore.state.engineIds) {
await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
})
.then((instance) =>
instance.invoke("rewriteUserDictWordUserDictWordWordUuidPut")({
wordUuid,
surface,
pronunciation,
accentType,
priority,
})
);
}
};

const deleteWord = async ({ wordUuid }: { wordUuid: string }) => {
if (vuexStore.state.engineIds.length === 0)
throw new Error(`At least one engine must be registered`);
for (const engineId of vuexStore.state.engineIds) {
await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
})
.then((instance) =>
instance.invoke("deleteUserDictWordUserDictWordWordUuidDelete")({
wordUuid,
})
);
}
};

const syncAllUserDict = async () => {
const mergedDict = await loadAllUserDict();
for (const engineId of vuexStore.state.engineIds) {
// エンジンの辞書のIDリストを取得する。
const dictIdSet = await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
})
.then(
async (instance) =>
new Set(
Object.keys(
await instance.invoke("getUserDictWordsUserDictGet")({})
)
)
);
if (Object.keys(mergedDict).some((id) => !dictIdSet.has(id))) {
await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", {
engineId,
})
.then((instance) =>
// マージした辞書をエンジンにインポートする。
instance.invoke("importUserDictWordsImportUserDictPost")({
override: true,
requestBody: Object.fromEntries(
Object.entries(mergedDict).map(([k, v]) => [
k,
UserDictWordToJSON(v),
])
),
})
);
}
const removedDictIdSet = new Set(dictIdSet);
// マージされた辞書にあるIDを削除する。
// これにより、マージ処理で削除された項目のIDが残る。
for (const id of Object.keys(mergedDict)) {
if (removedDictIdSet.has(id)) {
removedDictIdSet.delete(id);
}
}

await vuexStore
.dispatch("INSTANTIATE_ENGINE_CONNECTOR", { engineId })
.then((instance) => {
// マージ処理で削除された項目をエンジンから削除する。
Promise.all(
[...removedDictIdSet].map((id) =>
instance.invoke("deleteUserDictWordUserDictWordWordUuidDelete")({
wordUuid: id,
})
)
);
});
}
};

return {
loadUserDict,
loadAllUserDict,
addWord,
rewriteWord,
deleteWord,
syncAllUserDict,
};
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
});
1 change: 1 addition & 0 deletions src/pinia-stores/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useDictionary } from "./dictionary";
15 changes: 15 additions & 0 deletions src/pinia-stores/storeHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { StoreDefinition } from "pinia";
import type { DeepReadonly } from "ts-essentials";

type ToReadonlyStoreDefinition<SD> = SD extends StoreDefinition<
infer Id,
infer S,
infer G,
infer A
>
? StoreDefinition<Id, DeepReadonly<S>, G, A>
: SD;

export function toReadonlyStoreDefinition<SD>(useStore: SD) {
return useStore as ToReadonlyStoreDefinition<SD>;
}
Loading