Add version system, add groundwork for rivals, add new update modal, clean up game constants

This commit is contained in:
Trenton Zimmer 2025-04-04 16:58:52 -04:00
parent d3af72808b
commit b92c78f48b
24 changed files with 1365 additions and 2601 deletions

View File

@ -1,3 +1,4 @@
VITE_APP_VERSION="3.0.1"
VITE_API_URL="http://10.5.7.5:8000/"
VITE_API_KEY="your_api_key_should_be_here"
VITE_ASSET_PATH="/assets"

View File

@ -1,3 +1,4 @@
VITE_APP_VERSION="3.0.1"
VITE_API_URL="https://restfulsleep.phaseii.network"
VITE_API_KEY="your_api_key_should_be_here"
VITE_ASSET_PATH="https://cdn.phaseii.network/file/PhaseII/web-assets"

View File

@ -51,11 +51,11 @@
<noscript>
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- Snow -->
<div id="particles-js"></div>
<script src="/assets/particles.js"></script>
<!-- Particles -->
<!-- <div id="particles-js"></div>
<script src="/assets/particles.js"></script> -->
<!-- <script src="/assets/network.js"></script> -->
<script src="/assets/network.js"></script>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>

3395
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
{
"3.0.1": ["- (Feature) Added update popup", "- (Optimization) Disabled ParticlesJS for the time being. This will be returned as an option in the future.", "- (Optimization) Fill in more score table data for GFDM.", "- (soon™) I've started rival support, but it's not ready just yet. Stay tuned!"]
}

View File

