) {
+ return Object.entries(params)
+ .filter(([_key, value]) => value != null)
+ .map(([key, value]) => {
+ if (Array.isArray(value)) {
+ return value.map((v, i) => `${key}[${i}]=${encodeURIComponent(v)}`).join("&");
+ } else {
+ return `${key}=${encodeURIComponent(value)}`;
+ }
+ })
+ .join("&");
+}
\ No newline at end of file
diff --git a/v2/app/images/icons/preview.png b/v2/app/images/icons/preview.png
new file mode 100755
index 00000000..96a83f9c
Binary files /dev/null and b/v2/app/images/icons/preview.png differ
diff --git a/v2/app/images/icons/user.png b/v2/app/images/icons/user.png
new file mode 100755
index 00000000..c578c0d4
Binary files /dev/null and b/v2/app/images/icons/user.png differ
diff --git a/v2/app/pages/creations.tsx b/v2/app/pages/creations.tsx
new file mode 100644
index 00000000..afb7557d
--- /dev/null
+++ b/v2/app/pages/creations.tsx
@@ -0,0 +1,134 @@
+import { NextPage } from "next";
+import ClassicPage, { commonStyles } from "../components/ClassicPage/ClassicPage";
+import styles from "../styles/Creations.module.scss";
+import cx from "classnames";
+import useLanguage from "../hooks/useLanguage";
+import Ad from "../components/Ad/Ad";
+import WithAppContext from "../components/WithAppContext/WithAppContext";
+import useSmoothFetch, { Placeholder } from "../hooks/useSmoothFetch";
+import Skeleton from "../components/Skeleton/Skeleton";
+import { useMemo } from "react";
+import { useRouter } from "next/router";
+import TrackCreationCard from "../components/TrackCreationCard/TrackCreationCard";
+import { buildQuery } from "../helpers/uris";
+import Link from "next/link";
+import useCreations from "../hooks/useCreations";
+
+const CreationsList: NextPage = () => {
+ const language = useLanguage();
+ const router = useRouter();
+ const { user, admin, tri, type, nom, auteur } = router.query;
+ const { data: creator } = useSmoothFetch<{ name: string }>(`/api/user/${user}`, {
+ disabled: !user
+ });
+ const nTri = +tri || 0;
+ const singleType = (type !== '');
+ const sortTabs = language ? ['By latest', 'Top rated', 'Trending'] : ['Les plus récents', 'Les mieux notés', 'Tendances'];
+ const types = language
+ ? ['Complete mode - multicups', 'Quick mode - multicups', 'Complete mode - cups', 'Quick mode - cups', 'Complete mode - circuits', 'Quick mode - circuits', 'Complete mode - arenas', 'Quick mode - arenas']
+ : ['Mode complet - multicoupes', 'Mode simplifié - multicoupes', 'Mode complet - coupes', 'Mode simplifié - coupes', 'Mode complet - circuits', 'Mode simplifié - circuits', 'Mode complet - arènes', 'Mode simplifié - arènes']
+ const creationParams = useMemo(() => {
+ const params = {
+ user, admin, tri, type, nom, auteur
+ };
+ return buildQuery(params);
+ }, [router.query]);
+ const { data: creationsPayload, loading: creationsLoading } = useSmoothFetch(`/api/getCreations.php?${creationParams}`, {
+ placeholder: () => ({
+ data: Placeholder.array(60, (id) => ({
+ id,
+ author: "",
+ cicon: "",
+ icons: [],
+ href: "",
+ isCup: false,
+ name: Placeholder.text(15, 35),
+ nbComments: Placeholder.number(1, 100),
+ publicationDate: Placeholder.timestamp(),
+ rating: 0,
+ nbRatings: 0
+ })),
+ count: 0,
+ countByType: []
+ })
+ });
+ const creationCount = useMemo(() => {
+ if (creationsLoading) return;
+ return {
+ total: creationsPayload.count,
+ byType: creationsPayload.countByType
+ }
+ }, [creationsPayload, creationsLoading]);
+
+ const { previewCreation } = useCreations();
+
+ function defile() {
+ }
+ function masque() {
+ }
+ function reduceAll() {
+ }
+ function handleTabSelect(e) {
+ e.preventDefault();
+ }
+ function scrollToTop(e) {
+ e.preventDefault();
+ window.scrollTo(0, 0);
+ }
+
+ return (
+
+
+ {creator
+ ? (language ? 'Creations list of ' + creator.name : 'Liste des créations de ' + creator.name)
+ : (language ? 'Creations list of Mario Kart PC' : 'Liste des créations Mario Kart PC')}
+
+ {!user && (
+ language ? <>Welcome to the list of circuits and courses shared by the Mario Kart PC community !
+ You too, share your circuit creations by clicking on "Share circuit" at the bottom-left of the circuit page.>
+ : <>Bienvenue dans la liste des circuits et arènes partagés par la communauté de Mario Kart PC !
+ Vous aussi, partagez les circuits que vous créez en cliquant sur "Partager le circuit" en bas à gauche de la page du circuit.>
+ )}
+
+
+
+
+ {creationsPayload.data.map((creation) => )}
+
+
+
+ defile()} />{" "}
+ masque()} />{" "}
+ reduceAll()} />
+
+
+
+ {language ? 'Back to top' : 'Retour haut de page'} {" - "}
+ {language ? 'Back to Mario Kart PC' : 'Retour à Mario Kart PC'}
+
+
+ );
+}
+
+export default WithAppContext(CreationsList);
\ No newline at end of file
diff --git a/v2/app/pages/index.tsx b/v2/app/pages/index.tsx
index 7591112b..bc85613d 100644
--- a/v2/app/pages/index.tsx
+++ b/v2/app/pages/index.tsx
@@ -57,6 +57,7 @@ import ss12xs from "../images/main/screenshots/ss12xs.png"
import { formatDate } from "../helpers/dates";
import { formatRank, formatTime } from "../helpers/records";
import { escapeHtml } from "../helpers/strings";
+import { buildQuery } from "../helpers/uris";
import { Fragment, useEffect, useMemo, useState } from "react";
import cx from "classnames";
import { uniqBy } from "../helpers/objects";
@@ -246,19 +247,13 @@ const Home: NextPage = () => {
}, [lastNewsRead, newsPayload, lastNewsReadLoading]);
const creationParams = useMemo(() => {
const nbByType = [1, 1, 2, 2, 3, 3, 2, 2];
- let nbByTypeParams = {};
- for (let i = 0; i < nbByType.length; i++)
- nbByTypeParams[`nbByType[${i}]`] = nbByType[i];
- return new URLSearchParams({
- ...nbByTypeParams
- }).toString();
+ return buildQuery({ nbByType });
}, []);
const { data: creationsPayload, loading: creationsLoading } = useSmoothFetch(`/api/getCreations.php?${creationParams}`, {
placeholder: () => ({
data: Placeholder.array(10, (id) => ({
id,
author: "",
- category: Placeholder.text(3, 8),
cicon: "",
icons: [],
href: "",
@@ -668,14 +663,14 @@ const Home: NextPage = () => {
Most of the modes from Mario Kart have been included: Grand Prix, VS, Battle mode, Time Trials, and more!
There's also a brand new mode: the track builder ! Place straight lines and turns, add items, boost panels and more!
Everything is customizable! The only limit is your own imagination!
- You can share your tracks, and try other people's tracks thanks to the sharing tool . Thousands of custom tracks are already available!
+ You can share your tracks, and try other people's tracks thanks to the sharing tool. Thousands of custom tracks are already available!
Finally, you can face players from the whole world thanks to the multiplayer online mode ! Climb the rankings and become world champion!
> : <> Vous connaissez certainement Mario Kart, le jeu de course le plus fun de tous les temps !
Mario Kart PC reprend les mêmes principes que le jeu original mais il est jouable sur navigateur, et gratuitement .
La plupart des modes issus de Mario Kart ont été repris : Grand Prix, courses VS, batailles de ballons, contre-la-montre...
Et un dernier mode inédit : l'éditeur de circuits ! Placez les lignes droites et les virages, ajoutez les objets, insérez des accélérateurs...
Tout est personnalisable ! Votre imagination est la seule limite !
- Vous pouvez également partager vos créations et essayer celles des autres grâce à l'outil de partage .
+ Vous pouvez également partager vos créations et essayer celles des autres grâce à l' outil de partage.
Plusieurs milliers de circuits ont déjà été partagés !
Enfin, il est possible d'affronter les joueurs du monde entier grâce au mode multijoueurs en ligne ! Grimpez dans le classement et devenez champion du monde !
>}
@@ -778,7 +773,7 @@ const Home: NextPage = () => {
{language ? 'All news' : 'Toutes les news'}
-
+
{language ? 'Latest creations' : 'Dernières créations'}
@@ -802,7 +797,7 @@ const Home: NextPage = () => {
-
{language ? 'Display all' : 'Afficher tout'}
+
{language ? 'Display all' : 'Afficher tout'}
{language ? 'Last challenges' : 'Derniers défis'}
{
diff --git a/v2/app/styles/Creations.module.scss b/v2/app/styles/Creations.module.scss
new file mode 100644
index 00000000..efdda158
--- /dev/null
+++ b/v2/app/styles/Creations.module.scss
@@ -0,0 +1,143 @@
+.Creations {
+ position: relative;
+
+ #form-search {
+ text-align: center;
+ margin-bottom: 10px;
+ }
+ #sort-tabs {
+ display: table;
+ margin-left: auto;
+ margin-right: auto;
+ margin-bottom: 6px;
+ text-align: center;
+ font-size: 0.9em;
+ }
+ #sort-tabs > * {
+ display: table-cell;
+ vertical-align: middle;
+ border-top: solid 1px #820;
+ border-bottom: solid 1px #820;
+ padding: 6px 10px;
+ }
+ @media screen and (max-width: 500px) {
+ #sort-tabs {
+ font-size: 0.7em;
+ }
+ #sort-tabs > * {
+ padding: 5px 8px;
+ }
+ }
+ #sort-tabs > a {
+ background-color: #FFE30C;
+ color: #F60;
+ text-decoration: none;
+ }
+ #sort-tabs > a:hover {
+ background-color: #FFD816;
+ color: #C30;
+ }
+ #sort-tabs > span {
+ background-color: #FFC02C;
+ color: #820;
+ font-weight: bold;
+ }
+ #sort-tabs > *:first-child {
+ border-left: solid 1px #820;
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+ }
+ #sort-tabs > *:last-child {
+ border-right: solid 1px #820;
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+ }
+ .pub {
+ width: 100%;
+ overflow: hidden;
+ text-align: center;
+ }
+ .retour {
+ color: #F90;
+ font-weight: bold;
+ }
+ .retour:hover {
+ background-color: #FF9;
+ }
+ select {
+ background-color: #FC0;
+ color: black;
+ border: solid 1px maroon;
+ padding: 2px;
+ }
+ select:hover {
+ background-color: #F90;
+ }
+ select:active {
+ background-color: #F60;
+ }
+ input[type="text"] {
+ background-color: #FFEE99;
+ width: 100px;
+ padding: 2px;
+ }
+ input[type="text"]:hover {
+ background-color: #FFF3A9;
+ }
+ input[type="text"]:focus {
+ background-color: #FFF6CC;
+ }
+ form div {
+ margin: 2px 0px;
+ }
+ h1, h2 {
+ text-decoration: underline;
+ font-family: Verdana;
+ color: #560000;
+ }
+ .subbuttons {
+ margin-top: 12px;
+ }
+ @media screen and (min-width: 880px) {
+ h2, .subbuttons {
+ margin-left: 15%;
+ margin-left: calc(50% - 350px);
+ }
+ .subbuttons {
+ margin-right: 15%;
+ margin-right: calc(50% - 350px);
+ }
+ }
+ .hidden {
+ /*height: px;*/
+ overflow: hidden;
+ text-align: center;
+ }
+ input.defiler {
+ margin-left: 5px;
+ }
+ .defiler {
+ position: relative;
+ top: -4px;
+ }
+ a.defiler::before {
+ content: "+";
+ color: #FED;
+ margin-right: 5px;
+ }
+ a.defiler {
+ float: right;
+ background-color: #F90;
+ }
+ a.defiler:hover {
+ float: right;
+ background-color: #FB0;
+ }
+ .liste {
+ max-width: 851px;
+ margin-left: auto;
+ margin-right: auto;
+ text-align: center;
+ line-height: 0;
+ }
+}
\ No newline at end of file
diff --git a/v2/node-api/src/user/user.controller.ts b/v2/node-api/src/user/user.controller.ts
index fa0967e0..fa458c1d 100644
--- a/v2/node-api/src/user/user.controller.ts
+++ b/v2/node-api/src/user/user.controller.ts
@@ -1,4 +1,4 @@
-import { Controller, Get, HttpCode } from '@nestjs/common';
+import { Controller, Get, HttpCode, Param } from '@nestjs/common';
import { Auth } from '../auth/auth.decorator';
import { EntityManager } from 'typeorm';
import { User } from './user.entity';
@@ -36,4 +36,13 @@ export class UserController {
endDate: ban.endDate
}
}
+
+ @Get("/:id")
+ async getUser(@Param("id") id: number) {
+ const user = await this.em.findOne(User, id);
+ return {
+ id: user.id,
+ name: user.name
+ }
+ }
}
diff --git a/v2/php-api/endpoints/getCreations.php b/v2/php-api/endpoints/getCreations.php
index 6765f5ff..0c54ce55 100644
--- a/v2/php-api/endpoints/getCreations.php
+++ b/v2/php-api/endpoints/getCreations.php
@@ -63,5 +63,6 @@
$nbCreations = array_sum($nbByType);
renderResponse(array(
'data' => $data,
- 'count' => $nbCreations
+ 'count' => $nbCreations,
+ 'countByType' => $nbByType
));