3 var Marionette = require('backbone.marionette');
4 var Backbone = require('backbone');
5 var context = require('gitter-web-client-context');
6 var social = require('../../utils/social');
7 var apiClient = require('../../components/api-client');
8 var template = require('./tmpl/collaboratorsView.hbs');
9 var itemTemplate = require('./tmpl/collaboratorsItemView.hbs');
10 var emptyViewTemplate = require('./tmpl/collaboratorsEmptyView.hbs');
11 var appEvents = require('../../utils/appevents');
12 var collaboratorsModels = require('../../collections/collaborators');
14 require('@gitterhq/styleguide/css/components/buttons.css');
15 require('@gitterhq/styleguide/css/components/links.css');
17 module.exports = (function() {
18 var ItemView = Marionette.ItemView.extend({
20 'submit form': 'inviteUser',
21 'click .js-add': 'addUser'
24 className: 'welcome-modal__collaborator',
26 template: itemTemplate,
28 initialize: function(options) {
29 this.userModel = options.model;
30 this.stateModel = new Backbone.Model({
32 emailRequiredUserId: null
35 this.listenTo(this.userModel, 'change', this.render);
36 this.listenTo(this.stateModel, 'change', this.render);
40 * TODO: deal with non-GitHub users too
42 inviteGitHubUser: function(data) {
44 var state = 'inviting';
46 this.stateModel.set('state', state);
49 .post('/invites', data)
50 .then(function(invite) {
52 self.userModel.set('email', invite.email);
55 if (invite.status === 'added') {
56 self.stateModel.set('state', 'added');
57 } else if (invite.status === 'invited') {
58 self.stateModel.set('state', 'invited');
62 if (e.status === 409) {
63 self.stateModel.set('state', 'fail_409');
64 } else if (e.status === 428) {
66 state: 'email_address_required'
69 self.stateModel.set('state', 'fail');
74 inviteUser: function() {
75 var email = this.$el.find('.js-invite-email').val();
76 this.userModel.set({ email: email });
77 this.inviteGitHubUser(this.userModel.toJSON());
79 // stop the page reloading
84 appEvents.triggerParent('track-event', 'welcome-add-user-click');
86 this.inviteGitHubUser(this.userModel.toJSON(), null);
91 serializeData: function() {
92 var state = this.stateModel.get('state');
93 var displayName = this.userModel.get('displayName');
94 var email = this.userModel.get('email');
97 initial: { text: displayName, showAddButton: true },
98 adding: { text: 'Adding…' },
99 added: { text: displayName + ' added' },
100 invited: { text: email ? 'Invited ' + email : 'Invited' },
101 fail: { text: 'Unable to add ' + displayName },
102 fail_409: { text: 'Already invited' },
103 email_address_required: { text: 'Enter ' + displayName + "'s email", showEmailForm: true },
104 inviting: { text: 'Inviting…' }
107 var data = states[state] || states.initial;
108 data.avatarUrl = this.userModel.get('avatarUrl');
114 var EmptyView = Marionette.ItemView.extend({
115 template: emptyViewTemplate,
116 className: 'welcome-modal__no-suggestions',
117 initialize: function(options) {
118 this.model.set('security', options.security);
119 this.model.set('githubType', options.githubType);
120 this.model.set('url', options.url);
123 serializeData: function() {
124 var data = this.model.toJSON();
125 // FIXME: Just rename it so it doesn't include the `url` module: https://github.com/altano/handlebars-loader/issues/75
126 data.stub = data.url;
128 if (data.githubType === 'ORG') {
129 data.showOrgMessage = true;
132 if (data.githubType === 'ORG_CHANNEL') {
133 if (data.security === 'INHERITED') {
134 data.showOrgMessage = true;
138 if (data.security === 'PUBLIC') {
139 data.isPublic = true;
146 var View = Marionette.CompositeView.extend({
147 childViewContainer: '.js-container',
149 emptyView: EmptyView,
151 childViewOptions: function() {
152 if (!this.collection.length) {
154 githubType: context.troupe().get('githubType'),
155 security: context.troupe().get('security'),
156 url: context.troupe().get('url')
163 constructor: function() {
164 //instantiate our collection
165 this.collection = new collaboratorsModels.CollabCollection();
167 //if we should fetch data we should
168 if (this.shouldFetch()) {
169 //If we render initially we will get a flash of the empty view
170 //to avoid that we set hasGotData to signify that we have not yet received any data
171 this.collection.fetch();
172 this.hasGotSomeData = false;
175 //if we don't need to get some data we should reset the catch
176 else this.hasGotSomeData = true;
181 //once we get some data we set it to true so we can
183 this.hasGotSomeData = true;
185 //and call a manual render
192 Marionette.CompositeView.prototype.constructor.apply(this, arguments);
195 initialize: function() {
196 //listen to room permission changes so we can refresh the collection
197 this.listenTo(context.troupe(), 'change:id', this.onRoomChange, this);
201 'click .js-close': 'dismiss',
202 'click #add-button': 'clickAddButton',
203 'click #share-button': 'clickShareButton'
206 //when a room changes refresh the collection
207 onRoomChange: function() {
208 //hide the view so we don't see collaborators from previous rooms
210 appEvents.trigger('collaboratorsView:hide');
212 //fetch if we need to
213 if (this.shouldFetch()) return this.collection.fetch();
215 //render if we do not
219 serializeData: function() {
220 var uri = context.troupe().get('uri');
222 isPublic: context.troupe().get('security') === 'PUBLIC',
223 twitterLink: social.generateTwitterShareUrl(uri),
224 facebookLink: social.generateFacebookShareUrl(uri)
228 clickAddButton: function() {
229 appEvents.triggerParent('track-event', 'welcome-search-clicked');
230 window.location.href = '#add';
233 clickShareButton: function() {
234 window.location.href = '#share';
237 dismiss: function() {
241 //Check if we should fetch data
242 shouldFetch: function() {
243 var roomModel = context.troupe();
244 var roomType = roomModel.get('githubType');
245 var userCount = roomModel.get('userCount');
247 //don't fetch for one-to-one rooms
248 if (roomType === 'ONETOONE') return false;
250 //don't fetch if the user is not an admin
251 if (!context.isTroupeAdmin()) return false;
253 //don't run if we have more than one user
254 if (userCount > 1) return false;
256 //if all else fails fetch some data
260 //Check if we should render content
261 shouldRender: function() {
262 //if we should fetch data && have have previously
263 //in the app life cycle had some data
264 if (this.shouldFetch() && this.hasGotSomeData) return true;
268 if (this.shouldRender()) {
269 Marionette.CompositeView.prototype.render.apply(this, arguments);
271 appEvents.trigger('collaboratorsView:show');
274 appEvents.trigger('collaboratorsView:hide');