Skip to content

Commit

Permalink
Merge pull request #57 from glaciyan/builds
Browse files Browse the repository at this point in the history
Builds update
  • Loading branch information
glaciyan authored Jan 4, 2022
2 parents 5e15a5e + a22b722 commit d5bbfc2
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 131 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "gscale",
"license": "MIT",
"version": "2.1.0",
"version": "2.1.2",
"scripts": {
"pre": "yarn gen:version",
"dev": "yarn pre && vite",
Expand Down
12 changes: 5 additions & 7 deletions src/components/ItemCardClickable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,26 @@ import { ItemWithAmount } from "~/lib/types/ItemWithAmount";
import CheckIcon from "./icons/CheckIcon.vue";
import FillAmount, { FillState } from "~/lib/item/FillAmount";
const props = defineProps<{ itemWithAmount: ItemWithAmount; fillAmount: FillAmount }>();
const props = defineProps<{ item: ItemWithAmount; fillAmount: FillAmount }>();
defineEmits(["click"]);
const done = computed(() => props.fillAmount.state === FillState.Done);
const partial = computed(() => props.fillAmount.state === FillState.Partial);
const tooltip = computed(() => {
if (done.value) {
return `${props.itemWithAmount.amount.toLocaleString()} ${props.itemWithAmount.item.name}`;
return `${props.item.amount.toLocaleString()} ${props.item.item.name}`;
} else if (partial.value) {
return `${props.fillAmount.amount.toLocaleString()}/${props.itemWithAmount.amount.toLocaleString()} ${
props.itemWithAmount.item.name
}`;
return `${props.fillAmount.amount.toLocaleString()}/${props.item.amount.toLocaleString()} ${props.item.item.name}`;
} else {
return `${props.itemWithAmount.amount.toLocaleString()} ${props.itemWithAmount.item.name}`;
return `${props.item.amount.toLocaleString()} ${props.item.item.name}`;
}
});
</script>

<template>
<div v-tooltip="tooltip" class="cursor-pointer m-1 relative select-none" @click="$emit('click', fillAmount)">
<ItemCard :itemWithAmount="itemWithAmount" :grayscale="done" class="!m-0" />
<ItemCard :itemWithAmount="item" :grayscale="done" class="!m-0" />
<div
:class="[
'ring-inset hover:ring-2 transition-all absolute inset-0 rounded-md ring-green-400 flex items-center justify-center',
Expand Down
2 changes: 1 addition & 1 deletion src/components/ItemChecklist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const getState = (item: ItemWithAmount): FillAmount => {
<ItemCardClickable
v-for="item in items"
:key="item.item.normalizedName + id"
:itemWithAmount="item"
:item="item"
:fillAmount="getState(item)"
@click="$emit('itemClick', item, $event)"
/>
Expand Down
88 changes: 39 additions & 49 deletions src/components/character/CharacterBuildPreview.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import useCheckList from "~/composites/useCheckList";
import { useLoadingFunction } from "~/composites/useLoadingFunction";
import { ICharacter } from "~/lib/data/contracts/ICharacter";
import { ITraveler } from "~/lib/data/contracts/ITraveler";
import FillAmount, { FillState } from "~/lib/item/FillAmount";
Expand All @@ -18,29 +17,24 @@ import RangeLevelDisplay from "../levelRange/display/RangeLevelDisplay.vue";
import RangeTalentDisplay from "../levelRange/display/RangeTalentDisplay.vue";
import Modal from "../Modal.vue";
const props = defineProps<{ character: ICharacter | ITraveler; total: ItemWithAmount[]; data: Build }>();
const props = defineProps<{ character: ICharacter | ITraveler; items: ItemWithAmount[]; data: Build }>();
const emit = defineEmits(["deleted"]);
//#region Deleting
const deleted = ref(false);
const { loading: isDeleting, execute: deleteBuild } = useLoadingFunction(async () => {
const deleteBuild = async () => {
if (props.data.id) {
try {
await db.builds.delete(props.data.id);
emit("deleted", props.data.id);
deleted.value = true;
} catch (error) {
// TODO prompt user to refresh page and try again
console.error(error);
}
}
});
};
const deleteDialog = useConfirmDialog();
deleteDialog.onConfirm(deleteBuild);
const hidden = computed(() => deleted.value || isDeleting.value);
//#endregion
//#region Checklist
Expand Down Expand Up @@ -73,60 +67,56 @@ const edit = () => {
</script>

<template>
<template v-if="!hidden">
<ElementProvider :element="character.element">
<div class="rounded-md flex flex-col bg-dark-700 shadow-md overflow-hidden">
<div class="flex flex-grow <lg:flex-col">
<div class="lg:flex">
<div class="flex min-h-32 relative lg:w-50">
<div class="inset-x-0 top-0 absolute fading-out">
<Image
<ElementProvider :element="character.element">
<div class="rounded-md flex flex-col bg-dark-700 shadow-md overflow-hidden">
<div class="flex flex-grow <lg:flex-col">
<div class="lg:flex">
<div class="flex min-h-32 relative lg:w-50">
<div class="inset-x-0 top-0 absolute fading-out">
<Image
class="object-cover h-32 w-full opacity-50"
type="characterCard"
:entity="character"
width="240"
height="150"
/>
</div>
<div class="p-6 relative <sm:p-4">
<p class="font-bold text-lg text-light-important mb-2">
{{ character.name
}}<span
v-if="character.isTraveler"
:class="`text-genshin-element-${character.element.normalizedName}`"
>{{ " " }}{{ character.element.name }}</span
>
</p>
<div class="-m-1">
<RangeLevelDisplay class="m-1" :range="data.level" />
<div class="flex flex-wrap">
<RangeTalentDisplay class="m-1" title="Normal" :range="data.normal" :icon="Sword" />
<RangeTalentDisplay class="m-1" title="Elemental" :range="data.elemental" :icon="Elemental" />
<RangeTalentDisplay class="m-1" title="Burst" :range="data.burst" :icon="Fire" />
</div>
</div>
<div class="p-6 relative <sm:p-4">
<p class="font-bold text-lg text-light-important mb-2">
{{ character.name }}
<span v-if="character.isTraveler" :class="`text-genshin-element-${character.element.normalizedName}`">
{{ character.element.name }}
</span>
</p>
<div class="-m-1">
<RangeLevelDisplay class="m-1" :range="data.level" />
<div class="flex flex-wrap">
<RangeTalentDisplay class="m-1" title="Normal" :range="data.normal" :icon="Sword" />
<RangeTalentDisplay class="m-1" title="Elemental" :range="data.elemental" :icon="Elemental" />
<RangeTalentDisplay class="m-1" title="Burst" :range="data.burst" :icon="Fire" />
</div>
</div>
</div>
</div>
<div class="bg-dark-600 w-full relative">
<div class="flex flex-wrap h-full h-max p-4 <sm:p-3">
<div v-if="checkList.loading.value" class="flex inset-0 absolute items-center justify-center">
<GSpinner />
</div>
<ItemChecklist v-else :items="total" :checkedOff="checkList.items.value" @itemClick="handleItemClick" />
</div>
<div class="bg-dark-600 w-full relative">
<div class="flex flex-wrap h-full h-max p-4 <sm:p-3">
<div v-if="checkList.loading.value" class="flex inset-0 absolute items-center justify-center">
<GSpinner />
</div>
<ItemChecklist v-else :items="items" :checkedOff="checkList.items.value" @itemClick="handleItemClick" />
</div>
</div>
<div class="flex bg-dark-600/70 border-t-2 border-dark-300 py-3 px-6 justify-end">
<EButton look="ghost" element="neutral" class="mr-2 !h-9 !text-light-ternary" @click="deleteDialog.reveal">
Delete
</EButton>
<EButton look="outline" class="mr-2 !h-9" @click="edit">Edit</EButton>
<!-- <Button class="!h-9">Planner</Button> -->
</div>
</div>
</ElementProvider>
</template>
<div class="flex bg-dark-600/70 border-t-2 border-dark-300 py-3 px-6 justify-end">
<EButton look="ghost" element="neutral" class="mr-2 !h-9 !text-light-ternary" @click="deleteDialog.reveal">
Delete
</EButton>
<EButton look="outline" class="mr-2 !h-9" @click="edit">Edit</EButton>
<!-- <Button class="!h-9">Planner</Button> -->
</div>
</div>
</ElementProvider>
<Modal
class="max-w-96"
:isOpen="deleteDialog.isRevealed.value"
Expand Down
2 changes: 1 addition & 1 deletion src/composites/useCheckList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default (buildId: number) => {
const syncToDb = async () => await db.builds.update(buildId, { completed: toRaw(items.value) });

const newItem = async (item: ItemWithAmount) => {
items.value.push(Object.assign({}, item));
items.value.push(Object.assign({}, toRaw(item)));
await syncToDb();
};

Expand Down
2 changes: 1 addition & 1 deletion src/composites/useElementContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { ComputedRef } from "vue";
import { IElement } from "~/lib/data/contracts/IElement";

export function useElementContext() {
return inject("element") as ComputedRef<IElement>;
return inject("element", undefined) as unknown as ComputedRef<IElement>;
}
18 changes: 8 additions & 10 deletions src/lib/item/totalBuildItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,14 @@ export default function (
}
) {
const levelingItems = calculateLeveling(level.start, level.goal);
const items = computed(() =>
sortItems(
mergeAmountByName([
calculateAscension(character, level.start, level.goal),
calculateTalent(character, normal.start, normal.goal, true),
calculateTalent(character, elemental.start, elemental.goal),
calculateTalent(character, burst.start, burst.goal),
[levelingItems.mora, levelingItems.lazy],
])
)
const items = sortItems(
mergeAmountByName([
calculateAscension(character, level.start, level.goal),
calculateTalent(character, normal.start, normal.goal, true),
calculateTalent(character, elemental.start, elemental.goal),
calculateTalent(character, burst.start, burst.goal),
[levelingItems.mora, levelingItems.lazy],
])
);

return items;
Expand Down
109 changes: 48 additions & 61 deletions src/pages/Builds.vue
Original file line number Diff line number Diff line change
@@ -1,103 +1,90 @@
<script setup lang="ts">
import { db } from "~/lib/offlineDatabase/db";
import { useScrollLock } from "@vueuse/core";
import CharacterBuildPreview from "~/components/character/CharacterBuildPreview.vue";
import { Build } from "~/lib/offlineDatabase/db";
import Container from "../components/PageContainer";
import GLink from "~/components/GLink";
import Center from "../components/Center.vue";
import useRandomElement from "~/composites/useRandomElement";
import repo from "~/lib/data/repository/GenshinDataRepository";
import totalBuildItems from "~/lib/item/totalBuildItems";
import mergeAmountByName from "~/lib/item/mergeAmountByName";
import GButton from "~/components/GButton.vue";
import ItemList from "~/components/ItemList.vue";
import PopOver from "~/components/PopOver.vue";
import { useScrollLock } from "@vueuse/core";
import GButton from "~/components/GButton.vue";
import EButton from "~/components/ElementButton.vue";
const { element, pickNew: newElement } = useRandomElement();
const buildsData = ref<Build[]>();
const totalBuilds = ref(0);
const builds = computed(() =>
buildsData.value
? buildsData.value.map((build) => {
const character = repo.needCharacter(build.entityId);
const total = totalBuildItems(character, build);
import { ICharacter } from "~/lib/data/contracts/ICharacter";
import { ITraveler } from "~/lib/data/contracts/ITraveler";
import repo from "~/lib/data/repository/GenshinDataRepository";
import mergeAmountByName from "~/lib/item/mergeAmountByName";
import totalBuildItems from "~/lib/item/totalBuildItems";
import { Build, db } from "~/lib/offlineDatabase/db";
import { ItemWithAmount } from "~/lib/types/ItemWithAmount";
import Container from "../components/PageContainer";
import { RouterLink } from "vue-router";
return {
character,
total,
data: build,
};
})
: []
);
const buildsData = ref<{ character: ICharacter | ITraveler; items: ItemWithAmount[]; data: Build }[]>();
const buildsReady = computed(() => buildsData.value !== undefined);
const getBuilds = async () => {
const buildsFromDb = await db.builds.where("type").equals("character").toArray();
buildsData.value = buildsFromDb;
totalBuilds.value = buildsFromDb.length;
buildsData.value = buildsFromDb.map((build) => {
const character = repo.needCharacter(build.entityId);
const items = totalBuildItems(character, build);
return {
character,
items,
data: build,
};
});
};
const total = computed(() => {
if (buildsData.value) return mergeAmountByName(buildsData.value.map((build) => build.items));
else return [];
});
onBeforeMount(() => {
getBuilds();
newElement();
});
const hasBuilds = computed(() => totalBuilds.value > 0);
const onDelete = (id: number) => {
console.log("deleted", id);
const handleBuildDelete = () => {
if (totalBuilds.value <= 1) {
window.scrollTo({ top: 0 });
}
totalBuilds.value--;
getBuilds();
};
const total = computed(() => {
return mergeAmountByName(builds.value.map((build) => build.total.value));
});
//#region Total Popover
const totalVisible = ref(false);
const lock = useScrollLock(document.body);
const scrollLock = useScrollLock(document.body);
const showTotal = () => {
totalVisible.value = true;
lock.value = true;
scrollLock.value = true;
};
const hideTotal = () => {
totalVisible.value = false;
lock.value = false;
scrollLock.value = false;
};
//#endregion
</script>

<template>
<Container v-if="builds !== null" size="2xl">
<div class="flex space-x-2 mb-4">
<Container v-if="buildsReady" size="2xl">
<div v-if="buildsData!.length > 0" class="flex space-x-2 mb-4">
<GButton @click="showTotal">Show Total</GButton>
</div>
<div v-if="hasBuilds" w:grid="gap-5 cols-2 <sm:cols-1" class="grid">
<div w:grid="gap-5 cols-2 <sm:cols-1" class="grid">
<CharacterBuildPreview
v-for="build in builds"
v-for="build in buildsData"
:key="build.data.id"
:character="build.character"
:total="build.total.value"
:items="build.items"
:data="build.data"
@deleted="handleBuildDelete"
@deleted="onDelete"
/>
<RouterLink
v-if="buildsData!.length < 2"
to="/"
class="border-dashed rounded-xl flex flex-col h-full border-4 border-dark-600 min-h-96 py-4 px-6 transition-colors text-light-ternary/70 block items-center justify-center hover:text-light-ternary"
>
<p class="font-bold text-xl">Add a new build</p>
<p class="font-bold text-xl">+</p>
</RouterLink>
</div>
<Center v-else>
<div class="rounded-lg flex flex-col bg-dark-400 shadow-md w-max py-4 px-6 items-center justify-center">
<div class="m-2 text-6xl">?</div>
<p>You don't have any builds.</p>
<GLink to="/" isRouter><EButton :element="element" class="mt-2">Create one</EButton></GLink>
</div>
</Center>
</Container>
<teleport to="#modal">
<PopOver
Expand Down

1 comment on commit d5bbfc2

@vercel
Copy link

@vercel vercel bot commented on d5bbfc2 Jan 4, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.