Polish on lists; Moving URI en/decoding for Mopidy to formatter

This commit is contained in:
James Barnsley
2021-01-06 06:32:36 +13:00
parent 37a06a304c
commit 67889670a1
12 changed files with 223 additions and 149 deletions

View File

@ -106479,14 +106479,14 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _LinksSentence__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./LinksSentence */ "./src/js/components/LinksSentence.js");
/* harmony import */ var _Dater__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Dater */ "./src/js/components/Dater.js");
/* harmony import */ var _URILink__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./URILink */ "./src/js/components/URILink.js");
/* harmony import */ var _ContextMenuTrigger__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ContextMenuTrigger */ "./src/js/components/ContextMenuTrigger.js");
/* harmony import */ var _Icon__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./Icon */ "./src/js/components/Icon.js");
/* harmony import */ var _Thumbnail__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./Thumbnail */ "./src/js/components/Thumbnail.js");
/* harmony import */ var _Popularity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./Popularity */ "./src/js/components/Popularity.js");
/* harmony import */ var _util_helpers__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../util/helpers */ "./src/js/util/helpers.js");
/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../locale */ "./src/js/locale/index.js");
/* harmony import */ var _util_arrays__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../util/arrays */ "./src/js/util/arrays.js");
/* harmony import */ var _NiceNumber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./NiceNumber */ "./src/js/components/NiceNumber.js");
/* harmony import */ var _URILink__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./URILink */ "./src/js/components/URILink.js");
/* harmony import */ var _ContextMenuTrigger__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ContextMenuTrigger */ "./src/js/components/ContextMenuTrigger.js");
/* harmony import */ var _Icon__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./Icon */ "./src/js/components/Icon.js");
/* harmony import */ var _Thumbnail__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./Thumbnail */ "./src/js/components/Thumbnail.js");
/* harmony import */ var _Popularity__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./Popularity */ "./src/js/components/Popularity.js");
/* harmony import */ var _util_helpers__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../util/helpers */ "./src/js/util/helpers.js");
/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../locale */ "./src/js/locale/index.js");
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
@ -106557,7 +106557,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
if (e.target.tagName.toLowerCase() !== 'a') {
e.preventDefault();
history.push("".concat(link_prefix).concat(encodeURIComponent(uri)));
Object(_util_helpers__WEBPACK_IMPORTED_MODULE_8__["scrollTo"])();
Object(_util_helpers__WEBPACK_IMPORTED_MODULE_9__["scrollTo"])();
}
});
@ -106571,7 +106571,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
if (e.target.tagName.toLowerCase() !== 'a') {
e.preventDefault();
history.push("".concat(link_prefix).concat(encodeURIComponent(uri)));
Object(_util_helpers__WEBPACK_IMPORTED_MODULE_8__["scrollTo"])();
Object(_util_helpers__WEBPACK_IMPORTED_MODULE_9__["scrollTo"])();
}
});
@ -106596,9 +106596,9 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
if (key === 'tracks') {
var total = item.tracks_total;
if (!total && !value) return null;
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.tracks",
count: total || value.length,
count: Object(_NiceNumber__WEBPACK_IMPORTED_MODULE_3__["nice_number"])(total || value.length),
"data-qa-node": "I18n",
"data-qa-file": "ListItem"
});
@ -106613,9 +106613,9 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
"data-qa-file": "ListItem"
});
if (!_total && !array) return null;
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.artists",
count: _total || array.length,
count: Object(_NiceNumber__WEBPACK_IMPORTED_MODULE_3__["nice_number"])(_total || array.length),
"data-qa-node": "I18n",
"data-qa-file": "ListItem"
});
@ -106625,7 +106625,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
var _total2 = item.albums_total,
_array = item.albums_uris;
if (!_total2 && !_array) return null;
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.albums",
count: _total2 || _array.length,
"data-qa-node": "I18n",
@ -106637,25 +106637,25 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
if (!value) return null;
if (key === 'followers') {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.followers",
count: value.toLocaleString(),
count: Object(_NiceNumber__WEBPACK_IMPORTED_MODULE_3__["nice_number"])(value),
"data-qa-node": "I18n",
"data-qa-file": "ListItem"
});
}
if (key === 'listeners') {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.listeners",
count: value.toLocaleString(),
count: Object(_NiceNumber__WEBPACK_IMPORTED_MODULE_3__["nice_number"])(value),
"data-qa-node": "I18n",
"data-qa-file": "ListItem"
});
}
if (key === 'added_at') {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.added_ago",
time: Object(_Dater__WEBPACK_IMPORTED_MODULE_2__["dater"])('ago', value),
"data-qa-node": "I18n",
@ -106664,7 +106664,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
}
if (key === 'last_modified') {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_9__["I18n"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_10__["I18n"], {
path: "specs.updated_ago",
time: Object(_Dater__WEBPACK_IMPORTED_MODULE_2__["dater"])('ago', value),
"data-qa-node": "I18n",
@ -106673,7 +106673,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
}
if (key === 'owner') {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_URILink__WEBPACK_IMPORTED_MODULE_3__["default"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_URILink__WEBPACK_IMPORTED_MODULE_4__["default"], {
type: "user",
uri: value.uri,
"data-qa-node": "URILink",
@ -106682,7 +106682,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
}
if (key === 'popularity') {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Popularity__WEBPACK_IMPORTED_MODULE_7__["default"], {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Popularity__WEBPACK_IMPORTED_MODULE_8__["default"], {
full: true,
popularity: value,
"data-qa-node": "Popularity",
@ -106690,7 +106690,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
});
}
if (value === true) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Icon__WEBPACK_IMPORTED_MODULE_5__["default"], {
if (value === true) return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Icon__WEBPACK_IMPORTED_MODULE_6__["default"], {
name: "check",
"data-qa-node": "Icon",
"data-qa-file": "ListItem"
@ -106716,7 +106716,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
// try fetching them from LastFM
if (!item.images) {
switch (Object(_util_helpers__WEBPACK_IMPORTED_MODULE_8__["uriType"])(item.uri)) {
switch (Object(_util_helpers__WEBPACK_IMPORTED_MODULE_9__["uriType"])(item.uri)) {
case 'artist':
if (discogsActions) {//discogsActions.getArtistImages(item.uri, item);
}
@ -106771,7 +106771,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
"data-qa-node": "span",
"data-qa-file": "ListItem"
}, _this2.renderValue(column, item));
}), !nocontext && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_ContextMenuTrigger__WEBPACK_IMPORTED_MODULE_4__["default"], {
}), !nocontext && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_ContextMenuTrigger__WEBPACK_IMPORTED_MODULE_5__["default"], {
className: "list__item__column__item list__item__column__item--context-menu-trigger subtle",
onTrigger: this.handleContextMenu,
"data-qa-node": "ContextMenuTrigger",
@ -106780,7 +106780,7 @@ var ListItem = /*#__PURE__*/function (_React$Component) {
className: "list__item__column list__item__column--name",
"data-qa-node": "div",
"data-qa-file": "ListItem"
}, thumbnail && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Thumbnail__WEBPACK_IMPORTED_MODULE_6__["default"], {
}, thumbnail && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_Thumbnail__WEBPACK_IMPORTED_MODULE_7__["default"], {
className: "list__item__column__item list__item__column__item--thumbnail",
images: item.images,
size: "small",
@ -107285,7 +107285,7 @@ __webpack_require__.r(__webpack_exports__);
var nice_number = function nice_number(value) {
var formatted = parseInt(value); // > 1 million
var formatted = parseInt(value, 10); // > 1 million
if (formatted > 1000000) {
formatted /= 1000000;
@ -107848,8 +107848,7 @@ var Parallax = /*#__PURE__*/function (_React$Component) {
_this.state = {
loaded: false,
url: null,
outgoingUrl: null
url: null
};
return _this;
}
@ -107857,8 +107856,10 @@ var Parallax = /*#__PURE__*/function (_React$Component) {
_createClass(Parallax, [{
key: "componentDidMount",
value: function componentDidMount() {
if (this.props.image) {
this.loadImage(this.props.image);
var image = this.props.image;
if (image) {
this.loadImage(image);
}
}
}, {
@ -107893,7 +107894,6 @@ var Parallax = /*#__PURE__*/function (_React$Component) {
var _this$props = this.props,
blur = _this$props.blur,
fixedHeight = _this$props.fixedHeight,
image = _this$props.image,
_this$props$animate = _this$props.animate,
animate = _this$props$animate === void 0 ? true : _this$props$animate;
var _this$state = this.state,
@ -125586,7 +125586,7 @@ var snapcastPersistConfig = {
var pusherPersistConfig = {
key: 'pusher',
storage: localforage__WEBPACK_IMPORTED_MODULE_2___default.a,
blacklist: ['connections'],
blacklist: ['connected', 'connections'],
debug: window.test_mode
};
var mopidyPersistConfig = {
@ -125922,7 +125922,7 @@ var shuffle = function shuffle(array) {
/*!*******************************!*\
!*** ./src/js/util/format.js ***!
\*******************************/
/*! exports provided: toJSON, getTrackIcon, digestMopidyImages, formatImages, formatSimpleObject, formatSimpleObjects, formatAlbum, formatAlbums, formatArtist, formatArtists, formatPlaylist, formatPlaylists, formatUser, formatUsers, formatTrack, formatTracks, formatClient, formatGroup, formatCategory, formatCategories, collate, collateLibrary, injectSortId, default */
/*! exports provided: toJSON, getTrackIcon, digestMopidyImages, formatImages, formatSimpleObject, formatSimpleObjects, formatAlbum, formatAlbums, formatArtist, formatArtists, formatPlaylist, formatPlaylists, formatUser, formatUsers, formatTrack, formatTracks, formatClient, formatGroup, formatCategory, formatCategories, collate, collateLibrary, injectSortId, encodeUri, decodeUri, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@ -125950,6 +125950,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "collate", function() { return collate; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "collateLibrary", function() { return collateLibrary; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "injectSortId", function() { return injectSortId; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "encodeUri", function() { return encodeUri; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "decodeUri", function() { return decodeUri; });
/* harmony import */ var lodash__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js");
/* harmony import */ var lodash__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lodash__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _helpers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./helpers */ "./src/js/util/helpers.js");
@ -126328,6 +126330,59 @@ var formatSimpleObjects = function formatSimpleObjects() {
return formatted;
};
/**
* Prepare a URI for use in a URL
*
* Needs to have all special characters encoded to avoid being parsed incorrectly, especially
* '/' as this is a URL parameter delimiter
* @param {String} uri
*/
var encodeUri = function encodeUri(uri) {
return encodeURIComponent(uri);
};
/**
* Rebuild a URI with some ugly-ass handling of encoding.
*
* Basically the ID part of a Mopidy URI needs to be encoded, but the rest of the URI can't be.
* This means we need to break down the URI (decoded) and then reconstruct with an encoded ID.
* This is all required because he URI is passed to us *from* a URL which has been encoded for
* obvious reasons.
*
* For example somebackend:track:https://youtube.com/1234 would cause issues with the
* https:// section. Our explode-by-':' approach would break this type of URI.
*
* @param {String} rawUri
*/
var decodeUri = function decodeUri(rawUri) {
var uri = decodeURIComponent(rawUri);
var source = Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["uriSource"])(uri);
var type = Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["uriType"])(uri); // Escape unreserved characters (RFC 3986)
// https://stackoverflow.com/questions/18251399/why-doesnt-encodeuricomponent-encode-single-quotes-apostrophes
var id = Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["getFromUri"])("".concat(type, "id"), uri);
id = encodeURIComponent(id).replace(/[!'()*]/g, escape); // Reinstate slashes for the Mopidy-Local structure
id = id.replace(/%2F/g, '/'); // Ensure all ':' are uri encoded to lowercase
id = id.replace(/%3A/g, '%3a');
var decoded = "".concat(source, ":");
switch (source) {
case 'dleyna':
// Doesn't contain URI type within it's URI schema *facepalm*
break;
default:
decoded += "".concat(type, ":");
}
decoded += "".concat(id);
return decoded;
};
/**
* Format our album objects into a universal format
*
@ -127052,7 +127107,9 @@ var injectSortId = function injectSortId(array) {
formatCategories: formatCategories,
collate: collate,
collateLibrary: collateLibrary,
injectSortId: injectSortId
injectSortId: injectSortId,
encodeUri: encodeUri,
decodeUri: decodeUri
});
/***/ }),
@ -127284,6 +127341,10 @@ var uriType = function uriType(uri) {
return exploded[1];
}
if (exploded[0] === 'dleyna') {
return 'track';
}
if (exploded[0] === 'tunein') {
switch (exploded[1]) {
case 'station':
@ -127399,6 +127460,10 @@ var getFromUri = function getFromUri(element, uri) {
return exploded[2];
}
if (exploded[0] === 'dleyna') {
return uri.replace('dleyna:', '');
}
break;
case 'userid':
@ -127462,14 +127527,16 @@ var getFromUri = function getFromUri(element, uri) {
var buildLink = function buildLink(uri) {
// Start the link with the URI type
var type = uriType(uri);
var link = "/".concat(type, "/"); // Encode the whole URI as though it's a component. This makes it URL friendly for
// all Mopidy backends (some use URIs like local:track:http://rss.com/stuff.mp3) which
// is never going to work nicely.
uri = encodeURIComponent(uri);
link += uri;
link += encodeURIComponent(uri);
console.log({
type: type,
link: link
});
return link;
};
/**
@ -131225,6 +131292,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _components_Button__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../components/Button */ "./src/js/components/Button.js");
/* harmony import */ var _components_Trackable__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ../components/Trackable */ "./src/js/components/Trackable.js");
/* harmony import */ var _util_selectors__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ../util/selectors */ "./src/js/util/selectors.js");
/* harmony import */ var _components_NiceNumber__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ../components/NiceNumber */ "./src/js/components/NiceNumber.js");
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@ -131276,6 +131344,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var Artist = /*#__PURE__*/function (_React$Component) {
@ -131739,7 +131808,7 @@ var Artist = /*#__PURE__*/function (_React$Component) {
"data-qa-file": "Artist"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_24__["I18n"], {
path: "specs.followers",
count: artist.followers.toLocaleString(),
count: Object(_components_NiceNumber__WEBPACK_IMPORTED_MODULE_28__["nice_number"])(artist.followers),
"data-qa-node": "I18n",
"data-qa-file": "Artist"
}))), artist.popularity && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
@ -131775,7 +131844,7 @@ var Artist = /*#__PURE__*/function (_React$Component) {
"data-qa-file": "Artist"
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_locale__WEBPACK_IMPORTED_MODULE_24__["I18n"], {
path: "specs.listeners",
count: artist.listeners.toLocaleString(),
count: Object(_components_NiceNumber__WEBPACK_IMPORTED_MODULE_28__["nice_number"])(artist.listeners),
"data-qa-node": "I18n",
"data-qa-file": "Artist"
})))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
@ -135884,6 +135953,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../locale */ "./src/js/locale/index.js");
/* harmony import */ var _components_Button__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../components/Button */ "./src/js/components/Button.js");
/* harmony import */ var _util_selectors__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../util/selectors */ "./src/js/util/selectors.js");
/* harmony import */ var _util_format__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../util/format */ "./src/js/util/format.js");
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@ -135931,6 +136001,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
var Track = /*#__PURE__*/function (_React$Component) {
@ -135962,7 +136033,7 @@ var Track = /*#__PURE__*/function (_React$Component) {
getTrack = _this$props.lastfmActions.getTrack;
if (prevUri !== uri) {
loadItem(uri);
loadItem(Object(_util_format__WEBPACK_IMPORTED_MODULE_24__["decodeUri"])(uri));
} // We have just received our full track or our track artists
@ -136334,7 +136405,7 @@ var Track = /*#__PURE__*/function (_React$Component) {
uri = _this$props7.uri,
track = _this$props7.track,
loadItem = _this$props7.coreActions.loadItem;
loadItem(uri);
loadItem(Object(_util_format__WEBPACK_IMPORTED_MODULE_24__["decodeUri"])(uri));
if (track) {
this.setWindowTitle(track);
@ -136344,33 +136415,9 @@ var Track = /*#__PURE__*/function (_React$Component) {
return Track;
}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);
/**
* Rebuild a track URI with some ugly-ass handling of encoding.
*
* Basically the ID part of a Mopidy URI needs to be encoded, but the rest of the URI can't be.
* This means we need to break down the URI (decoded) and then reconstruct with an encoded ID.
* This is all required because he URI is passed to us *from* a URL which has been encoded for
* obvious reasons.
*
* @param uri String
* @return String
* */
var rebuildUri = function rebuildUri(uri) {
var rebuilt_uri = "".concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_20__["uriSource"])(uri), ":").concat(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_20__["uriType"])(uri), ":"); // Escape unreserved characters (RFC 3986)
// https://stackoverflow.com/questions/18251399/why-doesnt-encodeuricomponent-encode-single-quotes-apostrophes
var id = Object(_util_helpers__WEBPACK_IMPORTED_MODULE_20__["getFromUri"])('trackid', uri);
id = encodeURIComponent(id).replace(/[!'()*]/g, escape); // Reinstate slashes for the Mopidy-Local structure
id = id.replace(/%2F/g, '/');
return rebuilt_uri + id;
};
var mapStateToProps = function mapStateToProps(state, ownProps) {
var uri = decodeURIComponent(ownProps.match.params.uri);
uri = rebuildUri(uri);
var uri = Object(_util_format__WEBPACK_IMPORTED_MODULE_24__["decodeUri"])(ownProps.match.params.uri);
var loadingSelector = Object(_util_selectors__WEBPACK_IMPORTED_MODULE_23__["makeLoadingSelector"])(["(.*)".concat(uri, "(.*)")]);
var trackSelector = Object(_util_selectors__WEBPACK_IMPORTED_MODULE_23__["makeItemSelector"])(uri);
return {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -106,7 +106,7 @@
// Release details
// These are automatically injected to built HTML
var build = "1608449869";
var build = "1609658292";
var version = "3.55.5";
// Construct the script tag

View File

@ -1,8 +1,7 @@
import React from 'react';
import LinksSentence from './LinksSentence';
import { Dater, dater } from './Dater';
import { dater } from './Dater';
import { nice_number } from './NiceNumber';
import URILink from './URILink';
import ContextMenuTrigger from './ContextMenuTrigger';
import Icon from './Icon';
@ -13,7 +12,6 @@ import {
scrollTo,
} from '../util/helpers';
import { I18n } from '../locale';
import { arrayOf } from '../util/arrays';
export default class ListItem extends React.Component {
componentDidMount() {
@ -102,7 +100,7 @@ export default class ListItem extends React.Component {
tracks_total: total,
} = item;
if (!total && !value) return null;
return <I18n path="specs.tracks" count={total || value.length} />;
return <I18n path="specs.tracks" count={nice_number(total || value.length)} />;
}
if (key === 'artists') {
const {
@ -111,7 +109,7 @@ export default class ListItem extends React.Component {
} = item;
if (Array.isArray(value)) return <LinksSentence items={value} />;
if (!total && !array) return null;
return <I18n path="specs.artists" count={total || array.length} />;
return <I18n path="specs.artists" count={nice_number(total || array.length)} />;
}
if (key === 'albums') {
const {
@ -126,10 +124,10 @@ export default class ListItem extends React.Component {
if (!value) return null;
if (key === 'followers') {
return <I18n path="specs.followers" count={value.toLocaleString()} />;
return <I18n path="specs.followers" count={nice_number(value)} />;
}
if (key === 'listeners') {
return <I18n path="specs.listeners" count={value.toLocaleString()} />;
return <I18n path="specs.listeners" count={nice_number(value)} />;
}
if (key === 'added_at') {
return <I18n path="specs.added_ago" time={dater('ago', value)} />;

View File

@ -1,8 +1,7 @@
import React, { memo } from 'react';
import { memo } from 'react';
const nice_number = (value) => {
let formatted = parseInt(value);
let formatted = parseInt(value, 10);
// > 1 million
if (formatted > 1000000) {

View File

@ -1,8 +1,5 @@
import React from 'react';
import {
isCached,
} from '../util/storage';
import { isCached } from '../util/storage';
export default class Parallax extends React.Component {
constructor(props) {
@ -11,13 +8,14 @@ export default class Parallax extends React.Component {
this.state = {
loaded: false,
url: null,
outgoingUrl: null,
};
}
componentDidMount() {
if (this.props.image) {
this.loadImage(this.props.image);
const { image } = this.props;
if (image) {
this.loadImage(image);
}
}
@ -38,7 +36,7 @@ export default class Parallax extends React.Component {
const imageObject = new Image();
imageObject.src = url;
imageObject.onload = function () {
imageObject.onload = () => {
self.setState({
loaded: true,
url,
@ -58,7 +56,6 @@ export default class Parallax extends React.Component {
const {
blur,
fixedHeight,
image,
animate = true,
} = this.props;
const {

View File

@ -154,6 +154,7 @@ const pusherPersistConfig = {
key: 'pusher',
storage: localForage,
blacklist: [
'connected',
'connections',
],
debug: window.test_mode,

View File

@ -3,6 +3,8 @@ import {
isObject,
upgradeSpotifyPlaylistUri,
uriSource,
uriType,
getFromUri,
} from './helpers';
/**
@ -273,6 +275,58 @@ const formatSimpleObjects = function (records = []) {
return formatted;
};
/**
* Prepare a URI for use in a URL
*
* Needs to have all special characters encoded to avoid being parsed incorrectly, especially
* '/' as this is a URL parameter delimiter
* @param {String} uri
*/
const encodeUri = (uri) => {
return encodeURIComponent(uri);
};
/**
* Rebuild a URI with some ugly-ass handling of encoding.
*
* Basically the ID part of a Mopidy URI needs to be encoded, but the rest of the URI can't be.
* This means we need to break down the URI (decoded) and then reconstruct with an encoded ID.
* This is all required because he URI is passed to us *from* a URL which has been encoded for
* obvious reasons.
*
* For example somebackend:track:https://youtube.com/1234 would cause issues with the
* https:// section. Our explode-by-':' approach would break this type of URI.
*
* @param {String} rawUri
*/
const decodeUri = (rawUri) => {
const uri = decodeURIComponent(rawUri);
const source = uriSource(uri);
const type = uriType(uri);
// Escape unreserved characters (RFC 3986)
// https://stackoverflow.com/questions/18251399/why-doesnt-encodeuricomponent-encode-single-quotes-apostrophes
let id = getFromUri(`${type}id`, uri);
id = encodeURIComponent(id).replace(/[!'()*]/g, escape);
// Reinstate slashes for the Mopidy-Local structure
id = id.replace(/%2F/g, '/');
// Ensure all ':' are uri encoded to lowercase
id = id.replace(/%3A/g, '%3a');
let decoded = `${source}:`;
switch (source) {
case 'dleyna':
// Doesn't contain URI type within it's URI schema *facepalm*
break;
default:
decoded += `${type}:`;
}
decoded += `${id}`;
return decoded;
};
/**
* Format our album objects into a universal format
@ -969,6 +1023,8 @@ export {
collate,
collateLibrary,
injectSortId,
encodeUri,
decodeUri,
};
export default {
@ -995,4 +1051,6 @@ export default {
collate,
collateLibrary,
injectSortId,
encodeUri,
decodeUri,
};

View File

@ -163,6 +163,10 @@ const uriType = function (uri) {
return exploded[1];
}
if (exploded[0] === 'dleyna') {
return 'track';
}
if (exploded[0] === 'tunein') {
switch (exploded[1]) {
case 'station':
@ -191,7 +195,6 @@ const uriType = function (uri) {
}
};
const sourceIcon = function (uri, source = null) {
if (uri) source = uriSource(uri);
switch (source) {
@ -219,19 +222,18 @@ const sourceIcon = function (uri, source = null) {
}
};
/**
* Get an element from a URI
* @param element = string, the element we wish to extract
* @param uri = string
* */
const getFromUri = function (element, uri) {
const getFromUri = (element, uri) => {
if (!uri) return null;
const exploded = `${uri}`.split(':');
switch (element) {
case 'mbid':
var index = exploded.indexOf('mbid');
const index = exploded.indexOf('mbid');
if (index > -1) return exploded[index + 1];
break;
@ -266,6 +268,9 @@ const getFromUri = function (element, uri) {
if (exploded[1] == 'track') {
return exploded[2];
}
if (exploded[0] === 'dleyna') {
return uri.replace('dleyna:', '');
}
break;
case 'userid':
@ -312,7 +317,6 @@ const getFromUri = function (element, uri) {
}
};
/**
* Build a link to an asset. Using the URI type we can ascertain where we need
* to direct the user (eg /track/local:track:1235.mp3)
@ -320,17 +324,15 @@ const getFromUri = function (element, uri) {
* @param $uri = String
* @return String
* */
const buildLink = function (uri) {
// Start the link with the URI type
const buildLink = (uri) => {
const type = uriType(uri);
let link = `/${type}/`;
// Encode the whole URI as though it's a component. This makes it URL friendly for
// all Mopidy backends (some use URIs like local:track:http://rss.com/stuff.mp3) which
// is never going to work nicely.
uri = encodeURIComponent(uri);
link += uri;
link += encodeURIComponent(uri);
console.log({ type, link });
return link;
};

View File

@ -34,6 +34,7 @@ import {
makeItemSelector,
makeLoadingSelector,
} from '../util/selectors';
import { nice_number } from '../components/NiceNumber';
class Artist extends React.Component {
constructor(props) {
@ -427,7 +428,7 @@ class Artist extends React.Component {
<div className="tile">
<span className="content">
<Icon type="fontawesome" name="users" />
<I18n path="specs.followers" count={artist.followers.toLocaleString()} />
<I18n path="specs.followers" count={nice_number(artist.followers)} />
</span>
</div>
)}
@ -443,7 +444,7 @@ class Artist extends React.Component {
<div className="tile">
<span className="content">
<Icon type="fontawesome" name="headphones" />
<I18n path="specs.listeners" count={artist.listeners.toLocaleString()} />
<I18n path="specs.listeners" count={nice_number(artist.listeners)} />
</span>
</div>
)}

View File

@ -1,4 +1,3 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
@ -21,14 +20,12 @@ import * as lastfmActions from '../services/lastfm/actions';
import * as geniusActions from '../services/genius/actions';
import {
isLoading,
getFromUri,
sourceIcon,
uriSource,
uriType,
} from '../util/helpers';
import { i18n, I18n } from '../locale';
import Button from '../components/Button';
import { makeLoadingSelector, makeItemSelector } from '../util/selectors';
import { decodeUri } from '../util/format';
class Track extends React.Component {
componentDidMount() {
@ -40,7 +37,7 @@ class Track extends React.Component {
},
} = this.props;
loadItem(uri);
loadItem(decodeUri(uri));
if (track) {
this.setWindowTitle(track);
@ -68,7 +65,7 @@ class Track extends React.Component {
} = this.props;
if (prevUri !== uri) {
loadItem(uri);
loadItem(decodeUri(uri));
}
// We have just received our full track or our track artists
@ -312,34 +309,8 @@ class Track extends React.Component {
}
}
/**
* Rebuild a track URI with some ugly-ass handling of encoding.
*
* Basically the ID part of a Mopidy URI needs to be encoded, but the rest of the URI can't be.
* This means we need to break down the URI (decoded) and then reconstruct with an encoded ID.
* This is all required because he URI is passed to us *from* a URL which has been encoded for
* obvious reasons.
*
* @param uri String
* @return String
* */
const rebuildUri = (uri) => {
const rebuilt_uri = `${uriSource(uri)}:${uriType(uri)}:`;
// Escape unreserved characters (RFC 3986)
// https://stackoverflow.com/questions/18251399/why-doesnt-encodeuricomponent-encode-single-quotes-apostrophes
let id = getFromUri('trackid', uri);
id = encodeURIComponent(id).replace(/[!'()*]/g, escape);
// Reinstate slashes for the Mopidy-Local structure
id = id.replace(/%2F/g, '/');
return rebuilt_uri + id;
};
const mapStateToProps = (state, ownProps) => {
let uri = decodeURIComponent(ownProps.match.params.uri);
uri = rebuildUri(uri);
const uri = decodeUri(ownProps.match.params.uri);
const loadingSelector = makeLoadingSelector([`(.*)${uri}(.*)`]);
const trackSelector = makeItemSelector(uri);