Initial commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
var carto = require('internal-carto.js');
|
||||
var _ = require('underscore');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var createDefaultFallbackMap = require('dashboard/components/create-fallback-map');
|
||||
|
||||
/**
|
||||
* View to render the "favourite" map, either a user's map visualization, or a default fallback map.
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
render: function () {
|
||||
this.$el.removeClass('is-pre-loading').addClass('is-loading');
|
||||
|
||||
var promise;
|
||||
if (this.options.createVis) {
|
||||
promise = this._createVisMap(this.options.createVis);
|
||||
} else {
|
||||
promise = this._createFallbackMap();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
promise.done(function () {
|
||||
self.$el.removeClass('is-loading');
|
||||
self.$('.js-spinner').remove();
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_createVisMap: function (createVis) {
|
||||
return carto.createVis(this.el, createVis.url || createVis.vizJson, _.defaults(createVis.opts, {
|
||||
authToken: 'default_public',
|
||||
title: false,
|
||||
header: false,
|
||||
description: false,
|
||||
searchControl: false,
|
||||
layer_selector: false,
|
||||
text: false,
|
||||
image: false,
|
||||
zoomControl: false,
|
||||
logo: false,
|
||||
scrollwheel: false,
|
||||
mobile_layout: true,
|
||||
legends: false,
|
||||
loader: false,
|
||||
fullscreen: false,
|
||||
no_cdn: false
|
||||
}));
|
||||
},
|
||||
|
||||
_createFallbackMap: function () {
|
||||
createDefaultFallbackMap({
|
||||
el: this.el,
|
||||
basemap: this.options.fallbackBaselayer
|
||||
});
|
||||
|
||||
// Fake promise, to keep the render method consistent with how the vis map would have been handled (async)
|
||||
return {
|
||||
done: function (fn) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,304 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var moment = require('moment');
|
||||
|
||||
var Utils = require('builder/helpers/utils');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var randomQuote = require('builder/components/loading/random-quote');
|
||||
var Visualizations = require('./user-feed/feed-collection');
|
||||
var ViewFactory = require('builder/components/view-factory');
|
||||
var MapCardPreview = require('dashboard/components/mapcard-preview-view');
|
||||
|
||||
var feedTemplate = require('./user-feed/feed.tpl');
|
||||
var mapTemplate = require('./user-feed/map-item.tpl');
|
||||
var datasetTemplate = require('./user-feed/dataset-item.tpl');
|
||||
var loaderTemplatePath = require('./user-feed/loading.tpl');
|
||||
var emptyTemplatePath = require('./user-feed/empty.tpl');
|
||||
var errorTemplatePath = require('./user-feed/error.tpl');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'config',
|
||||
'authenticatedUser'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'div',
|
||||
|
||||
_PAGE: 1,
|
||||
_ITEMS_PER_PAGE: 8,
|
||||
|
||||
_SMALL_WIDTH: 544,
|
||||
|
||||
_CARD_HEIGHT: 170,
|
||||
_LOADER_TITLE: 'Loading visualizations',
|
||||
|
||||
events: {
|
||||
'click .js-more': '_onClickMore'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
_.bindAll(this, '_onFetchError', '_onWindowResize', '_renderStaticMaps');
|
||||
|
||||
this.maps = [];
|
||||
|
||||
this._initModels();
|
||||
this._initBindings();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(feedTemplate());
|
||||
this._renderLoader();
|
||||
this._fetchItems({ page: this._PAGE });
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_onWindowResize: function () {
|
||||
var width = $(window).width();
|
||||
|
||||
if (width <= this._SMALL_WIDTH) {
|
||||
this.model.set('size', 'small');
|
||||
} else {
|
||||
this.model.set('size', 'big');
|
||||
}
|
||||
|
||||
this._renderStaticMaps();
|
||||
},
|
||||
|
||||
_initModels: function () {
|
||||
var size = 'big';
|
||||
var wWidth = $(window).width();
|
||||
|
||||
if (wWidth <= this._SMALL_WIDTH) {
|
||||
size = 'small';
|
||||
}
|
||||
|
||||
this.model = new Backbone.Model({
|
||||
vis_count: 0,
|
||||
type: '',
|
||||
size: size,
|
||||
page: 0,
|
||||
order_by: 'updated_at'
|
||||
});
|
||||
|
||||
this.collection = new Visualizations([], {
|
||||
config: this._config
|
||||
});
|
||||
this.collection.on('reset', this._renderVisualizations, this);
|
||||
},
|
||||
|
||||
_initBindings: function () {
|
||||
this.model.on('change:show_more', this._onChangeShowMore, this);
|
||||
this.model.on('change:show_loader', this._onChangeShowLoader, this);
|
||||
this.model.on('change:show_mast', this._onChangeShowMast, this);
|
||||
this.model.on('change:vis_count', this._onChangeVisCount, this);
|
||||
|
||||
$(window).on('resize', this._onWindowResize);
|
||||
},
|
||||
|
||||
_onChangeVisCount: function () {
|
||||
if (this.model.get('vis_count') >= this.collection.total_entries) {
|
||||
this.model.set({ show_more: false });
|
||||
} else {
|
||||
this.model.set({ show_more: true });
|
||||
}
|
||||
},
|
||||
|
||||
_onChangeShowMast: function () {
|
||||
if (this.model.get('show_mast')) {
|
||||
this.$('.js-mast').removeClass('is-hidden');
|
||||
} else {
|
||||
this.$('.js-mast').addClass('is-hidden');
|
||||
}
|
||||
},
|
||||
|
||||
_onChangeShowLoader: function () {
|
||||
if (this.model.get('show_loader')) {
|
||||
this.loader.show();
|
||||
} else {
|
||||
this.loader.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onChangeShowMore: function () {
|
||||
this.$('.js-more').toggleClass('is-hidden', !this.model.get('show_more'));
|
||||
},
|
||||
|
||||
_renderLoader: function () {
|
||||
this.loader = ViewFactory.createByTemplate(loaderTemplatePath, {
|
||||
title: this._LOADER_TITLE,
|
||||
quote: randomQuote()
|
||||
});
|
||||
|
||||
this.$el.append(this.loader.render().$el);
|
||||
},
|
||||
|
||||
_renderError: function () {
|
||||
this.error = ViewFactory.createByTemplate(errorTemplatePath, {
|
||||
});
|
||||
|
||||
this.$el.append(this.error.render().$el);
|
||||
},
|
||||
|
||||
_renderEmpty: function () {
|
||||
this.empty = ViewFactory.createByTemplate(emptyTemplatePath, {
|
||||
name: this._config.user_name
|
||||
});
|
||||
|
||||
this.$el.append(this.empty.render().$el);
|
||||
},
|
||||
|
||||
_getDatasetSize: function (item) {
|
||||
var size = item.get('table').size;
|
||||
return size ? Utils.readablizeBytes(size, true).split(' ') : 0;
|
||||
},
|
||||
|
||||
_getGeometryType: function (geomTypes) {
|
||||
if (geomTypes && geomTypes.length > 0) {
|
||||
var types = ['point', 'polygon', 'line', 'raster'];
|
||||
var geomType = geomTypes[0];
|
||||
|
||||
return _.find(types, function (type) {
|
||||
return geomType.toLowerCase().indexOf(type) !== -1;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_renderVisualizations: function () {
|
||||
if (this.collection.length === 0) {
|
||||
this.model.set({ show_loader: false });
|
||||
this._renderEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.set({
|
||||
vis_count: this.model.get('vis_count') + this.collection.length,
|
||||
show_loader: false,
|
||||
show_more: true,
|
||||
show_filters: true,
|
||||
show_mast: true
|
||||
});
|
||||
|
||||
this.collection.each(function (item) {
|
||||
var template = item.get('type') === 'derived' ? mapTemplate : datasetTemplate;
|
||||
var geomType = (item.get('table') && item.get('table').geometry_types) ? this._getGeometryType(item.get('table').geometry_types) : null;
|
||||
|
||||
var owner = item.get('permission').owner;
|
||||
|
||||
var el = template({
|
||||
vis: item.attributes,
|
||||
datasetSize: this._getDatasetSize(item),
|
||||
username: owner.username,
|
||||
avatar_url: owner.avatar_url,
|
||||
table_count: owner.table_count,
|
||||
maps_count: owner.maps_count,
|
||||
geomType: geomType,
|
||||
account_host: this._config.get('account_host'),
|
||||
base_url: this._config.get('base_url'),
|
||||
updated: moment(item.get('updated_at')).fromNow(),
|
||||
showLikeButton: this._userLoggedIn()
|
||||
});
|
||||
|
||||
this.$('.js-items').append(el);
|
||||
|
||||
var $item = this.$('.js-items').find('[data-vis-id="' + item.get('id') + '"]');
|
||||
|
||||
if (item.get('type') === 'derived') {
|
||||
this.maps.push(item);
|
||||
}
|
||||
|
||||
if (item.get('type') === 'derived' && !item.get('rendered_' + this.model.get('size'))) {
|
||||
this._renderStaticMap(item, $item);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
_renderStaticMaps: function () {
|
||||
_.each(this.maps, function (item) {
|
||||
if (!item.get('rendered_' + this.model.get('size'))) {
|
||||
var $item = this.$('.js-items').find('[data-vis-id="' + item.get('id') + '"]');
|
||||
this._renderStaticMap(item, $item);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
_renderStaticMap: function (vis, $el) {
|
||||
var visId = vis.get('id');
|
||||
var username = vis.get('permission').owner.username;
|
||||
var width = 540;
|
||||
var className = 'is-' + this.model.get('size');
|
||||
|
||||
if (this.model.get('size') === 'small') {
|
||||
width = 288;
|
||||
}
|
||||
|
||||
if (visId && username) {
|
||||
vis.set('rendered_' + this.model.get('size'), true);
|
||||
|
||||
new MapCardPreview({
|
||||
el: $el.find('.js-header'),
|
||||
width: width,
|
||||
height: this._CARD_HEIGHT,
|
||||
mapsApiResource: this._config.getMapsResourceName(username),
|
||||
username: username,
|
||||
visId: visId,
|
||||
className: className,
|
||||
config: this._config
|
||||
}).load();
|
||||
}
|
||||
},
|
||||
|
||||
_getLikesEndpoint: function (vis) {
|
||||
return '/api/v1/viz/' + vis.get('id') + '/like';
|
||||
},
|
||||
|
||||
_userLoggedIn: function () {
|
||||
return this._authenticatedUser && !!this._authenticatedUser.get('username');
|
||||
},
|
||||
|
||||
_fetchLike: function (model, url) {
|
||||
if (this._authenticatedUser.get('username')) {
|
||||
// no more loadModelCompleted event
|
||||
model.once('change', function () {
|
||||
model.set('likeable', true);
|
||||
});
|
||||
model.url = url;
|
||||
model.sync = Backbone.withCORS;
|
||||
model.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
_fetchItems: function (params) {
|
||||
var data = _.extend({
|
||||
types: 'table,derived',
|
||||
privacy: 'public',
|
||||
only_published: 'true',
|
||||
exclude_shared: 'true',
|
||||
per_page: this._ITEMS_PER_PAGE,
|
||||
order: 'updated_at'
|
||||
}, params);
|
||||
|
||||
this.collection.fetch({
|
||||
data: data,
|
||||
error: this._onFetchError,
|
||||
reset: true
|
||||
});
|
||||
},
|
||||
|
||||
_onFetchError: function () {
|
||||
this.model.set({ show_loader: false });
|
||||
this._renderError();
|
||||
},
|
||||
|
||||
_onClickMore: function (e) {
|
||||
this.killEvent(e);
|
||||
this.model.set({ show_more: false, show_loader: true });
|
||||
this._fetchItems({ page: ++this._PAGE });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
<li class="FeedItem">
|
||||
<a href="http://<%- username %>.<%- account_host %>" class="FeedItem-avatar u-hideOnTablet">
|
||||
<img src="<%- avatar_url %>" class="UserAvatar-img UserAvatar-img--big" />
|
||||
</a>
|
||||
|
||||
<div class="DatasetCard DatasetCard--long" data-vis-id="<%- vis.id %>">
|
||||
<div class="DatasetCard-content">
|
||||
<div class="DatasetCard-contentBodyRow DatasetCard-contentBodyRow--flex">
|
||||
<div class="DatasetCard-contentBodyDetails--left">
|
||||
<div class="DatasetsList-itemCategory is--<%- geomType %>Dataset"></div>
|
||||
</div>
|
||||
<div class="DatasetCard-contentBodyDetails--right u-ellipsLongText">
|
||||
<h3 class="DatasetCard-title DefaultTitle CDB-Text is-semibold">
|
||||
<a href="<%- base_url %>/tables/<%- vis.name %>/public" class="DefaultTitle-link u-ellipsLongText" title="<%- vis.name %>"><%- vis.name %></a>
|
||||
</h3>
|
||||
|
||||
<div class="DatasetCard-contentFooter u-altTextColor CDB-Size-medium">
|
||||
<div class="MapCard-contentFooterDetails--left">
|
||||
<% if (datasetSize && datasetSize[0] > 0) { %>
|
||||
<div class="MapCard-contentFooterIcon u-hideOnMobile">
|
||||
<i class="CDB-IconFont CDB-IconFont-floppy SizeIndicator-icon"></i>
|
||||
<span class="MapCardIcon-counter"><%- datasetSize[0] %></span> <span class="MapCardIcon-label"><%- datasetSize[1] %></span>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="MapCard-contentFooterDetails--right">
|
||||
<div class="MapCard-contentFooterTimeDiff DefaultTimeDiff">
|
||||
<i class="CDB-IconFont CDB-IconFont-clock DefaultTimeDiff-icon"></i>
|
||||
<%- updated %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="IntermediateInfo IntermediateInfo--flex">
|
||||
<div class="LayoutIcon">
|
||||
<i class="CDB-IconFont CDB-IconFont-map"></i>
|
||||
</div>
|
||||
<h3 class="CDB-Text CDB-Size-large u-mainTextColor u-secondaryTextColor u-tSpace--m"><%- name %> doesn't have any public data yet</h3>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="IntermediateInfo IntermediateInfo--flex">
|
||||
<div class="LayoutIcon LayoutIcon--negative">
|
||||
<i class="CDB-IconFont CDB-IconFont-cockroach"></i>
|
||||
</div>
|
||||
<h3 class="CDB-Text CDB-Size-large u-mainTextColor u-secondaryTextColor u-bSpace--m">We couldn't retrieve the maps :(</h3>
|
||||
<p class="CDB-Text CDB-Size-medium u-altTextColor">Reload the page again. If the problem persists contact us at <a class="js-mail-link" href="mailto:support@carto.com">support@carto.com</a>.</p>
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url: '/api/v1/viz',
|
||||
|
||||
parse: function (models) {
|
||||
this.total_entries = models.total_entries;
|
||||
return models.visualizations;
|
||||
},
|
||||
|
||||
initialize: function (models, options) {
|
||||
if (!options.config) {
|
||||
throw new Error('config model is required for feed collection');
|
||||
}
|
||||
|
||||
this._config = options.config;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="Profile-content Feed-content">
|
||||
<div class="FeedMast is-hidden js-mast">
|
||||
<div class="FeedMast-ball FeedMast-ball--top"></div>
|
||||
<div class="FeedMast-ball FeedMast-ball--bottom"></div>
|
||||
</div>
|
||||
<ul class="js-items"></ul>
|
||||
</div>
|
||||
<button class="Button Button--gray Button--centered is-hidden js-more">View more</button>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="IntermediateInfo IntermediateInfo--flex">
|
||||
<div class="Spinner"></div>
|
||||
<h4 class="CDB-Text CDB-Size-large u-mainTextColor u-secondaryTextColor u-bSpace--m"><%- title %></h4>
|
||||
<div class="CDB-Text CDB-Size-medium u-altTextColor"><%= quote %></div>
|
||||
</div>
|
||||
@@ -0,0 +1,30 @@
|
||||
<li class="FeedItem">
|
||||
<a href="http://<%- username %>.<%- account_host %>" class="FeedItem-avatar">
|
||||
<img src="<%- avatar_url %>" class="UserAvatar-img UserAvatar-img--big" />
|
||||
</a>
|
||||
|
||||
<div class="MapCard MapCard--borderless MapCard--long js-card" data-vis-id="<%- vis.id %>">
|
||||
<a href="<%- base_url %>/viz/<%- vis.id %>/public_map" class="MapCard-header js-header">
|
||||
<div class="MapCard-loader js-loader"></div>
|
||||
</a>
|
||||
|
||||
<div class="MapCard-content">
|
||||
<div class="MapCard-contentBody">
|
||||
<div class="MapCard-contentBodyRow MapCard-contentBodyRow--flex">
|
||||
<h3 class="MapCard-title DefaultTitle CDB-Text is-semibold CDB-Size-large">
|
||||
<a href="<%- base_url %>/viz/<%- vis.id %>/public_map" class="DefaultTitle-link u-ellipsLongText" title="<%- vis.name %>"><%- vis.name %></a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="MapCard-contentFooter CDB-Size-medium u-altTextColor">
|
||||
<div class="MapCard-contentFooterDetails--left">
|
||||
<div class="MapCard-contentFooterTimeDiff DefaultTimeDiff">
|
||||
<i class="CDB-IconFont CDB-IconFont-clock DefaultTimeDiff-icon"></i>
|
||||
<%- updated %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
Reference in New Issue
Block a user