Skip to content

Commit

Permalink
Add project messages
Browse files Browse the repository at this point in the history
  • Loading branch information
begedin authored and joshsmith committed Dec 18, 2017
1 parent 9abb6c1 commit c24b18c
Show file tree
Hide file tree
Showing 98 changed files with 2,463 additions and 484 deletions.
12 changes: 11 additions & 1 deletion app/abilities/project.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { alias, equal } from '@ember/object/computed';
import { alias, equal, or } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import { Ability } from 'ember-can';
import projectMember from 'code-corps-ember/macros/project-member';
Expand All @@ -12,6 +12,15 @@ import projectMember from 'code-corps-ember/macros/project-member';
export default Ability.extend({
currentUser: service(),

/**
* An `ember-can` ability.
*
* Indicates if the current user can manage a project.
* Returns true if the user is the owner of the project.
* @type {Boolean}
*/
canAdminister: or('{userIsAdmin,userIsOwner}'),

/**
* An `ember-can` ability.
*
Expand All @@ -26,5 +35,6 @@ export default Ability.extend({
projectMembership: projectMember('project.projectUsers', 'currentUser.user'),

userRole: alias('projectMembership.role'),
userIsAdmin: equal('userRole', 'admin'),
userIsOwner: equal('userRole', 'owner')
});
10 changes: 10 additions & 0 deletions app/components/conversations/conversation-composer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Component from '@ember/component';
import { empty } from '@ember/object/computed';

export default Component.extend({
classNames: ['conversation-composer'],

body: null,

submitDisabled: empty('body')
});
4 changes: 4 additions & 0 deletions app/components/conversations/conversation-list-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Component from '@ember/component';

export default Component.extend({
});
26 changes: 26 additions & 0 deletions app/components/conversations/conversation-part.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Component from '@ember/component';
import { computed, get } from '@ember/object';
import { alias } from '@ember/object/computed';
import { inject as service } from '@ember/service';

export default Component.extend({
classNames: ['conversation-part'],
classNameBindings: ['isSelf:conversation-part--is-self'],

author: null,
body: null,
readAt: null,
sentAt: null,

currentUser: service(),

user: alias('currentUser.user'),

avatarTooltipSide: computed('isSelf', function() {
return get(this, 'isSelf') ? 'right' : 'left';
}),

isSelf: computed('author', 'user', function() {
return get(this, 'author.id') === get(this, 'user.id');
})
});
23 changes: 23 additions & 0 deletions app/components/conversations/conversation-thread.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Component from '@ember/component';
import { run } from '@ember/runloop';

export default Component.extend({
classNames: ['conversation-thread'],

_setup: (function() {
this.scrollBottomAfterRender();
}).on('didInsertElement'),

didUpdateAttrs() {
this.scrollBottomAfterRender();
},

scrollBottom() {
let [thread] = this.$();
thread.scrollTop = thread.scrollHeight;
},

scrollBottomAfterRender() {
run.scheduleOnce('afterRender', this, 'scrollBottom');
}
});
74 changes: 74 additions & 0 deletions app/components/conversations/new-conversation-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Component from '@ember/component';
import { get, set } from '@ember/object';
import { and, not, notEmpty } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import RSVP from 'rsvp';

const SAVE_SUCCESS = 'Your message is queued to send.';

export default Component.extend({
classNames: ['new-conversation-modal-container'],

showModal: false,

initiatedBy: null,
message: null,
project: null,
user: null,

currentUser: service(),
flashMessages: service(),
store: service(),

isBodyPresent: notEmpty('message.body'),
isSubjectPresent: notEmpty('message.subject'),
submitDisabled: not('submitEnabled'),
submitEnabled: and('isBodyPresent', 'isSubjectPresent'),

createConversation() {
let store = get(this, 'store');
let currentUser = get(this, 'currentUser.user');
let project = get(this, 'project');
let user = get(this, 'user');

let message = store.createRecord('message', {
author: currentUser,
initiatedBy: 'admin',
project
});

let conversation = store.createRecord('conversation', { user });

get(message, 'conversations').pushObject(conversation);

set(this, 'message', message);
},

discardConversation() {
let confirmed = window.confirm('You will lose any unsaved information if you close. Are you sure?');
if (confirmed) {
let message = get(this, 'message');
get(message, 'conversations.firstObject').destroyRecord();
message.destroyRecord();
} else {
// Stop the pipe by rejecting
return RSVP.reject();
}
},

actions: {
send(message) {
// Since it's created without an ID, the store will not detect the
// association was saved and will keep it around.
// We need to store a reference to it, so we can unload it on save
let unsavedConversation = get(message, 'conversations.firstObject');

let onSaved = () => {
get(this, 'flashMessages').clearMessages().success(SAVE_SUCCESS);
get(this, 'store').unloadRecord(unsavedConversation);
};

return message.save().then(onSaved);
}
}
});
5 changes: 5 additions & 0 deletions app/components/conversations/user-details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Component from '@ember/component';

export default Component.extend({
classNames: ['conversation-details']
});
39 changes: 4 additions & 35 deletions app/components/project-header.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Component from '@ember/component';
import { mapBy, alias } from '@ember/object/computed';
import { getProperties } from '@ember/object';
import { inject as service } from '@ember/service';
import projectMember from 'code-corps-ember/macros/project-member';

/**
Displays information about the project and allows a user to join the project
Displays information about the project and allows a user to join the project.
## default usage
```handlebars
Expand All @@ -23,42 +22,12 @@ export default Component.extend({
expanded: false,
tagName: 'header',

/**
* @property store
* @type Ember.Service
*/
store: service(),

/**
@property session
@type Ember.Service
*/
session: service(),

/**
@property currentUser
@type Ember.Service
*/
currentUser: service(),

/**
* @property user
* @type DS.Model
*/
session: service(),
store: service(),
user: alias('currentUser.user'),

currentProjectMembership: projectMember('project.projectUsers', 'currentUser.user'),

projectSkills: mapBy('project.projectSkills', 'skill'),

actions: {
// TODO: This should go outside the component, but with the way the
// project, project.index, project.settings and project.tasks templates are
// set up, it's difficult to move this into a route/controller action
joinProject(project) {
let { store, user } = getProperties(this, 'store', 'user');
return store.createRecord('project-user', { user, project, role: 'pending' })
.save();
}
}
projectSkills: mapBy('project.projectSkills', 'skill')
});
6 changes: 3 additions & 3 deletions app/components/project-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { filterBy, gt, alias } from '@ember/object/computed';
import { inject as service } from '@ember/service';