@ -55,6 +55,10 @@ const props = defineProps({
type: Boolean,
default: true,
},
tooltip: {
type: String,
default: null,
},
});
const is = computed(() => {
@ -128,6 +132,12 @@ const componentClass = computed(() => {
:target="target"
:disabled="disabled"
>
<div
v-if="tooltip"
class="absolute bottom-full left-1/2 mb-1 -translate-x-1/2 whitespace-nowrap rounded bg-black px-2 py-1 text-xs text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10"
>
{{ tooltip }}
</div>
<BaseIcon v-if="icon" :path="icon" :size="iconSize" />
<span v-if="label" :class="labelClass">{{ label }}</span>
</component>

View File

@ -5,10 +5,11 @@ import {
mdiAccountDetails,
mdiPlaylistMusicOutline,
mdiFormatListText,
mdiAxeBattle,
} from "@mdi/js";
import { ref, onMounted } from "vue";
import { dashCode } from "@/constants/userData";
import { GameConstants, getGameInfo } from "@/constants";
import { GameConstants, getGameInfo, getRivalInfo } from "@/constants";
import BaseButton from "@/components/BaseButton.vue";
import UserEmblem from "@/components/UserEmblem.vue";
import UserQpro from "@/components/UserQpro.vue";
@ -131,6 +132,14 @@ onMounted(async () => {
color="info"
label="Edit Profile"
/>
<BaseButton
v-if="!useSmall && game && getRivalInfo(game, version)"
:icon="mdiAxeBattle"
:href="`/#/games/${game}/rivals`"
:outline="false"
color="info"
label="Rivals"
/>
<BaseButton
v-if="!useSmall && !thisGame.noScores"
:href="`/#/games/${game}/scores/${profile.userId}`"

View File

@ -2,7 +2,7 @@
import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import GameIcon from "@/components/GameIcon.vue";
import BaseButton from "./BaseButton.vue";
import BaseButton from "@/components/BaseButton.vue";
import { getGameInfo } from "@/constants";
defineProps({

View File

@ -3,11 +3,11 @@ import { computed, ref } from "vue";
import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "./BaseIcon.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import { mdiEmailAlertOutline } from "@mdi/js";
import { useMainStore } from "@/stores/main.js";
import { loadCookie, saveCookie, deleteCookie } from "@/stores/cookies";
import { loadCookie, saveCookie } from "@/stores/cookies";
const mainStore = useMainStore();

View File

@ -2,8 +2,8 @@
import { useRouter } from "vue-router";
import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseIcon from "./BaseIcon.vue";
import BaseButton from "./BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseButton from "@/components/BaseButton.vue";
import { mdiCheckOutline, mdiCloseOutline } from "@mdi/js";
const $router = useRouter();

View File

@ -1,9 +1,6 @@
<script setup>
import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseIcon from "./BaseIcon.vue";
import BaseButton from "./BaseButton.vue";
import { mdiCheckOutline, mdiCloseOutline } from "@mdi/js";
defineProps({
active: {

View File

@ -4,7 +4,7 @@ import OverlayLayer from "@/components/OverlayLayer.vue";
import GameIcon from "@/components/GameIcon.vue";
import FormField from "@/components/FormField.vue";
import FormCheckRadio from "@/components/FormCheckRadio.vue";
import BaseButton from "./BaseButton.vue";
import BaseButton from "@/components/BaseButton.vue";
import { getGameInfo } from "@/constants";
import { APIGetProfile } from "@/stores/api/profile";
import { onMounted, ref } from "vue";

View File

@ -0,0 +1,117 @@
<script setup>
import axios from "axios";
import { ref, onMounted } from "vue";
import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import { mdiSourceBranchRefresh } from "@mdi/js";
import { APIUserAppUpdate } from "@/stores/api/account";
import { useMainStore } from "@/stores/main.js";
const changelog = ref(null);
const appVersion = import.meta.env.VITE_APP_VERSION;
const activeState = ref(true);
async function loadChangelogs() {
try {
const response = await axios.get(`/data-sources/changelog.json`);
if (response.data) {
changelog.value = response.data;
}
} catch (error) {
console.error("Error loading changelog:", error.message);
}
}
onMounted(async () => {
await loadChangelogs();
});
function isActive() {
const mainStore = useMainStore();
if (mainStore.userData.webVersions) {
if (mainStore.userData.webVersions.includes(appVersion)) {
activeState.value = false;
}
}
if (mainStore.userData.disableUpdateModal) {
activeState.value = false;
}
return activeState.value;
}
async function updateUserData(disable = false) {
var data = null;
try {
data = await APIUserAppUpdate(appVersion, disable);
} catch (error) {
console.error("Failed to update user:", error);
}
if (data?.status === "success") {
activeState.value = false;
}
}
</script>
<template>
<template v-if="isActive()">
<OverlayLayer v-if="activeState" :transparent="true">
<CardBox
class="shadow-lg max-h-modal w-11/12 md:w-3/5 lg:w-2/5 xl:w-4/12 z-50 text-white/90"
>
<div class="grid text-center justify-center grid-cols-1 gap-3">
<div class="place-self-center">
<BaseIcon
:path="mdiSourceBranchRefresh"
class="text-emerald-600"
w="w-20"
:size="45"
/>
<h1 class="text-2xl font-bold">Since you've been gone...</h1>
<h1 class="text-lg md:text-xl">
We've updated the site! These updates include feature updates and
bugfixes.
</h1>
</div>
<hr class="border-t border-1 mt-3 w-full" />
<div
v-if="changelog"
class="text-lg md:text-xl space-y-2 text-left text-pink-500 bg-slate-950 p-2"
>
<h1>Changelog:</h1>
<h2
v-for="change of changelog[appVersion] ?? ['No changes!']"
:key="change"
class="font-mono"
>
{{ change }}
</h2>
</div>
<h2 class="font-mono text-pink-500">Version: {{ appVersion }}</h2>
<div class="flex gap-2">
<BaseButton
label="Close"
color="success"
@click="updateUserData(false)"
/>
<BaseButton
label="Never Show Again"
color="danger"
@click="updateUserData(true)"
/>
</div>
</div>
</CardBox>
</OverlayLayer>
</template>
</template>

View File

@ -304,38 +304,47 @@ const GITADORA_VERSION_DATA = [
{
id: VersionConstants.GITADORA,
label: "Original",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_OVERDRIVE,
label: "OverDrive",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_TRIBOOST,
label: "Tri-Boost",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_TRIBOOST_RE_EVOLVE,
label: "Tri-Boost RE:Evolve",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_MATIXX,
label: "Matixx",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_EXCHAIN,
label: "EXCHAIN",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_NEXTAGE,
label: "Nex+Age",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_HIGH_VOLTAGE,
label: "HIGH-VOLTAGE",
maxRivals: 3,
},
{
id: VersionConstants.GITADORA_FUZZUP,
label: "FUZZUP",
maxRivals: 3,
},
];
@ -428,10 +437,7 @@ export const gameData = [
icon: null,
cardBG: null,
noRivals: true,
scoreHeaders: [
{ text: "Combos", value: "data.combo" },
{ text: "Halo", value: "halo" },
],
scoreHeaders: [{ text: "Combos", value: "data.combo" }],
chartTable: {
0: "1A",
1: "2A",
@ -566,34 +572,42 @@ export const gameData = [
{
id: VersionConstants.DDR_X2,
label: "X2",
maxRivals: 1,
},
{
id: VersionConstants.DDR_X3_VS_2ND_MIX,
label: "X3 vs. 2ND MIX",
maxRivals: 3,
},
{
id: VersionConstants.DDR_2013,
label: "(2013)",
maxRivals: 3,
},
{
id: VersionConstants.DDR_2014,
label: "(2014)",
maxRivals: 3,
},
{
id: VersionConstants.DDR_ACE,
label: "Ace",
maxRivals: 3,
},
{
id: VersionConstants.DDR_A20,
label: "A20",
maxRivals: 3,
},
{
id: VersionConstants.DDR_A20_PLUS,
label: "A20 PLUS",
maxRivals: 3,
},
{
id: VersionConstants.DDR_A3,
label: "A3",
maxRivals: 3,
},
],
},
@ -656,6 +670,7 @@ export const gameData = [
icon: `${ASSET_PATH}/icon/ddr.webp`,
cardBG: `${ASSET_PATH}/card/ddr.webp`,
assetId: "ddr",
gameOptions: DDROptions,
videoTable: [VersionConstants.DDR_A20_PLUS],
scoreHeaders: [
{ text: "Combos", value: "data.combo" },
@ -774,6 +789,7 @@ export const gameData = [
{
id: VersionConstants.DDR_A20_PLUS,
label: "OmniMIX (A20 PLUS)",
maxRivals: 3,
},
],
},
@ -799,7 +815,7 @@ export const gameData = [
],
scoreHeaders: [
{ text: "Combos", value: "data.combo" },
{ text: "Medal", value: "medal", width: 140 },
{ text: "Medal", value: "medal", width: 100 },
{ text: "SKILL %", value: "data.skill_perc", width: 90 },
{ text: "SKILL POINT", value: "data.skill_points", width: 140 },
],
@ -875,22 +891,27 @@ export const gameData = [
{
id: VersionConstants.DRUMMANIA_V4,
label: "V4: ROCKxROCK",
maxRivals: 3,
},
{
id: VersionConstants.DRUMMANIA_V5,
label: "V5: ROCK TO INFINITY!",
maxRivals: 3,
},
{
id: VersionConstants.DRUMMANIA_V6,
label: "V6: BLAZING!!!!",
maxRivals: 3,
},
{
id: VersionConstants.DRUMMANIA_V7,
label: "V7",
maxRivals: 3,
},
{
id: VersionConstants.DRUMMANIA_V8,
label: "V8",
maxRivals: 3,
},
],
},
@ -1040,7 +1061,7 @@ export const gameData = [
gameOptions: GFDMOptions,
scoreHeaders: [
{ text: "Combos", value: "data.combo" },
{ text: "Medal", value: "medal", width: 140 },
{ text: "Medal", value: "medal", width: 100 },
{ text: "SKILL %", value: "data.skill_perc", width: 90 },
{ text: "SKILL POINT", value: "data.skill_points", width: 140 },
],
@ -1142,22 +1163,27 @@ export const gameData = [
{
id: VersionConstants.GUITARFREAKS_V4,
label: "V4: ROCKxROCK",
maxRivals: 3,
},
{
id: VersionConstants.GUITARFREAKS_V5,
label: "V5: ROCK TO INFINITY!",
maxRivals: 3,
},
{
id: VersionConstants.GUITARFREAKS_V6,
label: "V6: BLAZING!!!!",
maxRivals: 3,
},
{
id: VersionConstants.GUITARFREAKS_V7,
label: "V7",
maxRivals: 3,
},
{
id: VersionConstants.GUITARFREAKS_V8,
label: "V8",
maxRivals: 3,
},
],
},
@ -1169,6 +1195,7 @@ export const gameData = [
cardBG: `${ASSET_PATH}/card/iidx.webp`,
assetId: "iidx",
gameOptions: IIDXOptions,
splitRivals: true,
videoTable: [
VersionConstants.IIDX_TRICORO,
VersionConstants.IIDX_SPADA,
@ -1315,50 +1342,62 @@ export const gameData = [
{
id: VersionConstants.IIDX_TRICORO,
label: "tricoro",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_SPADA,
label: "SPADA",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_PENDUAL,
label: "PENDUAL",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_COPULA,
label: "COPULA",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_SINOBUZ,
label: "SINOBUZ",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_CANNON_BALLERS,
label: "CANNON BALLERS",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_ROOTAGE,
label: "ROOTAGE",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_HEROIC_VERSE,
label: "HEROIC VERSE",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_BISTROVER,
label: "BISTROVER",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_CASTHOUR,
label: "CastHour",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_RESIDENT,
label: "RESIDENT",
maxRivals: 5,
},
{
id: VersionConstants.IIDX_EPOLIS,
label: "EPOLIS",
maxRivals: 5,
},
],
},
@ -1537,30 +1576,37 @@ export const gameData = [
{
id: VersionConstants.JUBEAT_SAUCER,
label: "Saucer",
maxRivals: 3,
},
{
id: VersionConstants.JUBEAT_SAUCER_FULFILL,
label: "Saucer Fulfill",
maxRivals: 3,
},
{
id: VersionConstants.JUBEAT_PROP,
label: "Prop",
maxRivals: 3,
},
{
id: VersionConstants.JUBEAT_QUBELL,
label: "Qubell",
maxRivals: 3,
},
{
id: VersionConstants.JUBEAT_CLAN,
label: "Clan",
maxRivals: 3,
},
{
id: VersionConstants.JUBEAT_FESTO,
label: "Festo",
maxRivals: 3,
},
{
id: VersionConstants.JUBEAT_AVE,
label: "Avenue",
maxRivals: 3,
},
],
},
@ -1778,38 +1824,47 @@ export const gameData = [
{
id: VersionConstants.POPN_MUSIC_TUNE_STREET,
label: "TUNE STREET",
maxRivals: 2,
},
{
id: VersionConstants.POPN_MUSIC_FANTASIA,
label: "fantasia",
maxRivals: 2,
},
{
id: VersionConstants.POPN_MUSIC_SUNNY_PARK,
label: "Sunny Park",
maxRivals: 2,
},
{
id: VersionConstants.POPN_MUSIC_LAPISTORIA,
label: "ラピストリア (Lapistoria)",
maxRivals: 4,
},
{
id: VersionConstants.POPN_MUSIC_ECLALE,
label: "éclale",
maxRivals: 4,
},
{
id: VersionConstants.POPN_MUSIC_USANEKO,
label: "うさぎと猫と少年の夢 (Usaneko)",
maxRivals: 4,
},
{
id: VersionConstants.POPN_MUSIC_PEACE,
label: "peace",
maxRivals: 4,
},
{
id: VersionConstants.POPN_MUSIC_KAIMEI_RIDDLES,
label: "解明リドルズ (Kaimei riddles)",
maxRivals: 4,
},
{
id: VersionConstants.POPN_MUSIC_UNILAB,
label: "UniLab",
maxRivals: 4,
},
],
},
@ -1843,22 +1898,27 @@ export const gameData = [
{
id: VersionConstants.REFLEC_BEAT_LIMELIGHT,
label: "Limelight",
maxRivals: 3,
},
{
id: VersionConstants.REFLEC_BEAT_COLETTE,
label: "Colette",
maxRivals: 3,
},
{
id: VersionConstants.REFLEC_BEAT_GROOVIN,
label: "groovin'!!",
maxRivals: 3,
},
{
id: VersionConstants.REFLEC_BEAT_VOLZZA,
label: "VOLZZA",
maxRivals: 3,
},
{
id: VersionConstants.REFLEC_BEAT_VOLZZA_2,
label: "VOLZZA 2",
maxRivals: 3,
},
],
},
@ -1893,26 +1953,32 @@ export const gameData = [
{
id: VersionConstants.SDVX_BOOTH,
label: "BOOTH",
maxRivals: 3,
},
{
id: VersionConstants.SDVX_INFINITE_INFECTION,
label: "-II- Infinite Infection",
maxRivals: 3,
},
{
id: VersionConstants.SDVX_GRAVITY_WARS,
label: "GRAVITY WARS",
maxRivals: 3,
},
{
id: VersionConstants.SDVX_HEAVENLY_HAVEN,
label: "HEAVENLY HAVEN",
maxRivals: 3,
},
{
id: VersionConstants.SDVX_VIVID_WAVE,
label: "VIVID WAVE",
maxRivals: 3,
},
{
id: VersionConstants.SDVX_EXCEED_GEAR,
label: "EXCEED GEAR",
maxRivals: 3,
},
],
},
@ -1983,7 +2049,7 @@ export function getGameTitle(game, version) {
var output = gameObject.name;
const versions = gameObject.versions;
if (versions !== null) {
if (versions !== undefined) {
const thisVersion = versions.find((x) => x.id == version);
if (thisVersion !== undefined) {
output += ` ${thisVersion.name}`;
@ -1992,3 +2058,18 @@ export function getGameTitle(game, version) {
return output;
}
export function getRivalInfo(game, version) {
const gameObject = gameData.find((x) => x.id == game);
var output = null;
const versions = gameObject.versions;
if (versions !== undefined) {
const thisVersion = versions.find((x) => x.id == version);
if (thisVersion !== undefined) {
output = thisVersion.maxRivals;
}
}
return output;
}

View File

@ -15,8 +15,9 @@ import { useRouter, useRoute } from "vue-router";
import menuNavBar from "@/menuNavBar.js";
import { useMainStore } from "@/stores/main.js";
import { useStyleStore } from "@/stores/style.js";
import LoadingModal from "@/components/LoadingModal.vue";
// import EmailModal from "@/components/EmailModal.vue";
import LoadingModal from "@/components/Modal/LoadingModal.vue";
// import EmailModal from "@/components/Modal/EmailModal.vue";
import UpdateModal from "@/components/Modal/UpdateModal.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import NavBar from "@/components/NavBar.vue";
import NavBarItemPlain from "@/components/NavBarItemPlain.vue";
@ -238,6 +239,10 @@ const menuAside = computed(() => {
'overflow-hidden lg:overflow-visible': isAsideMobileExpanded,
}"
>
<template v-if="userLoaded">
<UpdateModal class="transition-opacity duration-300 ease-out" />
<!-- <EmailModal class="transition-opacity duration-300 ease-out" /> -->
</template>
<LoadingModal
:active="loading || saving"
:is-save="saving"
@ -248,7 +253,6 @@ const menuAside = computed(() => {
'opacity-0': !loading && !saving,
}"
/>
<!-- <EmailModal class="transition-opacity duration-300 ease-out" /> -->
<div
v-if="userLoaded"
:class="[layoutAsidePadding, { 'ml-60 lg:ml-0': isAsideMobileExpanded }]"

View File

@ -2,7 +2,7 @@
import { useMainStore } from "@/stores/main.js";
import { useStyleStore } from "@/stores/style.js";
import { ref, watch } from "vue";
import LoadingModal from "@/components/LoadingModal.vue";
import LoadingModal from "@/components/Modal/LoadingModal.vue";
const mainStore = useMainStore();
const loading = ref(mainStore.isLoading);

View File

@ -1,7 +1,8 @@
import { useMainStore } from "@/stores/main";
const mainStore = useMainStore();
export async function APIEmailAuth(email) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/auth/emailAuth`, "POST", {
email: email,
@ -14,6 +15,8 @@ export async function APIEmailAuth(email) {
}
export async function APICheckKey(key) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/auth/check2FAKey`, "POST", {
key: key,
@ -26,6 +29,8 @@ export async function APICheckKey(key) {
}
export async function APIResetPassword(key, newPassword, confirmPassword) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/auth/changePassword`, "POST", {
key: key,
@ -44,6 +49,8 @@ export async function APIUpdatePassword(
newPassword,
confirmPassword
) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/updatePassword`, "POST", {
currentPassword: currentPassword,
@ -58,6 +65,8 @@ export async function APIUpdatePassword(
}
export async function APIRegisterUser(newProfile) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user`, "PUT", newProfile);
return data;
@ -68,6 +77,8 @@ export async function APIRegisterUser(newProfile) {
}
export async function APIGetCards() {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/card`);
return data.cards;
@ -78,6 +89,8 @@ export async function APIGetCards() {
}
export async function APIPutCard(cardId) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/card`, "POST", {
cardId: cardId,
@ -90,6 +103,8 @@ export async function APIPutCard(cardId) {
}
export async function APIDeleteCard(cardId) {
const mainStore = useMainStore();
try {
const confirmed = window.confirm("Are you really?");
if (confirmed) {
@ -105,6 +120,8 @@ export async function APIDeleteCard(cardId) {
}
export async function APIStartTakeover(cardId, pin) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(
`/user/takeover?cardId=${cardId}&pin=${pin}`
@ -120,6 +137,8 @@ export async function APIStartTakeover(cardId, pin) {
}
export async function APISaveTakeover(cardId, pin, mergeSettings) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/takeover`, "POST", {
cardId: cardId,
@ -134,6 +153,8 @@ export async function APISaveTakeover(cardId, pin, mergeSettings) {
}
export async function APIGetPlayVideos() {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/playVideos`);
return data.data;
@ -144,6 +165,8 @@ export async function APIGetPlayVideos() {
}
export async function APIGetUserContent(type) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/content?type=${type}`);
return data.data;
@ -154,6 +177,8 @@ export async function APIGetUserContent(type) {
}
export async function APIRemoveIntegration(service) {
const mainStore = useMainStore();
try {
const confirmed = window.confirm("Are you really?");
if (confirmed) {
@ -170,6 +195,8 @@ export async function APIRemoveIntegration(service) {
}
export async function APIIntegrateWith(service, code) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/integrate/${service}`, "POST", {
code: code,
@ -182,6 +209,8 @@ export async function APIIntegrateWith(service, code) {
}
export async function APIUserCustomize(customize) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/customize`, "POST", {
customize: customize,
@ -192,3 +221,18 @@ export async function APIUserCustomize(customize) {
throw error;
}
}
export async function APIUserAppUpdate(version = null, disable = false) {
const mainStore = useMainStore();
try {
const data = await mainStore.callApi(`/user/appVersion`, "POST", {
version: version,
disable: disable,
});
return data;
} catch (error) {
console.log(`Error saving user!`, error);
throw error;
}
}

View File

@ -1,9 +1,11 @@
import { useMainStore } from "@/stores/main";
const mainStore = useMainStore();
export async function APIGetAllProfiles(game) {
export async function APIGetAllProfiles(game, version = null) {
try {
const data = await mainStore.callApi(`/game/${game}/profiles`);
const data = await mainStore.callApi(
`/game/${game}/profiles?version=${version}`
);
return data.data;
} catch (error) {
console.log("Error fetching profiles:", error);

View File

@ -4,6 +4,9 @@ import { loadUserAuthKey, saveUserAuthKey } from "@/stores/auth";
export const useMainStore = defineStore("main", {
state: () => ({
/* App Information */
appVersion: import.meta.env.VITE_APP_VERSION,
/* User */
userId: null,
userName: null,

View File

@ -98,7 +98,6 @@ headers.push(
{ text: "Last Play", value: "stats.last_play_timestamp", width: 150 },
{ text: "Last Arcade", value: "stats.last_play_arcade", width: 150 },
{ text: "Plays", value: "stats.total_plays", sortable: true, width: 50 }
// { text: "Last Arcade", value: "last.arcade", sortable: true, width: 150 }
);
if (thisGame.playerHeaders) {

View File

@ -155,6 +155,16 @@ function formatScores(scores) {
item.data.music_rate = item.data?.music_rate / 10;
}
if (item.data?.excellent) {
item.medal = "EX FC";
} else if (item.data?.fullcombo) {
item.medal = "FC";
} else if (item.data?.clear) {
item.medal = "CLEARED";
} else {
item.medal = "FAILED";
}
formattedItems.push(item);
}
return formattedItems;

View File

@ -1,5 +1,5 @@
<script setup>
import { reactive } from "vue";
import { reactive, watch, ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiSwordCross, mdiPlus } from "@mdi/js";
import SectionMain from "@/components/SectionMain.vue";
@ -8,61 +8,83 @@ import BaseButton from "@/components/BaseButton.vue";
import ProfileCard from "@/components/Cards/ProfileCard.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
import GameTitleLine from "@/components/GameTitleLine.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import { APIGetProfile, APIGetAllProfiles } from "@/stores/api/profile";
import { getGameInfo } from "@/constants";
const ASSET_PATH = import.meta.env.VITE_ASSET_PATH;
import { getVideoSource, getCardStyle } from "@/constants/sources";
import { dashCode } from "@/constants/userData";
const $route = useRoute();
const $router = useRouter();
var gameID = null;
var thisGame = null;
const profile = {
id: 1,
name: "Trmazi",
extid: 12345678,
avatar:
"https://static.wikia.nocookie.net/dancedancerevolutionddr/images/3/3b/Yuni_img1.gif/",
last: {
arcade: "GhettoCade",
date: "7/16/2023",
},
};
const rivals = [
{
id: 1,
name: "TRMAZI",
rivalID: "1234-5678",
},
{
id: 2,
name: "BLADE",
rivalID: "1454-5687",
},
{
id: 3,
name: "HO!",
rivalID: "5364-5568",
},
];
gameID = $route.params.game;
thisGame = getGameInfo(gameID);
const profile = ref(null);
const allProfiles = ref([]);
const versionForm = reactive({
currentVersion: null,
});
const filterForm = reactive({
filter: null,
});
watch(
() => versionForm.currentVersion,
() => {
loadProfile();
loadAllProfiles();
}
);
gameID = $route.params.game;
thisGame = getGameInfo(gameID);
onMounted(async () => {
loadAllProfiles();
loadProfile();
});
if (!thisGame.versions) {
versionForm.currentVersion = 1;
}
async function loadProfile() {
try {
profile.value = null;
const data = await APIGetProfile(gameID, versionForm.currentVersion);
profile.value = data;
if (data && !versionForm.currentVersion) {
versionForm.currentVersion = data.versions[data.versions.length - 1];
}
} catch (error) {
console.error("Failed to fetch user profile data:", error);
}
}
async function loadAllProfiles() {
try {
const data = await APIGetAllProfiles(gameID, versionForm.currentVersion);
allProfiles.value = data;
} catch (error) {
console.error("Failed to fetch profile data:", error);
}
}
function filterVersions(haveVersions) {
var filtered = [];
for (const version of thisGame.versions) {
if (haveVersions.includes(version.id)) {
filtered.push(version);
}
}
return filtered;
}
const filterForm = reactive({
filter: null,
});
if (!thisGame) {
$router.push({
name: "ErrorPage",
@ -72,60 +94,67 @@ if (!thisGame) {
});
}
function filterUsers() {
function filterProfiles() {
if (filterForm.filter) {
return rivals.filter(
return allProfiles.value.filter(
(rival) =>
rival.name.toLowerCase().includes(filterForm.filter.toLowerCase()) ||
rival.rivalID.toLowerCase().includes(filterForm.filter.toLowerCase())
rival.username
.toLowerCase()
.includes(filterForm.filter.toLowerCase()) ||
rival.extid
.toString()
.toLowerCase()
.includes(filterForm.filter.toLowerCase())
);
}
}
function getSources() {
var sources = null;
if (!versionForm.currentVersion) {
sources = thisGame.cardBG;
} else {
sources = `${ASSET_PATH}/games/${thisGame.id}/card/${versionForm.currentVersion}.webp`;
}
return sources;
}
function getCardStyle() {
return `
background-image: url('${getSources()}');
background-size: cover;
background-repeat: no-repeat;
`;
}
</script>
<template>
<LayoutAuthenticated>
<SectionMain>
<div class="md:flex md:justify-end pb-6 items-center">
<div
v-if="thisGame.versions"
class="mt-2 md:mt-0 md:w-1/3 md:text-right"
>
<h2 class="text-md sm:text-lg md:text-xl font-bold p-2">
Select Version
</h2>
<FormControl
v-model="versionForm.currentVersion"
:options="thisGame.versions"
/>
</div>
</div>
<div
v-if="versionForm.currentVersion"
:style="getCardStyle()"
class="rounded-2xl mb-6"
:style="getCardStyle(thisGame, versionForm.currentVersion)"
class="rounded-2xl mb-6 card-container"
>
<div class="bg-white dark:bg-slate-900/90 rounded-2xl pt-6 p-3">
<video
autoplay
muted
loop
playsinline
:src="getVideoSource(thisGame, versionForm.currentVersion)"
class="background-video"
></video>
<div
class="bg-white dark:bg-slate-900/90 rounded-2xl pt-6 p-3 card-content"
>
<div class="w-full">
<ProfileCard use-small :profile="profile" />
<div
class="md:flex md:px-5 md:space-x-10 md:justify-between md:items-center"
>
<GameTitleLine :path="thisGame.icon" :title="thisGame.name" />
<div
v-if="thisGame.versions && profile"
class="md:w-1/3 md:text-right"
>
<h2 class="text-md sm:text-lg md:text-xl font-bold p-2">
Select Version
</h2>
<FormControl
v-model="versionForm.currentVersion"
:options="filterVersions(profile.versions)"
/>
</div>
</div>
</div>
<div v-if="profile" class="w-full">
<ProfileCard
:game="gameID"
:version="versionForm.currentVersion"
:profile="profile"
use-small
>
</ProfileCard>
</div>
</div>
</div>
@ -144,35 +173,39 @@ function getCardStyle() {
/>
</FormField>
<div class="grid gap-3">
<CardBox v-for="rival of filterUsers()" :key="rival.id">
<div class="flex justify-between items-center">
<div class="flex">
<img
class="w-10 mr-3"
src="https://static.wikia.nocookie.net/dancedancerevolutionddr/images/3/3b/Yuni_img1.gif/"
/>
<div class="grid">
<h1 class="text-lg">{{ rival.name }}</h1>
<h2 class="text-md font-mono">{{ rival.rivalID }}</h2>
</div>
<div class="grid gap-4">
<div
v-for="player of filterProfiles()"
:key="player.id"
class="bg-slate-800 p-4 rounded-xl"
>
<div class="flex w-full place-content-between">
<div>
<h1 class="text-lg md:text-xl">{{ player.username }}</h1>
<h2 class="text-md md:text-lg font-mono">
{{ dashCode(player.extid) }}
</h2>
</div>
<div class="flex align-middle">
<BaseButton
label="Add Rival"
color="success"
:disabled="player.userId == profile?.userId"
tooltip="penis"
/>
</div>
<BaseButton label="Add Rival" color="success" />
</div>
</CardBox>
</div>
</div>
</CardBox>
<SectionTitleLine :icon="mdiSwordCross" title="Rivals" main />
<CardBox v-if="versionForm.currentVersion" class="mb-6">
<div class="grid gap-3">
<CardBox v-for="rival of rivals" :key="rival.id">
<CardBox v-for="rival of profile?.rivals" :key="rival.id">
<div class="flex justify-between items-center">
<div class="flex">
<img
class="w-10 mr-3"
src="https://static.wikia.nocookie.net/dancedancerevolutionddr/images/3/3b/Yuni_img1.gif/"
/>
<div class="grid">
<h1 class="text-lg">{{ rival.name }}</h1>
<h2 class="text-md font-mono">{{ rival.rivalID }}</h2>
@ -181,6 +214,9 @@ function getCardStyle() {
<BaseButton label="Remove Rival" color="danger" />
</div>
</CardBox>
<CardBox v-if="!profile?.rivals">
<h1 class="text-2xl">You have no rivals!</h1>
</CardBox>
</div>
</CardBox>
</SectionMain>

View File

@ -19,8 +19,8 @@ import FormControl from "@/components/FormControl.vue";
import FormCheckRadio from "@/components/FormCheckRadio.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
import MergeModal from "@/components/MergeModal.vue";
import ConfirmMergeModal from "@/components/ConfirmMergeModal.vue";
import MergeModal from "@/components/Modal/MergeModal.vue";
import ConfirmMergeModal from "@/components/Modal/ConfirmMergeModal.vue";
import { APIStartTakeover, APISaveTakeover } from "@/stores/api/account";
import { getGameInfo } from "@/constants";