From f26154d0ac79a5019bc3a1f2ad7a1a3c19d52c4e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 19 Oct 2015 14:14:34 +0100 Subject: [PATCH] Add support for m.room.avatar: refactor avatar URLs BREAKING CHANGE. Scope each "getAvatarUrl" to be instance methods on the entity it relates to (Room and RoomMember respectively). By doing this, we can actually pull out specific state such as the `m.room.avatar` event more easily rather than keeping it in the global cesspit of `MatrixClient`. This was complicated by `getHttpUriForMxc` and `getIdenticonUri` which were attached to the HTTP API to pull out the `baseUrl` when crafting the URL. Pull out this dependency out and explicitly pass it in when crafting the URL. This is trivial to get from `MatrixClient.getHomeserverUrl()`. --- lib/client.js | 66 ++------------------------------- lib/content-repo.js | 77 +++++++++++++++++++++++++++++++++++++++ lib/http-api.js | 75 -------------------------------------- lib/models/room-member.js | 33 +++++++++++++++++ lib/models/room.js | 33 +++++++++++++++++ 5 files changed, 147 insertions(+), 137 deletions(-) create mode 100644 lib/content-repo.js diff --git a/lib/client.js b/lib/client.js index fa3e67846..86b525c1b 100644 --- a/lib/client.js +++ b/lib/client.js @@ -17,6 +17,7 @@ var Room = require("./models/room"); var User = require("./models/user"); var webRtcCall = require("./webrtc/call"); var utils = require("./utils"); +var contentRepo = require("./content-repo"); var CRYPTO_ENABLED = false; @@ -1403,67 +1404,6 @@ MatrixClient.prototype.setAvatarUrl = function(url, callback) { ); }; -/** - * Get the avatar URL for a room member. This method is experimental and - * may change. - * @param {module:room-member.RoomMember} member - * @param {Number} width The desired width of the thumbnail. - * @param {Number} height The desired height of the thumbnail. - * @param {string} resizeMethod The thumbnail resize method to use, either - * "crop" or "scale". - * @param {Boolean} allowDefault (optional) Passing false causes this method to - * return null if the user has no avatar image. Otherwise, a default image URL - * will be returned. - * @return {?string} the avatar URL or null. - */ -MatrixClient.prototype.getAvatarUrlForMember = - function(member, width, height, resizeMethod, allowDefault) { - if (!member || !member.events.member) { - return null; - } - if (allowDefault === undefined) { allowDefault = true; } - var rawUrl = member.events.member.getContent().avatar_url; - if (rawUrl) { - return this._http.getHttpUriForMxc(rawUrl, width, height, resizeMethod); - } else if (allowDefault) { - return this._http.getIdenticonUri(member.userId, width, height); - } - return null; -}; - -/** - * Get the avatar URL for a room. This method is experimental and - * may change. - * @param {module:room.Room} room - * @param {Number} width The desired width of the thumbnail. - * @param {Number} height The desired height of the thumbnail. - * @param {string} resizeMethod The thumbnail resize method to use, either - * "crop" or "scale". - * @param {Boolean} allowDefault (optional) Passing false causes this method to - * return null if the user has no avatar image. Otherwise, a default image URL - * will be returned. - * @return {?string} the avatar URL or null. - */ -MatrixClient.prototype.getAvatarUrlForRoom = - function(room, width, height, resizeMethod, allowDefault) { - - if (!room || !room.currentState || !room.currentState.members) { - return null; - } - - var userId = this.credentials.userId; - var members = utils.filter(room.currentState.getMembers(), function(m) { - return (m.membership === "join" && m.userId !== userId); - }); - - if (members[0]) { - return this.getAvatarUrlForMember( - members[0], width, height, resizeMethod, allowDefault - ); - } - return null; -}; - /** * Turn an MXC URL into an HTTP one. This method is experimental and * may change. @@ -1476,7 +1416,9 @@ MatrixClient.prototype.getAvatarUrlForRoom = */ MatrixClient.prototype.mxcUrlToHttp = function(mxcUrl, width, height, resizeMethod) { - return this._http.getHttpUriForMxc(mxcUrl, width, height, resizeMethod); + return contentRepo.getHttpUriForMxc( + this.baseUrl, mxcUrl, width, height, resizeMethod + ); }; /** diff --git a/lib/content-repo.js b/lib/content-repo.js new file mode 100644 index 000000000..480f940c0 --- /dev/null +++ b/lib/content-repo.js @@ -0,0 +1,77 @@ +var utils = require("./utils"); + +module.exports = { + /** + * Get the HTTP URL for an MXC URI. + * @param {string} baseUrl The base homeserver url which has a content repo. + * @param {string} mxc The mxc:// URI. + * @param {Number} width The desired width of the thumbnail. + * @param {Number} height The desired height of the thumbnail. + * @param {string} resizeMethod The thumbnail resize method to use, either + * "crop" or "scale". + * @return {string} The complete URL to the content. + */ + getHttpUriForMxc: function(baseUrl, mxc, width, height, resizeMethod) { + if (typeof mxc !== "string" || !mxc) { + return mxc; + } + if (mxc.indexOf("mxc://") !== 0) { + return mxc; + } + var serverAndMediaId = mxc.slice(6); // strips mxc:// + var prefix = "/_matrix/media/v1/download/"; + var params = {}; + + if (width) { + params.width = width; + } + if (height) { + params.height = height; + } + if (resizeMethod) { + params.method = resizeMethod; + } + if (utils.keys(params).length > 0) { + // these are thumbnailing params so they probably want the + // thumbnailing API... + prefix = "/_matrix/media/v1/thumbnail/"; + } + + var fragmentOffset = serverAndMediaId.indexOf("#"), + fragment = ""; + if (fragmentOffset >= 0) { + fragment = serverAndMediaId.substr(fragmentOffset); + serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset); + } + return baseUrl + prefix + serverAndMediaId + + (utils.keys(params).length === 0 ? "" : + ("?" + utils.encodeParams(params))) + fragment; + }, + + /** + * Get an identicon URL from an arbitrary string. + * @param {string} baseUrl The base homeserver url which has a content repo. + * @param {string} identiconString The string to create an identicon for. + * @param {Number} width The desired width of the image in pixels. + * @param {Number} height The desired height of the image in pixels. + * @return {string} The complete URL to the identicon. + */ + getIdenticonUri: function(baseUrl, identiconString, width, height) { + if (!identiconString) { + return; + } + if (!width) { width = 96; } + if (!height) { height = 96; } + var params = { + width: width, + height: height + }; + + var path = utils.encodeUri("/_matrix/media/v1/identicon/$ident", { + $ident: identiconString + }); + return baseUrl + path + + (utils.keys(params).length === 0 ? "" : + ("?" + utils.encodeParams(params))); + } +}; diff --git a/lib/http-api.js b/lib/http-api.js index d771471f7..e192dfa91 100644 --- a/lib/http-api.js +++ b/lib/http-api.js @@ -54,81 +54,6 @@ module.exports.MatrixHttpApi = function MatrixHttpApi(opts) { module.exports.MatrixHttpApi.prototype = { - // URI functions - // ============= - - /** - * Get the HTTP URL for an MXC URI. - * @param {string} mxc The mxc:// URI. - * @param {Number} width The desired width of the thumbnail. - * @param {Number} height The desired height of the thumbnail. - * @param {string} resizeMethod The thumbnail resize method to use, either - * "crop" or "scale". - * @return {string} The complete URL to the content. - */ - getHttpUriForMxc: function(mxc, width, height, resizeMethod) { - if (typeof mxc !== "string" || !mxc) { - return mxc; - } - if (mxc.indexOf("mxc://") !== 0) { - return mxc; - } - var serverAndMediaId = mxc.slice(6); // strips mxc:// - var prefix = "/_matrix/media/v1/download/"; - var params = {}; - - if (width) { - params.width = width; - } - if (height) { - params.height = height; - } - if (resizeMethod) { - params.method = resizeMethod; - } - if (utils.keys(params).length > 0) { - // these are thumbnailing params so they probably want the - // thumbnailing API... - prefix = "/_matrix/media/v1/thumbnail/"; - } - - var fragmentOffset = serverAndMediaId.indexOf("#"), - fragment = ""; - if (fragmentOffset >= 0) { - fragment = serverAndMediaId.substr(fragmentOffset); - serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset); - } - return this.opts.baseUrl + prefix + serverAndMediaId + - (utils.keys(params).length === 0 ? "" : - ("?" + utils.encodeParams(params))) + fragment; - }, - - /** - * Get an identicon URL from an arbitrary string. - * @param {string} identiconString The string to create an identicon for. - * @param {Number} width The desired width of the image in pixels. - * @param {Number} height The desired height of the image in pixels. - * @return {string} The complete URL to the identicon. - */ - getIdenticonUri: function(identiconString, width, height) { - if (!identiconString) { - return; - } - if (!width) { width = 96; } - if (!height) { height = 96; } - var params = { - width: width, - height: height - }; - - var path = utils.encodeUri("/_matrix/media/v1/identicon/$ident", { - $ident: identiconString - }); - return this.opts.baseUrl + path + - (utils.keys(params).length === 0 ? "" : - ("?" + utils.encodeParams(params))); - }, - /** * Get the content repository url with query parameters. * @return {Object} An object with a 'base', 'path' and 'params' for base URL, diff --git a/lib/models/room-member.js b/lib/models/room-member.js index 3fcdbca36..b836fc2be 100644 --- a/lib/models/room-member.js +++ b/lib/models/room-member.js @@ -3,6 +3,7 @@ * @module models/room-member */ var EventEmitter = require("events").EventEmitter; +var contentRepo = require("../content-repo"); var utils = require("../utils"); @@ -147,6 +148,38 @@ RoomMember.prototype.getLastModifiedTime = function() { return this._modified; }; +/** + * Get the avatar URL for a room member. + * @param {string} baseUrl The base homeserver URL + * @param {Number} width The desired width of the thumbnail. + * @param {Number} height The desired height of the thumbnail. + * @param {string} resizeMethod The thumbnail resize method to use, either + * "crop" or "scale". + * @param {Boolean} allowDefault (optional) Passing false causes this method to + * return null if the user has no avatar image. Otherwise, a default image URL + * will be returned. Default: true. + * @return {?string} the avatar URL or null. + */ +RoomMember.prototype.getAvatarUrl = + function(baseUrl, width, height, resizeMethod, allowDefault) { + if (!this.events.member) { + return null; + } + if (allowDefault === undefined) { allowDefault = true; } + var rawUrl = this.events.member.getContent().avatar_url; + if (rawUrl) { + return contentRepo.getHttpUriForMxc( + baseUrl, rawUrl, width, height, resizeMethod + ); + } + else if (allowDefault) { + return contentRepo.getIdenticonUri( + baseUrl, member.userId, width, height + ); + } + return null; +}; + function calculateDisplayName(member, event, roomState) { var displayName = event.getDirectionalContent().displayname; var selfUserId = member.userId; diff --git a/lib/models/room.js b/lib/models/room.js index 3e398c50f..475d5e365 100644 --- a/lib/models/room.js +++ b/lib/models/room.js @@ -7,6 +7,7 @@ var EventEmitter = require("events").EventEmitter; var RoomState = require("./room-state"); var RoomSummary = require("./room-summary"); var utils = require("../utils"); +var contentRepo = require("../content-repo"); /** * Construct a new Room. @@ -39,6 +40,38 @@ function Room(roomId, storageToken) { } utils.inherits(Room, EventEmitter); +/** + * Get the avatar URL for a room if one was set. + * @param {String} baseUrl The homeserver base URL. See {@link MatrixClient#getHomeserverUrl()}. + * @param {Number} width The desired width of the thumbnail. + * @param {Number} height The desired height of the thumbnail. + * @param {string} resizeMethod The thumbnail resize method to use, either + * "crop" or "scale". + * @param {boolean} allowDefault True to allow an identicon for this room if an + * avatar URL wasn't explicitly set. Default: true. + * @return {?string} the avatar URL or null. + */ +Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod, allowDefault) { + var roomAvatarEvent = this.currentState.getStateEvents("m.room.avatar", ""); + if (!roomAvatarEvent) { + return null; + } + if (allowDefault === undefined) { allowDefault = true; } + var mainUrl = roomAvatarEvent.getContent().url; + if (mainUrl) { + return contentRepo.getHttpUriForMxc( + baseUrl, mainUrl, width, height, resizeMethod + ); + } + else if (allowDefault) { + return contentRepo.getIdenticonUri( + baseUrl, member.userId, width, height + ); + } + + return null; +}; + /** * Get a member from the current room state. * @param {string} userId The user ID of the member.