/**
`project-menu` allows navigation within project functions. Users who
manage an organization can also view contributors and project settings through
additional menu items.
`project-menu` allows navigation within project functions.
Users who manage the project can view additional menu items.
## Default usage
```Handlebars
Expand Down
8 changes: 7 additions & 1 deletion app/components/user-list-item.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { set, get } from '@ember/object';
import { get, set } from '@ember/object';
import { alias } from '@ember/object/computed';

export default Component.extend({
classNames: ['user-list-item'],
Expand All @@ -10,6 +11,11 @@ export default Component.extend({

flashMessages: service(),

projectUser: null,
user: null,

project: alias('projectUser.project'),

actions: {
approve(projectUser) {
set(projectUser, 'role', 'contributor');
Expand Down
35 changes: 29 additions & 6 deletions app/controllers/application.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
import { not, alias, and } from '@ember/object/computed';
import { alias, not } from '@ember/object/computed';
import Controller from '@ember/controller';
import { get } from '@ember/object';
import { computed, get } from '@ember/object';
import { inject as service } from '@ember/service';

export default Controller.extend({
codeTheme: service(),
conversations: service(),
onboarding: service(),
projectTaskBoard: service(),
session: service(),

isNotOnboarding: not('isOnboarding'),
isNotViewingProjectTaskBoard: not('isViewingProjectTaskBoard'),
isNotViewingConversations: not('isViewingConversations'),
isNotViewingTasks: not('isViewingTasks'),
isOnboarding: alias('onboarding.isOnboarding'),
isViewingProjectTaskBoard: alias('projectTaskBoard.isViewing'),
isViewingConversations: alias('conversations.isViewing'),
isViewingTasks: alias('projectTaskBoard.isViewing'),

shouldShowFooter: and('isNotOnboarding', 'isNotViewingProjectTaskBoard'),
shouldShowSpacer: alias('isNotViewingProjectTaskBoard'),
shouldShowFooter: computed('isOnboarding', 'isViewingConversations', 'isViewingTasks', function() {
let isOnboarding = get(this, 'isOnboarding');
let isViewingConversations = get(this, 'isViewingConversations');
let isViewingTasks = get(this, 'isViewingTasks');

if (isOnboarding || isViewingConversations || isViewingTasks) {
return false;
} else {
return true;
}
}),

shouldShowSpacer: computed('isViewingConversations', 'isViewingTasks', function() {
let isViewingConversations = get(this, 'isViewingConversations');
let isViewingTasks = get(this, 'isViewingTasks');

if (isViewingConversations || isViewingTasks) {
return false;
} else {
return true;
}
}),

actions: {
invalidateSession() {
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/project/conversations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Controller from '@ember/controller';

export default Controller.extend({
});
20 changes: 20 additions & 0 deletions app/controllers/project/conversations/conversation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Controller from '@ember/controller';
import { get } from '@ember/object';
import { alias } from '@ember/object/computed';
import { inject as service } from '@ember/service';

export default Controller.extend({
currentUser: service(),

conversation: alias('model'),
user: alias('currentUser.user'),

send(body) {
let conversation = get(this, 'conversation');
let store = get(this, 'store');
let user = get(this, 'user');

let params = { author: user, body, conversation };
return store.createRecord('conversation-part', params).save();
}
});
13 changes: 13 additions & 0 deletions app/models/conversation-part.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo } from 'ember-data/relationships';

export default Model.extend({
body: attr(),
insertedAt: attr(),
readAt: attr(),
updatedAt: attr(),

author: belongsTo('user', { async: true }),
conversation: belongsTo('conversation', { async: true })
});
14 changes: 14 additions & 0 deletions app/models/conversation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo, hasMany } from 'ember-data/relationships';

export default Model.extend({
insertedAt: attr(),
readAt: attr(),
status: attr(),
updatedAt: attr(),

conversationParts: hasMany('conversation-part', { async: true }),
message: belongsTo('message', { async: true }),
user: belongsTo('user', { async: true })
});
16 changes: 16 additions & 0 deletions app/models/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo, hasMany } from 'ember-data/relationships';

export default Model.extend({
body: attr(),
initiatedBy: attr(),
insertedAt: attr(),
subject: attr(),
updatedAt: attr(),

author: belongsTo('user', { async: true }),
project: belongsTo('project', { async: true }),

conversations: hasMany('conversation', { aync: true })
});
Loading

0 comments on commit c24b18c

Please sign in to comment.