diff --git a/components/Blueprints/Settings.vue b/components/Blueprints/Settings.vue
index 2c5bf20..414ba23 100644
--- a/components/Blueprints/Settings.vue
+++ b/components/Blueprints/Settings.vue
@@ -51,26 +51,32 @@
{{ blueprints.length ?? 0 }}/10 accounts
- -
+
-
const props = defineProps<{
close: boolean;
- accountIndex: number;
data: BlueprintAllShip[] | undefined;
isOwner: boolean | undefined;
}>();
@@ -154,6 +159,7 @@ function getTotalTP(ships: Record[]) {
}
function goToAccount(index: number) {
+ if (userStore.isUnsavedAccount) return;
void router.push(`/modules/blueprint-tracker?a=${index}`);
}
diff --git a/components/Blueprints/Toolbar.vue b/components/Blueprints/Toolbar.vue
index 9258f01..91a52c3 100644
--- a/components/Blueprints/Toolbar.vue
+++ b/components/Blueprints/Toolbar.vue
@@ -6,7 +6,6 @@
>
(editName = acc)"
@@ -175,7 +174,10 @@ onMounted(() => window.addEventListener("scroll", detectSticky));
onBeforeUnmount(() => window.removeEventListener("scroll", detectSticky));
function createNewAccount() {
- void router.push({ query: { ...route.query, a: props.accountIndex + 1 } });
+ if (!userStore.user) return;
+
+ userStore.createNewAccount = true;
+ void router.push({ query: { ...route.query, a: userStore.user.blueprints.length } });
}
async function saveBlueprints() {
@@ -202,6 +204,7 @@ async function saveBlueprints() {
if (fetchSuccess) {
success.value = true;
+ userStore.isUnsavedAccount = false;
setTimeout(() => {
userStore.hasUnsavedChanges = false;
if (newBlueprints && userStore.user) userStore.user.blueprints = newBlueprints;
@@ -254,6 +257,7 @@ async function deleteAccount() {
if (!fetchSuccess && error) return console.error(error);
deleteSuccess.value = true;
+ void router.replace({ query: { ...route.query, a: userStore.user.blueprints.length - 2 } });
setTimeout(() => {
deleteModal.value = undefined;
diff --git a/pages/modules/blueprint-tracker.vue b/pages/modules/blueprint-tracker.vue
index 93fd18d..23d26b0 100644
--- a/pages/modules/blueprint-tracker.vue
+++ b/pages/modules/blueprint-tracker.vue
@@ -63,7 +63,7 @@ const accountIndex = ref(0);
watch(
data,
(val) => {
- if (userStore.user?.uid === route.query.u && val) userStore.blueprintsAutosave = val;
+ if (val && userStore.user?.uid === route.query.u) userStore.blueprintsAutosave = val;
},
{ deep: true }
);
@@ -140,15 +140,20 @@ onMounted(() => {
showVariants.value = localStorage.getItem("variants") === "true";
});
-async function getBlueprints(data: AllShip[]) {
- const {
- success,
- error,
- content,
- lastSaved: bpLastSaved
- } = await $fetch("/api/getBlueprints", { method: "POST", body: { uid: route.query.u ?? userStore.user?.uid, accountIndex: accountIndex.value } });
-
- if (!success && error) console.error(error);
+async function getAccount(data: AllShip[]): Promise {
+ // prettier-ignore
+ const { success, error, content, lastSaved: bpLastSaved } =
+ await $fetch("/api/getBlueprints", { method: "POST", body: { uid: route.query.u ?? userStore.user?.uid, accountIndex: accountIndex.value } });
+
+ if (!success && error) {
+ console.error(error);
+ if (route.query.a !== "0") {
+ await router.replace({ query: { ...route.query, a: 0 } });
+ userStore.hasUnsavedChanges = false;
+ window.location.reload();
+ return;
+ }
+ }
if (success && content && bpLastSaved) {
lastSaved.value = bpLastSaved;
@@ -167,9 +172,14 @@ async function getBlueprints(data: AllShip[]) {
return result as BlueprintAllShip;
});
}
+}
+
+function createAccount(data: AllShip[]): BlueprintAllShip[] {
+ if (userStore.user && !userStore.user.blueprints.some((account) => getObjectKey(account) === "Unnamed" && getObjectValue(account).length === 0)) userStore.user.blueprints.push({ Unnamed: [] });
lastSaved.value = new Date().toISOString().slice(0, 10);
- if (userStore.user) userStore.user.blueprints.push({ Unnamed: [] });
+ userStore.createNewAccount = false;
+ userStore.isUnsavedAccount = true;
return data.map((ship) => {
const result: Record = {
...ship,
@@ -184,6 +194,22 @@ async function getBlueprints(data: AllShip[]) {
});
}
+async function getBlueprints(data: AllShip[]): Promise {
+ if (route.query.u === undefined && !userStore.user)
+ return await waitUntil(
+ () => Boolean(route.query.u ?? userStore.user),
+ async () => await getBlueprints(data),
+ new Promise((resolve) => resolve([]))
+ );
+
+ if (!userStore.createNewAccount) {
+ const account = await getAccount(data);
+ if (account) return account;
+ }
+
+ return createAccount(data);
+}
+
watch(
() => route.query,
async (val) => {
@@ -197,11 +223,17 @@ watch(
}
);
-watch(isOwner, async (val) => {
- if (!userStore.shipData) return;
+onMounted(() => {
+ const stop = watch(isOwner, async (val) => {
+ if (!userStore.shipData) return;
- if (val) return (data.value = userStore.blueprintsAutosave ?? (await getBlueprints(userStore.shipData)));
- data.value = await getBlueprints(userStore.shipData);
+ if (val) {
+ data.value = userStore.blueprintsAutosave ?? (await getBlueprints(userStore.shipData));
+ return stop();
+ }
+ data.value = await getBlueprints(userStore.shipData);
+ stop();
+ });
});
function getTotalTP(ships: BlueprintAllShip[]) {
diff --git a/stores/user.ts b/stores/user.ts
index 0aee5f0..b28b2a0 100644
--- a/stores/user.ts
+++ b/stores/user.ts
@@ -8,6 +8,8 @@ export const useUserStore = defineStore("userStore", () => {
// blueprint tracker
const blueprintsAutosave = ref();
const hasUnsavedChanges = ref(false);
+ const createNewAccount = ref(false);
+ const isUnsavedAccount = ref(false);
async function getUser(createUserIfFail = true) {
const uid = localStorage.getItem("uid");
@@ -60,5 +62,5 @@ export const useUserStore = defineStore("userStore", () => {
void fetchShipData();
}
- return { isDarkMode, alert, user, shipData, shipDifficulties, blueprintsAutosave, hasUnsavedChanges, getUser, init };
+ return { isDarkMode, alert, user, shipData, shipDifficulties, blueprintsAutosave, hasUnsavedChanges, createNewAccount, isUnsavedAccount, getUser, init };
});
diff --git a/utils/functions.ts b/utils/functions.ts
index 7206fc5..7d54c5b 100644
--- a/utils/functions.ts
+++ b/utils/functions.ts
@@ -21,6 +21,45 @@ export function getRandomItem(array: T[] | string) {
return array[getRandomInt(0, array.length - 1)];
}
+/**
+ * Waits until a condition is fulfilled before executing a callback.
+ *
+ * @param condition - Function that returns a boolean indicating whether the condition is fulfilled.
+ * @param callback - Function to execute once the condition is fulfilled.
+ * @param defaultValue - Default value to return if the condition is not fulfilled within the specified timeout. Defaults to undefined.
+ * @param timeout - Maximum time, in milliseconds, to wait for the condition to be fulfilled. Defaults to 1000ms.
+ * @param interval - Time, in milliseconds, between checks of the condition. Defaults to 100ms.
+ * @returns The result of the callback function.
+ */
+export async function waitUntil(condition: () => boolean, callback: () => T, defaultValue: T, timeout?: number, interval?: number): Promise;
+/**
+ * Waits until a condition is fulfilled before executing a callback.
+ *
+ * @param condition - Function that returns a boolean indicating whether the condition is fulfilled.
+ * @param callback - Function to execute once the condition is fulfilled.
+ * @param timeout - Maximum time, in milliseconds, to wait for the condition to be fulfilled. Defaults to 1000ms.
+ * @param interval - Time, in milliseconds, between checks of the condition. Defaults to 100ms.
+ * @returns The result of the callback function.
+ */
+export async function waitUntil(condition: () => boolean, callback: () => T, timeout?: number, interval?: number): Promise;
+export async function waitUntil(condition: () => boolean, callback: () => T, defaultValue?: T, timeout = 1000, interval = 100): Promise {
+ return new Promise((resolve) => {
+ const timeoutId = setTimeout(() => {
+ // eslint-disable-next-line no-use-before-define
+ clearInterval(intervalId);
+ resolve(defaultValue);
+ }, timeout);
+
+ const intervalId = setInterval(() => {
+ if (condition()) {
+ clearTimeout(timeoutId);
+ clearInterval(intervalId);
+ resolve(callback());
+ }
+ }, interval);
+ });
+}
+
const dateOptions: Readonly>> = {
full: { dateStyle: "long", timeZone: "UTC" },
numeric: { dateStyle: "short", timeZone: "UTC" }