diff --git a/.env.development b/.env.development
index 10b1237..cd1c9c3 100644
--- a/.env.development
+++ b/.env.development
@@ -1,4 +1,4 @@
-VITE_APP_VERSION="3.0.27"
+VITE_APP_VERSION="3.0.28"
VITE_API_URL="http://10.5.7.5:8000/"
VITE_API_KEY="your_api_key_should_be_here"
VITE_ASSET_PATH="/assets"
diff --git a/.env.production b/.env.production
index 46c01ff..8682841 100644
--- a/.env.production
+++ b/.env.production
@@ -1,4 +1,4 @@
-VITE_APP_VERSION="3.0.27"
+VITE_APP_VERSION="3.0.28"
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"
diff --git a/public/assets/games/ddr/card/20.webp b/public/assets/games/ddr/card/20.webp
new file mode 100644
index 0000000..de028f0
Binary files /dev/null and b/public/assets/games/ddr/card/20.webp differ
diff --git a/public/data-sources/changelog.json b/public/data-sources/changelog.json
index 94b86b7..cc8849f 100644
--- a/public/data-sources/changelog.json
+++ b/public/data-sources/changelog.json
@@ -26,5 +26,6 @@
"3.0.24": ["- (Bugfix) Fix theme forcing for dark mode", "- (Bugfix) Fix animations across entire app", "- (Bugfix) Fix cursor on login screen easter egg", "- (Minor) Add new greeting"],
"3.0.25-oMini": ["- (Major ++) Move from Material Design Icons to Phosphor Icons. Icons are currently set to their initial picks, but may change (suggest a change!)", "- (Major) Add initial icon animations, start work on icon color standards", "- (Optimization) Improve random greeting selection (SoftieTechCat) ", "- (Minor) Fix ESlint config"],
"3.0.26": ["- (Minor) Game player table is more sortable", "- (Minor) Greetings now support custom styling", "- (Minor) Added the first styled greeting"],
- "3.0.27": ["- (Major) Profile data exporting added.", "- (Minor) More work for rivals."]
+ "3.0.27": ["- (Major) Profile data exporting added.", "- (Minor) More work for rivals."],
+ "3.0.28": ["- (Major) Full support for rivals across applicable games", "- (Major) Move from Vue Hash routing to standard routing", "- (Bugfix) Fix issue when customizing on iOS"]
}
\ No newline at end of file
diff --git a/src/components/ArcadeCard.vue b/src/components/ArcadeCard.vue
index 7b02d76..809ef37 100644
--- a/src/components/ArcadeCard.vue
+++ b/src/components/ArcadeCard.vue
@@ -95,26 +95,26 @@ function getCardStyle(path) {
setTimeout(resolve, 200));
+ }
+ const userId = mainStore.userId;
+
+ try {
+ await mainStore.callApi(`/profile/${game}/link`, "PUT", {
+ version: version,
+ userId: userId,
+ otherUserId: otherUserId,
+ type: type,
+ });
+ return true;
+ } catch (error) {
+ console.log("Error putting profile link:", error);
+ throw error;
+ }
+}
+
+export async function APIDeleteLink(game, version, otherUserId, type) {
+ while (!mainStore.userId) {
+ await new Promise((resolve) => setTimeout(resolve, 200));
+ }
+ const userId = mainStore.userId;
+
+ const confirmed = window.confirm("Are you really?");
+ if (confirmed) {
+ try {
+ await mainStore.callApi(`/profile/${game}/link`, "DELETE", {
+ version: version,
+ userId: userId,
+ otherUserId: otherUserId,
+ type: type,
+ });
+ return true;
+ } catch (error) {
+ console.log("Error deleting profile link:", error);
+ throw error;
+ }
+ } else {
+ return false;
+ }
+}
diff --git a/src/views/Arcade/ClaimView.vue b/src/views/Arcade/ClaimView.vue
index 67062a7..879d8c8 100644
--- a/src/views/Arcade/ClaimView.vue
+++ b/src/views/Arcade/ClaimView.vue
@@ -132,7 +132,7 @@ async function saveSettings() {
diff --git a/src/views/Auth/LoginView.vue b/src/views/Auth/LoginView.vue
index 5d81ebc..5471a4f 100644
--- a/src/views/Auth/LoginView.vue
+++ b/src/views/Auth/LoginView.vue
@@ -100,18 +100,10 @@ const submit = async () => {
Not Registered?
-
+
Forgot Password?
-
+
diff --git a/src/views/Auth/RegisterView.vue b/src/views/Auth/RegisterView.vue
index daa76fe..e9d5ed3 100644
--- a/src/views/Auth/RegisterView.vue
+++ b/src/views/Auth/RegisterView.vue
@@ -186,7 +186,7 @@ async function registerProfile() {
Have an Account?
-
+
diff --git a/src/views/Auth/ResetView.vue b/src/views/Auth/ResetView.vue
index 248791d..2647058 100644
--- a/src/views/Auth/ResetView.vue
+++ b/src/views/Auth/ResetView.vue
@@ -177,7 +177,7 @@ async function resetPassword() {
Remember it?
-
+
diff --git a/src/views/ErrorView.vue b/src/views/ErrorView.vue
index 298f2f1..85db1d8 100644
--- a/src/views/ErrorView.vue
+++ b/src/views/ErrorView.vue
@@ -26,7 +26,7 @@ import LayoutGuest from "@/layouts/LayoutGuest.vue";
The server was unable to locate the page you're trying to go to.
-
+
diff --git a/src/views/Game/NetworkRecords.vue b/src/views/Game/NetworkRecords.vue
index 74e1a5e..edec9f2 100644
--- a/src/views/Game/NetworkRecords.vue
+++ b/src/views/Game/NetworkRecords.vue
@@ -87,7 +87,7 @@ const filteredSongs = computed(() => {
diff --git a/src/views/Game/PersonalRecords.vue b/src/views/Game/PersonalRecords.vue
index 5a1a7c3..d637039 100644
--- a/src/views/Game/PersonalRecords.vue
+++ b/src/views/Game/PersonalRecords.vue
@@ -92,14 +92,14 @@ const filteredSongs = computed(() => {
{
diff --git a/src/views/Game/PersonalScores.vue b/src/views/Game/PersonalScores.vue
index 2023022..02b331e 100644
--- a/src/views/Game/PersonalScores.vue
+++ b/src/views/Game/PersonalScores.vue
@@ -185,14 +185,14 @@ const navigateToSong = (item) => {
versionForm.currentVersion,
@@ -41,19 +48,25 @@ watch(
loadProfile();
loadLinks();
loadAllProfiles();
+ versionMaxRivals();
},
);
onMounted(async () => {
- loadAllProfiles();
- await loadProfile();
- loadLinks();
+ await loadAll();
});
if (!thisGame.versions) {
versionForm.currentVersion = 1;
}
+async function loadAll() {
+ loadAllProfiles();
+ await loadProfile();
+ loadLinks();
+ filterForm.filter = null;
+}
+
async function loadProfile() {
try {
profile.value = null;
@@ -63,6 +76,10 @@ async function loadProfile() {
if (data && !versionForm.currentVersion) {
versionForm.currentVersion = data.versions[data.versions.length - 1];
}
+
+ if (thisGame.useActiveRival) {
+ getActiveRivals();
+ }
} catch (error) {
console.error("Failed to fetch user profile data:", error);
}
@@ -71,7 +88,7 @@ async function loadProfile() {
async function loadLinks() {
try {
links.value = null;
- const linkData = await APIGetLinks(gameID, versionForm.currentVersion);
+ var linkData = await APIGetLinks(gameID, versionForm.currentVersion);
links.value = linkData;
} catch (error) {
console.error("Failed to fetch user link data:", error);
@@ -111,19 +128,196 @@ if (!thisGame) {
}
function filterProfiles() {
- if (filterForm.filter) {
+ var inputForm = JSON.parse(JSON.stringify(filterForm.filter));
+ if (inputForm) {
+ inputForm = inputForm.replace(/-/g, "");
return allProfiles.value.filter(
(rival) =>
- rival.username
- .toLowerCase()
- .includes(filterForm.filter.toLowerCase()) ||
- rival.extid
- .toString()
- .toLowerCase()
- .includes(filterForm.filter.toLowerCase()),
+ rival.username.toLowerCase().includes(inputForm.toLowerCase()) ||
+ rival.extid.toString().toLowerCase().includes(inputForm.toLowerCase()),
);
}
}
+
+function checkDisabled(rivalId) {
+ if (rivalId == profile.value?.userId) {
+ return true;
+ }
+
+ if (links.value == null) {
+ return false;
+ }
+
+ if (links.value.length >= maxRivals.value) {
+ return true;
+ }
+
+ return links.value.some((link) => link.otherUserId === rivalId);
+}
+
+function versionMaxRivals() {
+ const versionInfo = thisGame.versions.find(
+ (version) => version.id == versionForm.currentVersion,
+ );
+
+ maxRivals.value = versionInfo?.maxRivals ?? 3;
+ maxActiveRivals.value = versionInfo?.maxActiveRivals ?? 3;
+}
+
+function getActiveRivals() {
+ if (!profile.value?.last) {
+ activeRivals.value = [];
+ return;
+ }
+
+ var rivalList = [];
+ const keys = ["fri", "rival1", "rival2", "rival3"];
+ for (const key of keys) {
+ const rivalId = profile.value?.last[key];
+ if (rivalId >= 1) {
+ rivalList.push(rivalId);
+ }
+ }
+
+ activeRivals.value = rivalList;
+}
+
+async function addRival(otherUserId) {
+ if (links.value && links.value.length >= maxRivals.value) {
+ return;
+ }
+
+ var type = "rival";
+ if (thisGame?.useActiveRival) {
+ const usedTypes = new Set(
+ (links.value ?? [])
+ .map((link) => {
+ if (link?.type?.startsWith("friend_")) {
+ return parseInt(link.type.replace("friend_", ""), 10);
+ }
+ return null;
+ })
+ .filter((v) => v !== null && !isNaN(v)),
+ );
+
+ var availableId = null;
+ for (let i = 0; i <= 9; i++) {
+ if (!usedTypes.has(i)) {
+ availableId = i;
+ break;
+ }
+ }
+ type = `friend_${availableId}`;
+ }
+
+ try {
+ await APIPutLink(gameID, versionForm.currentVersion, otherUserId, type);
+ await loadAll();
+ } catch (error) {
+ console.error("Failed to add rival:", error);
+ }
+}
+
+async function deleteRival(otherUserId, type) {
+ try {
+ const deleteState = await APIDeleteLink(
+ gameID,
+ versionForm.currentVersion,
+ otherUserId,
+ type,
+ );
+ if (deleteState) {
+ if (thisGame.useActiveRival) {
+ await setInactive(type);
+ }
+ }
+
+ await loadAll();
+ } catch (error) {
+ console.error("Failed to delete rival:", error);
+ }
+}
+
+async function setActive(type) {
+ var lastObject = profile.value?.last ?? {};
+ var rivalPosition = null;
+ if (type.startsWith("friend_")) {
+ rivalPosition = parseInt(type.replace("friend_", ""), 10);
+ } else {
+ return;
+ }
+
+ if ([GameConstants.DDR, GameConstants.DDROMNI].includes(gameID)) {
+ if (
+ [VersionConstants.DDR_X, VersionConstants.DDR_X2].includes(
+ versionForm.currentVersion,
+ )
+ ) {
+ lastObject.fri = rivalPosition + 1;
+ } else {
+ if (!lastObject.rival1 || lastObject.rival1 < 1) {
+ lastObject.rival1 = rivalPosition + 1;
+ } else if (!lastObject.rival2 || lastObject.rival2 < 1) {
+ lastObject.rival2 = rivalPosition + 1;
+ } else if (!lastObject.rival3 || lastObject.rival3 < 1) {
+ lastObject.rival3 = rivalPosition + 1;
+ }
+ }
+ }
+
+ await APIUpdateProfile(gameID, versionForm.currentVersion, {
+ last: lastObject,
+ });
+ await loadAll();
+}
+
+async function setInactive(type) {
+ var lastObject = profile.value?.last ?? {};
+ var rivalPosition = null;
+
+ if (type.startsWith("friend_")) {
+ rivalPosition = parseInt(type.replace("friend_", ""), 10);
+ } else {
+ return;
+ }
+
+ if ([GameConstants.DDR, GameConstants.DDROMNI].includes(gameID)) {
+ if (
+ [VersionConstants.DDR_X, VersionConstants.DDR_X2].includes(
+ versionForm.currentVersion,
+ )
+ ) {
+ if (lastObject.fri === rivalPosition + 1) {
+ lastObject.fri = 0;
+ }
+ } else {
+ if (lastObject.rival1 === rivalPosition + 1) {
+ lastObject.rival1 = 0;
+ } else if (lastObject.rival2 === rivalPosition + 1) {
+ lastObject.rival2 = 0;
+ } else if (lastObject.rival3 === rivalPosition + 1) {
+ lastObject.rival3 = 0;
+ }
+ }
+ }
+
+ await APIUpdateProfile(gameID, versionForm.currentVersion, {
+ last: lastObject,
+ });
+ await loadAll();
+}
+
+const getLinkNum = (type) => {
+ if (type.startsWith("friend_")) {
+ return parseInt(type.replace("friend_", ""), 10) + 1;
+ } else {
+ return;
+ }
+};
+
+const navigateToProfile = (userID) => {
+ $router.push(`/games/${gameID}/profiles/${userID}`);
+};
@@ -175,7 +369,7 @@ function filterProfiles() {
:key="player.id"
class="bg-slate-800 p-4 rounded-xl"
>
-
+
{{ player.username }}
@@ -183,36 +377,91 @@ function filterProfiles() {
-
+
+
-
+
+
+ {{ links?.length ?? 0 }} / {{ maxRivals }} added
+
+ -
+ {{ activeRivals?.length ?? 0 }} /
+ {{ maxActiveRivals }} active
+
+
-
-
+
+
-
-
{{ rival.name }}
- {{ rival.rivalID }}
-
+
+ {{ link?.otherProfileData?.username }}
+
+ -
+ Active
+ Inactive
+
+
-
+
+
+
+
+
+
+
+
+
-
- {{ links }}
-
+
+
+ No rivals!
+
diff --git a/src/views/Profile/ClaimView.vue b/src/views/Profile/ClaimView.vue
index c4a76e3..fc221e8 100644
--- a/src/views/Profile/ClaimView.vue
+++ b/src/views/Profile/ClaimView.vue
@@ -248,7 +248,7 @@ async function saveSettings() {
-
+
diff --git a/src/views/Profile/CustomizeView.vue b/src/views/Profile/CustomizeView.vue
index dcacb7d..e85426e 100644
--- a/src/views/Profile/CustomizeView.vue
+++ b/src/views/Profile/CustomizeView.vue
@@ -1,6 +1,5 @@