Browse Source

Add more prettty and human readable artists URLs

Phyks (Lucas Verney) 5 years ago
parent
commit
e232ec8c16

+ 7
- 0
CONTRIBUTING.md View File

@@ -46,3 +46,10 @@ homogeneous.
46 46
 ## Hooks
47 47
 
48 48
 Usefuls Git hooks are located in `hooks` folder.
49
+
50
+
51
+## Notes on URLs
52
+
53
+Text after any dash in a URL parameter is considered as a comment and
54
+discarded. In `/artist/1-foobar`, the artist ID is `1` and the `foobar` text
55
+is simply considered as a comment for human readable URLs.

+ 3
- 0
app/components/Artists.jsx View File

@@ -26,6 +26,9 @@ export default class Artists extends Component {
26 26
             itemsLabel: "app.common.artist",
27 27
             subItemsType: "albums",
28 28
             subItemsLabel: "app.common.album",
29
+            buildLinkTo: (itemType, item) => {
30
+                return "/" + itemType + "/" + item.get("id") + "-" + encodeURIComponent(item.get("name"));
31
+            },
29 32
         };
30 33
 
31 34
         return (

+ 8
- 3
app/components/elements/Grid.jsx View File

@@ -64,7 +64,10 @@ class GridItemCSSIntl extends Component {
64 64
             { itemCount: nSubItems }
65 65
         );
66 66
 
67
-        const to = "/" + this.props.itemsType + "/" + this.props.item.get("id");
67
+        let to = "/" + this.props.itemsType + "/" + this.props.item.get("id");
68
+        if (this.props.buildLinkTo) {
69
+            to = this.props.buildLinkTo(this.props.itemsType, this.props.item);
70
+        }
68 71
         const id = "grid-item-" + this.props.itemsType + "/" + this.props.item.get("id");
69 72
         const title = formatMessage(gridMessages["app.grid.goTo" + this.props.itemsType.capitalize() + "Page"]);
70 73
 
@@ -85,6 +88,7 @@ GridItemCSSIntl.propTypes = {
85 88
     itemsLabel: PropTypes.string.isRequired,
86 89
     subItemsType: PropTypes.string.isRequired,
87 90
     subItemsLabel: PropTypes.string.isRequired,
91
+    buildLinkTo: PropTypes.func,
88 92
     intl: intlShape.isRequired,
89 93
 };
90 94
 export let GridItem = injectIntl(CSSModules(GridItemCSSIntl, css));
@@ -237,9 +241,9 @@ export class Grid extends Component {
237 241
 
238 242
         // Build grid items
239 243
         let gridItems = [];
240
-        const { itemsType, itemsLabel, subItemsType, subItemsLabel } = this.props;
244
+        const { itemsType, itemsLabel, subItemsType, subItemsLabel, buildLinkTo } = this.props;
241 245
         this.props.items.forEach(function (item) {
242
-            gridItems.push(<GridItem item={item} itemsType={itemsType} itemsLabel={itemsLabel} subItemsType={subItemsType} subItemsLabel={subItemsLabel} key={item.get("id")} />);
246
+            gridItems.push(<GridItem item={item} itemsType={itemsType} itemsLabel={itemsLabel} subItemsType={subItemsType} subItemsLabel={subItemsLabel} buildLinkTo={buildLinkTo} key={item.get("id")} />);
243 247
         });
244 248
 
245 249
         return (
@@ -264,6 +268,7 @@ Grid.propTypes = {
264 268
     itemsLabel: PropTypes.string.isRequired,
265 269
     subItemsType: PropTypes.string.isRequired,
266 270
     subItemsLabel: PropTypes.string.isRequired,
271
+    buildLinkTo: PropTypes.func,
267 272
     filterText: PropTypes.string,
268 273
 };
269 274
 

+ 15
- 4
app/views/ArtistPage.jsx View File

@@ -1,12 +1,12 @@
1 1
 // NPM imports
2
-import React, { Component } from "react";
2
+import React, { Component, PropTypes } from "react";
3 3
 import { bindActionCreators } from "redux";
4 4
 import { connect } from "react-redux";
5 5
 import { defineMessages, injectIntl, intlShape } from "react-intl";
6 6
 import Immutable from "immutable";
7 7
 
8 8
 // Local imports
9
-import { messagesMap, handleErrorI18nObject } from "../utils";
9
+import { messagesMap, handleErrorI18nObject, filterInt } from "../utils";
10 10
 
11 11
 // Actions
12 12
 import * as actionCreators from "../actions";
@@ -26,9 +26,16 @@ const artistMessages = defineMessages(messagesMap(Array.concat([], APIMessages))
26 26
  */
27 27
 class ArtistPageIntl extends Component {
28 28
     componentWillMount() {
29
+        const id = filterInt(this.props.params.id.split("-")[0]);
30
+        if (isNaN(id)) {
31
+            // Redirect to homepage
32
+            this.context.router.replace({
33
+                pathname: "/",
34
+            });
35
+        }
29 36
         // Load the data
30 37
         this.props.actions.loadArtist({
31
-            filter: this.props.params.id,
38
+            filter: id,
32 39
             include: ["albums", "songs"],
33 40
         });
34 41
     }
@@ -53,10 +60,14 @@ class ArtistPageIntl extends Component {
53 60
 ArtistPageIntl.propTypes = {
54 61
     intl: intlShape.isRequired,
55 62
 };
63
+ArtistPageIntl.contextTypes = {
64
+    router: PropTypes.object.isRequired,
65
+};
56 66
 
57 67
 const mapStateToProps = (state, ownProps) => {
68
+    const id = ownProps.params.id.split("-")[0];
58 69
     // Get artist
59
-    let artist = state.entities.getIn(["entities", "artist", ownProps.params.id]);
70
+    let artist = state.entities.getIn(["entities", "artist", id]);
60 71
     let albums = new Immutable.List();
61 72
     let songs = new Immutable.Map();
62 73
     if (artist) {

+ 1
- 1
public/fix.ie9.js.map
File diff suppressed because it is too large
View File


+ 9
- 9
public/index.js
File diff suppressed because it is too large
View File


+ 1
- 1
public/index.js.map
File diff suppressed because it is too large
View File