Geometrically 2d416d491c
Project, Search, User redesign (#1281)
* New project page

* fix silly icon tailwind classes

* Start new versions page, add new ButtonStyled component

* Pagination and finish mocking up versions page functionality

* green download button

* hover animation

* New Modal, Avatar refactor, subpages in NavTabs

* lint

* Download modal

* New user page + fix lint

* fix ui lint

* Download animation fix

* Versions filter + finish project page

* Improve consistency of buttons on home page

* Fix ButtonStyled breaking

* Fix margin on version summary

* finish search, new modals, user + project page mobile

* fix gallery image pages

* New project header

* Fix gallery tab showing improperly

* Use auto direction + position for all popouts

* Preliminary user page

* test to see if this fixes login stuff

* remove extra slash

* Add version actions, move download button on versions page

* Listed -> public

* Shorten download modal selector height

* Fix user menu open direction

* Change breakpoint for header collapse

* Only underline title

* Tighten padding on stats a little

* New nav

* Make mobile breakpoint more consistent

* fix header breakpoint regression

* Add sign in button

* Fix edit icon color

* Fix margin at top of screen

* Fix user bios and ad width

* Fix user nav showing when there's only one type of project

* Fix plural projects on user page & extract i18n

* Remove ads on mobile for now

* Fix overflow menu showing hidden items

* NavTabs on mobile

* Fix navbar z index

* Search filter overhaul + negative filters

* fix no-max-height

* port version filters, fix following/collections, lint

* hide promos

* ui lint

* Disable modal background animation to reduce reported motion sickness

* Hide install with modrinth app button on mobile

---------

Signed-off-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
Co-authored-by: Prospector <prospectordev@gmail.com>
2024-08-20 23:03:16 -07:00

258 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export const getProjectTypeForUrl = (type, categories) => {
return getProjectTypeForUrlShorthand(type, categories);
};
export const getProjectTypeForUrlShorthand = (type, categories, overrideTags) => {
const tags = overrideTags ?? useTags().value;
if (type === "mod") {
const isMod = categories.some((category) => {
return tags.loaderData.modLoaders.includes(category);
});
const isPlugin = categories.some((category) => {
return tags.loaderData.allPluginLoaders.includes(category);
});
const isDataPack = categories.some((category) => {
return tags.loaderData.dataPackLoaders.includes(category);
});
if (isDataPack) {
return "datapack";
} else if (isPlugin) {
return "plugin";
} else if (isMod) {
return "mod";
} else {
return "mod";
}
} else {
return type;
}
};
export const getProjectLink = (project) => {
return `/${getProjectTypeForUrl(project.project_type, project.loaders)}/${
project.slug ? project.slug : project.id
}`;
};
export const getVersionLink = (project, version) => {
if (version) {
return getProjectLink(project) + "/version/" + version.id;
} else {
return getProjectLink(project);
}
};
export const isApproved = (project) => {
return project && APPROVED_PROJECT_STATUSES.includes(project.status);
};
export const isListed = (project) => {
return project && LISTED_PROJECT_STATUSES.includes(project.status);
};
export const isUnlisted = (project) => {
return project && UNLISTED_PROJECT_STATUSES.includes(project.status);
};
export const isPrivate = (project) => {
return project && PRIVATE_PROJECT_STATUSES.includes(project.status);
};
export const isRejected = (project) => {
return project && REJECTED_PROJECT_STATUSES.includes(project.status);
};
export const isUnderReview = (project) => {
return project && UNDER_REVIEW_PROJECT_STATUSES.includes(project.status);
};
export const isDraft = (project) => {
return project && DRAFT_PROJECT_STATUSES.includes(project.status);
};
export const APPROVED_PROJECT_STATUSES = ["approved", "archived", "unlisted", "private"];
export const LISTED_PROJECT_STATUSES = ["approved", "archived"];
export const UNLISTED_PROJECT_STATUSES = ["unlisted", "withheld"];
export const PRIVATE_PROJECT_STATUSES = ["private", "rejected", "processing"];
export const REJECTED_PROJECT_STATUSES = ["rejected", "withheld"];
export const UNDER_REVIEW_PROJECT_STATUSES = ["processing"];
export const DRAFT_PROJECT_STATUSES = ["draft"];
export function getVersionsToDisplay(project) {
return formatVersionsForDisplay(project.game_versions.slice());
}
export function formatVersionsForDisplay(gameVersions, overrideTags) {
const tags = overrideTags ?? useTags().value;
const inputVersions = gameVersions.slice();
const allVersions = tags.gameVersions.slice();
const allSnapshots = allVersions.filter((version) => version.version_type === "snapshot");
const allReleases = allVersions.filter((version) => version.version_type === "release");
const allLegacy = allVersions.filter(
(version) => version.version_type !== "snapshot" && version.version_type !== "release",
);
{
const indices = allVersions.reduce((map, gameVersion, index) => {
map[gameVersion.version] = index;
return map;
}, {});
inputVersions.sort((a, b) => indices[a] - indices[b]);
}
const releaseVersions = inputVersions.filter((projVer) =>
allReleases.some((gameVer) => gameVer.version === projVer),
);
const latestReleaseVersionDate = Date.parse(
allReleases.find((version) => version.version === releaseVersions[0])?.date,
);
const latestSnapshot = inputVersions.find((projVer) =>
allSnapshots.some(
(gameVer) =>
gameVer.version === projVer &&
(!latestReleaseVersionDate || latestReleaseVersionDate < Date.parse(gameVer.date)),
),
);
const allReleasesGrouped = groupVersions(
allReleases.map((release) => release.version),
false,
);
const projectVersionsGrouped = groupVersions(releaseVersions, true);
const releaseVersionsAsRanges = projectVersionsGrouped.map(({ major, minor }) => {
if (minor.length === 1) {
return formatVersion(major, minor[0]);
}
if (
allReleasesGrouped
.find((x) => x.major === major)
.minor.every((value, index) => value === minor[index])
) {
return `${major}.x`;
}
return `${formatVersion(major, minor[0])}${formatVersion(major, minor[minor.length - 1])}`;
});
const legacyVersionsAsRanges = groupConsecutiveIndices(
inputVersions.filter((projVer) => allLegacy.some((gameVer) => gameVer.version === projVer)),
allLegacy,
);
let output = [...legacyVersionsAsRanges];
// show all snapshots if there's no release versions
if (releaseVersionsAsRanges.length === 0) {
const snapshotVersionsAsRanges = groupConsecutiveIndices(
inputVersions.filter((projVer) =>
allSnapshots.some((gameVer) => gameVer.version === projVer),
),
allSnapshots,
);
output = [...snapshotVersionsAsRanges, ...output];
} else {
output = [...releaseVersionsAsRanges, ...output];
}
if (latestSnapshot && !output.includes(latestSnapshot)) {
output = [latestSnapshot, ...output];
}
return output;
}
const mcVersionRegex = /^([0-9]+.[0-9]+)(.[0-9]+)?$/;
function groupVersions(versions, consecutive = false) {
return versions
.slice()
.reverse()
.reduce((ranges, version) => {
const matchesVersion = version.match(mcVersionRegex);
if (matchesVersion) {
const majorVersion = matchesVersion[1];
const minorVersion = matchesVersion[2];
const minorNumeric = minorVersion ? parseInt(minorVersion.replace(".", "")) : 0;
let prevInRange;
if (
(prevInRange = ranges.find(
(x) =>
x.major === majorVersion && (!consecutive || x.minor.at(-1) === minorNumeric - 1),
))
) {
prevInRange.minor.push(minorNumeric);
return ranges;
}
return [...ranges, { major: majorVersion, minor: [minorNumeric] }];
}
return ranges;
}, [])
.reverse();
}
function groupConsecutiveIndices(versions, referenceList) {
if (!versions || versions.length === 0) {
return [];
}
const referenceMap = new Map();
referenceList.forEach((item, index) => {
referenceMap.set(item.version, index);
});
const sortedList = versions.slice().sort((a, b) => referenceMap.get(a) - referenceMap.get(b));
const ranges = [];
let start = sortedList[0];
let previous = sortedList[0];
for (let i = 1; i < sortedList.length; i++) {
const current = sortedList[i];
if (referenceMap.get(current) !== referenceMap.get(previous) + 1) {
ranges.push(validateRange(`${previous}${start}`));
start = current;
}
previous = current;
}
ranges.push(validateRange(`${previous}${start}`));
return ranges;
}
function validateRange(range) {
switch (range) {
case "rd-132211b1.8.1":
return "All legacy versions";
case "a1.0.4b1.8.1":
return "All alpha and beta versions";
case "a1.0.4a1.2.6":
return "All alpha versions";
case "b1.0b1.8.1":
return "All beta versions";
case "rd-132211inf20100618":
return "All pre-alpha versions";
}
const splitRange = range.split("");
if (splitRange && splitRange[0] === splitRange[1]) {
return splitRange[0];
}
return range;
}
function formatVersion(major, minor) {
return minor === 0 ? major : `${major}.${minor}`;
}