Affichage d'une collection partagée et correction d'un gros bug

This commit is contained in:
Damien Broqua 2022-03-06 14:24:10 +01:00
parent 4872c4d82c
commit 0703d0bc33
9 changed files with 283 additions and 12 deletions

View file

@ -13,6 +13,7 @@ import { isXhr } from "./helpers";
import indexRouter from "./routes"; import indexRouter from "./routes";
import maCollectionRouter from "./routes/ma-collection"; import maCollectionRouter from "./routes/ma-collection";
import collectionRouter from "./routes/collection";
import importAlbumRouterApiV1 from "./routes/api/v1/albums"; import importAlbumRouterApiV1 from "./routes/api/v1/albums";
import importSearchRouterApiV1 from "./routes/api/v1/search"; import importSearchRouterApiV1 from "./routes/api/v1/search";
@ -83,6 +84,7 @@ app.use(
app.use("/", indexRouter); app.use("/", indexRouter);
app.use("/ma-collection", maCollectionRouter); app.use("/ma-collection", maCollectionRouter);
app.use("/collection", collectionRouter);
app.use("/api/v1/albums", importAlbumRouterApiV1); app.use("/api/v1/albums", importAlbumRouterApiV1);
app.use("/api/v1/search", importSearchRouterApiV1); app.use("/api/v1/search", importSearchRouterApiV1);
app.use("/api/v1/me", importMeRouterApiV1); app.use("/api/v1/me", importMeRouterApiV1);
@ -115,7 +117,10 @@ app.use((error, req, res, next) => {
} else { } else {
res.status(error.errorCode || 500); res.status(error.errorCode || 500);
res.render("index", { res.render("index", {
page: { title: "500: Oups… le serveur a crashé !", error }, page: {
title: error.title || "500: Oups… le serveur a crashé !",
error,
},
errorCode: error.errorCode || 500, errorCode: error.errorCode || 500,
viewname: "error", viewname: "error",
user: req.user || null, user: req.user || null,

View file

@ -4,9 +4,10 @@
class ErrorEvent extends Error { class ErrorEvent extends Error {
/** /**
* @param {Number} errorCode * @param {Number} errorCode
* @param {String} title
* @param {Mixed} ...params * @param {Mixed} ...params
*/ */
constructor(errorCode, ...params) { constructor(errorCode, title, ...params) {
super(...params); super(...params);
if (Error.captureStackTrace) { if (Error.captureStackTrace) {
@ -14,6 +15,7 @@ class ErrorEvent extends Error {
} }
this.errorCode = parseInt(errorCode, 10); this.errorCode = parseInt(errorCode, 10);
this.title = title;
this.date = new Date(); this.date = new Date();
} }
} }

View file

@ -5,6 +5,7 @@ import xl from "excel4node";
import Pages from "./Pages"; import Pages from "./Pages";
import AlbumsModel from "../models/albums"; import AlbumsModel from "../models/albums";
import UsersModel from "../models/users";
import ErrorEvent from "../libs/error"; import ErrorEvent from "../libs/error";
/** /**
@ -493,7 +494,7 @@ class Albums extends Pages {
static async getAllDistincts(field, user) { static async getAllDistincts(field, user) {
const distincts = await AlbumsModel.find( const distincts = await AlbumsModel.find(
{ {
user, User: user,
}, },
[], [],
{ {
@ -519,8 +520,11 @@ class Albums extends Pages {
order = "asc", order = "asc",
artists_sort, artists_sort,
format, format,
userId: collectionUserId,
} = this.req.query; } = this.req.query;
let userId = this.req.user?._id;
const where = {}; const where = {};
if (artists_sort) { if (artists_sort) {
@ -530,8 +534,35 @@ class Albums extends Pages {
where["formats.name"] = format; where["formats.name"] = format;
} }
if (!this.req.user && !collectionUserId) {
throw new ErrorEvent(
401,
"Cette collection n'est pas publique",
"Cette collection n'est pas publique"
);
}
if (collectionUserId) {
const userIsSharingCollection = await UsersModel.findById(
collectionUserId
);
if (
!userIsSharingCollection ||
!userIsSharingCollection.isPublicCollection
) {
throw new ErrorEvent(
401,
"Cette collection n'est pas publique",
"Cette collection n'est pas publique"
);
}
userId = userIsSharingCollection._id;
}
const count = await AlbumsModel.count({ const count = await AlbumsModel.count({
user: this.req.user._id, User: userId,
...where, ...where,
}); });
@ -553,7 +584,7 @@ class Albums extends Pages {
const rows = await AlbumsModel.find( const rows = await AlbumsModel.find(
{ {
user: this.req.user._id, User: userId,
...where, ...where,
}, },
[], [],
@ -625,6 +656,29 @@ class Albums extends Pages {
this.setPageContent("item", item); this.setPageContent("item", item);
} }
/**
* Méthode permettant de créer la page "collection/:userId"
*/
async loadPublicCollection() {
const { userId } = this.req.params;
const user = await UsersModel.findById(userId);
if (!user || !user.isPublicCollection) {
throw new ErrorEvent(
401,
"Cet utilisateur ne souhaite pas partager sa collection"
);
}
const artists = await Albums.getAllDistincts("artists_sort", userId);
const formats = await Albums.getAllDistincts("formats.name", userId);
this.setPageContent("username", user.username);
this.setPageContent("artists", artists);
this.setPageContent("formats", formats);
}
} }
export default Albums; export default Albums;

View file

@ -1,7 +1,6 @@
import Joi from "joi"; import Joi from "joi";
import mongoose from "mongoose";
const Users = mongoose.model("Users"); import UsersModel from "../models/users";
/** /**
* Classe permettant la gestion de l'utilisateur connecté * Classe permettant la gestion de l'utilisateur connecté
@ -23,7 +22,7 @@ class Me {
}); });
const value = await schema.validateAsync(body); const value = await schema.validateAsync(body);
const update = await Users.findByIdAndUpdate( const update = await UsersModel.findByIdAndUpdate(
user._id, user._id,
{ $set: value }, { $set: value },
{ new: true } { new: true }

View file

@ -9,7 +9,7 @@ const router = express.Router();
router router
.route("/") .route("/")
.get(ensureLoggedIn("/connexion"), async (req, res, next) => { .get(async (req, res, next) => {
try { try {
const albums = new Albums(req); const albums = new Albums(req);
const data = await albums.getAll(); const data = await albums.getAll();

22
src/routes/collection.js Normal file
View file

@ -0,0 +1,22 @@
import express from "express";
import Albums from "../middleware/Albums";
import render from "../libs/format";
// eslint-disable-next-line new-cap
const router = express.Router();
router.route("/:userId").get(async (req, res, next) => {
try {
const page = new Albums(req, "collection");
await page.loadPublicCollection();
render(res, page);
} catch (err) {
next(err);
}
});
export default router;

View file

@ -5,7 +5,9 @@
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" /> <img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
</p> </p>
<% } %> <% } %>
<% if ( process.env.NODE_ENV !== 'production' ) { %>
<div> <div>
<pre><%= page.error %></pre> <pre><%= page.error %></pre>
</div> </div>
<% } %>
</main> </main>

184
views/pages/collection.ejs Normal file
View file

@ -0,0 +1,184 @@
<main class="layout-maxed ma-collection" id="app">
<h1>
Collection de <%= page.username %>
</h1>
<div class="filters">
<div class="field">
<label for="artist">Artiste</label>
<select id="artist" v-model="artist" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.artists.length; i += 1 ) {
__append(`<option value="${page.artists[i]}">${page.artists[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="format">Format</label>
<select id="format" v-model="format" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.formats.length; i += 1 ) {
__append(`<option value="${page.formats[i]}">${page.formats[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="sortOrder">Trier par</label>
<select id="sortOrder" v-model="sortOrder" @change="changeSort">
<option value="artists_sort-asc">Artiste (A-Z)</option>
<option value="artists_sort-desc">Artiste (Z-A)</option>
<option value="year-asc">Année (A-Z)</option>
<option value="year-desc">Année (Z-A)</option>
<option value="country-asc">Pays (A-Z)</option>
<option value="country-desc">Pays (Z-A)</option>
<option value="formats.name-asc">Format (A-Z)</option>
<option value="formats.name-desc">Format (Z-A)</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 list">
<div class="item" v-if="!loading" v-for="item in items">
<span class="title">
{{ item.artists_sort}} - {{ item.title }}
</span>
<div class="grid grid-cols-2 md:grid-cols-4">
<div>
<img :src="item.thumb" :alt="item.title" />
</div>
<div class="md:col-span-3">
<span><strong>Année :</strong> {{ item.year }}</span>
<br />
<span><strong>Pays :</strong> {{ item.country }}</span>
<br />
<span>
<strong>Format : </strong>
<span v-for="(format, index) in item.formats">
{{ format.name }}
<template v-if="format.descriptions">
(<template v-for="(description, j) in format.descriptions">
{{description}}<template v-if="j < format.descriptions.length - 1">, </template>
</template>)
</template>
<template v-if="index < item.formats.length - 1">, </template>
</span>
</span>
<br />
<span><strong>Genre :</strong> <template v-for="(genre, index) in item.genres">{{ genre }}<template v-if="index < item.genres.length - 1">, </template></template></span>
<br />
<span><strong>Style :</strong> <template v-for="(style, index) in item.styles">{{ style }}<template v-if="index < item.styles.length - 1">, </template></template></span>
</div>
</div>
</div>
</div>
<div class="total">
<strong>Nombre total d'éléments : </strong>{{total}}
</div>
<nav class="pagination" role="navigation" aria-label="Pagination">
<ul class="pagination-list">
<template v-for="p in Array.from({length: totalPages}, (v, i) => (i+1))">
<template v-if="p < 2 || p > (totalPages - 1) || (page - 1) <= p && page + 1 >= p">
<li>
<a class="pagination-link" :class="{'is-current': p === page}" @click="goTo(p)" aria-label="Aller à la page {{p}}">{{ p }}</a>
</li>
</template>
<template v-if="(page - 3 === p && page - 2 > 1) || (page + 2 === p && page + 2 < totalPages - 1)">
<li>
<a class="pagination-link is-disabled">…</a>
</li>
</template>
</template>
</ul>
</nav>
</main>
<script>
const {
protocol,
host
} = window.location;
Vue.createApp({
data() {
return {
loading: false,
items: [],
total: 0,
page: 1,
totalPages: 1,
limit: 16,
artist: '',
format: '',
sortOrder: 'artists_sort-asc',
sort: 'artists_sort',
order: 'asc',
userId: "<%= params.userId %>",
}
},
created() {
this.fetch();
},
methods: {
fetch() {
this.loading = true;
let url = `/api/v1/albums?userId=${this.userId}&page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`;
if ( this.artist ) {
url += `&artists_sort=${this.artist}`;
}
if ( this.format ) {
url += `&format=${this.format}`;
}
axios.get(url)
.then( response => {
this.items = response.data.rows;
this.total = response.data.count;
this.totalPages = parseInt(response.data.count / this.limit) + (response.data.count % this.limit > 0 ? 1 : 0);
})
.catch((err) => {
showToastr(err.response?.data?.message || "Impossible de charger cette collection");
})
.finally(() => {
this.loading = false;
});
},
next(event) {
event.preventDefault();
this.page += 1;
this.fetch();
},
previous(event) {
event.preventDefault();
this.page -= 1;
this.fetch();
},
goTo(page) {
this.page = page;
this.fetch();
},
changeSort() {
const [sort,order] = this.sortOrder.split('-');
this.sort = sort;
this.order = order;
this.page = 1;
this.fetch();
},
changeFilter() {
this.page = 1;
this.fetch();
},
}
}).mount('#app');
</script>

View file

@ -3,6 +3,9 @@
Ma collection Ma collection
<i class="icon-share" @click="toggleModalShare" aria-label="Partager ma collection" title="Votre collection sera visible en lecture aux personnes ayant le lien de partage"></i> <i class="icon-share" @click="toggleModalShare" aria-label="Partager ma collection" title="Votre collection sera visible en lecture aux personnes ayant le lien de partage"></i>
</h1> </h1>
<a :href="shareLink" v-if="isPublicCollection" target="_blank">
<i class="icon-share"></i> Voir ma collection partagée
</a>
<div class="filters"> <div class="filters">
<div class="field"> <div class="field">
<label for="artist">Artiste</label> <label for="artist">Artiste</label>
@ -257,11 +260,11 @@
}) })
.then( (res) => { .then( (res) => {
this.isPublicCollection = res.data.isPublicCollection; this.isPublicCollection = res.data.isPublicCollection;
showToastr("Collection partagée", true);
if ( this.isPublicCollection ) { if ( this.isPublicCollection ) {
console.log('ici', this.shareLink) showToastr("Votre collection est désormais publique", true);
window.open(this.shareLink, '_blank'); } else {
showToastr("Votre collection n'est plus partagée", true);
} }
}) })
.catch((err) => { .catch((err) => {