From 5f2ca86cae615e4ea41dcdd080de93dd896d4273 Mon Sep 17 00:00:00 2001 From: William Lin Date: Tue, 11 Jul 2017 16:08:10 -0500 Subject: [PATCH 1/6] added comment system --- app/components/comments/comment-compose.js | 50 ++++++++++ app/components/comments/comment-view.js | 5 + app/components/comments/comments-list.js | 38 ++++++++ app/models/comment.js | 12 +++ app/styles/app.scss | 1 + app/styles/comments.scss | 96 +++++++++++++++++++ .../components/comments/comment-compose.hbs | 20 ++++ .../components/comments/comment-view.hbs | 12 +++ .../components/comments/comments-list.hbs | 13 +++ app/templates/components/posts/post-view.hbs | 1 + 10 files changed, 248 insertions(+) create mode 100644 app/components/comments/comment-compose.js create mode 100644 app/components/comments/comment-view.js create mode 100644 app/components/comments/comments-list.js create mode 100644 app/models/comment.js create mode 100644 app/styles/comments.scss create mode 100644 app/templates/components/comments/comment-compose.hbs create mode 100644 app/templates/components/comments/comment-view.hbs create mode 100644 app/templates/components/comments/comments-list.hbs diff --git a/app/components/comments/comment-compose.js b/app/components/comments/comment-compose.js new file mode 100644 index 0000000..5f83d29 --- /dev/null +++ b/app/components/comments/comment-compose.js @@ -0,0 +1,50 @@ +import Component from 'ember-component'; +import get, { getProperties } from 'ember-metal/get'; +import set from 'ember-metal/set'; +import computed from 'ember-computed'; +import service from 'ember-service/inject'; +import { invokeAction } from 'ember-invoke-action'; +import { next } from 'ember-runloop'; +import { isEmpty } from 'ember-utils'; +import $ from 'jquery'; + +export default Component.extend({ + classNames: ['comment-compose'], + expanded: false, + store: service(), + + editing: computed('text', function() { + return !isEmpty(get(this, 'text')); + }).readOnly(), + + reset() { + set(this, 'text', null); + set(this, 'expanded', false); + }, + + actions: { + expand() { + set(this, 'expanded', true); + next(() => { + $('.comment-compose-link').focus(); + }); + }, + + collapse() { + if (!get(this, 'editing')) { + set(this, 'expanded', false); + } + }, + + create() { + const store = get(this, 'store'); + const { text, post } = getProperties(this, 'text', 'post'); + const user = get(this, 'session.account'); + const comment = store.createRecord('comment', { text, user, post }); + comment.save().then((createdComment) => { + invokeAction(this, 'insertComment', createdComment); + this.reset(); + }); + } + } +}); diff --git a/app/components/comments/comment-view.js b/app/components/comments/comment-view.js new file mode 100644 index 0000000..158566f --- /dev/null +++ b/app/components/comments/comment-view.js @@ -0,0 +1,5 @@ +import Component from 'ember-component'; + +export default Component.extend({ + classNames: ['comment-view'] +}); diff --git a/app/components/comments/comments-list.js b/app/components/comments/comments-list.js new file mode 100644 index 0000000..1695228 --- /dev/null +++ b/app/components/comments/comments-list.js @@ -0,0 +1,38 @@ +import Component from 'ember-component'; +import service from 'ember-service/inject'; +import get from 'ember-metal/get'; +import set from 'ember-metal/set'; +import { task } from 'ember-concurrency'; + +export default Component.extend({ + classNames: ['comment-list'], + expanded: false, + store: service(), + + init() { + this._super(...arguments); + get(this, 'getComments').perform(); + }, + + actions: { + expand() { + set(this, 'expanded', true); + }, + + collapse() { + set(this, 'expanded', false); + }, + insertComment(comment) { + get(this, 'comments').pushObject(comment._internalModel); + } + }, + + getComments: task(function* () { + const comments = yield get(this, 'store').query('comment', { + filter: { postId: get(this, 'post.id') }, + include: 'user', + sort: 'createdAt' + }); + set(this, 'comments', comments); + }) +}); diff --git a/app/models/comment.js b/app/models/comment.js new file mode 100644 index 0000000..5956bc9 --- /dev/null +++ b/app/models/comment.js @@ -0,0 +1,12 @@ +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; +import { belongsTo } from 'ember-data/relationships'; + +export default Model.extend({ + text: attr('string'), + rating: attr('integer'), + createdAt: attr('utc'), + updatedAt: attr('utc'), + user: belongsTo('user'), + post: belongsTo('post') +}); diff --git a/app/styles/app.scss b/app/styles/app.scss index 93ceea8..7a04e20 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -80,5 +80,6 @@ a { @import 'channels'; @import 'dashboard'; @import 'posts'; +@import 'comments'; @import 'sign-up'; @import 'ui'; diff --git a/app/styles/comments.scss b/app/styles/comments.scss new file mode 100644 index 0000000..b9b5615 --- /dev/null +++ b/app/styles/comments.scss @@ -0,0 +1,96 @@ +.comment-compose { + width: 100%; + max-width: 512px; + margin: 16px auto; + padding: 16px; + border-radius: 8px; + background: $darker-gray; + overflow: auto; + .comment-compose-header { + display: flex; + align-items: center; + width: 100%; + height: 16px; + cursor: text; + } + input { + height: 36px; + border-bottom: 1px solid $dim; + } + input, textarea { + width: 100%; + padding: 8px; + border-top: 0; + border-right: 0; + border-left: 0; + background: inherit; + color: $text; + font-size: medium; + resize: none; + } + textarea { + min-height: 128px; + margin-bottom: 8px; + border-bottom: 0; + overflow: hidden; + } + button { + padding: 8px 12px; + float: right; + border: 0; + border-radius: 4px; + background: $accent; + color: $darker-gray; + font-size: medium; + cursor: pointer; + &:disabled { + background: $text; + color: $dim; + cursor: default; + } + } +} + +.comment-list { + .comment-list-header { + background: $darkest-gray; + border-radius: 8px; + margin: 16px auto; + padding: 16px; + } +} +.comment-view { + max-width: 512px; + margin: 16px auto; + padding: 16px; + border-radius: 8px; + background: $darker-gray; + .comment-subject { + iframe { + width: 100%; + } + } + .comment-user { + display: flex; + align-items: center; + margin-top: 4px; + .comment-user-avatar { + width: 38px; + margin-right: 16px; + border-radius: 999em; + } + .comment-user-info { + .comment-user-name { + margin-bottom: 4px; + color: $bright; + font-size: 16px; + } + .timestamp { + font-size: 12px; + } + } + } + .comment-text { + margin-top: 8px; + } +} diff --git a/app/templates/components/comments/comment-compose.hbs b/app/templates/components/comments/comment-compose.hbs new file mode 100644 index 0000000..9fbbfae --- /dev/null +++ b/app/templates/components/comments/comment-compose.hbs @@ -0,0 +1,20 @@ +{{#if (not expanded)}} +
+ comment... +
+{{else}} + {{#click-outside + action=(action "collapse") + except-selector=".comment-compose"}} + {{textarea + class="comment-compose-text" + placeholder="comment..." + value=text + autoresize=true + rows=2 + max-rows=15}} + + {{/click-outside}} +{{/if}} +{{yield}} diff --git a/app/templates/components/comments/comment-view.hbs b/app/templates/components/comments/comment-view.hbs new file mode 100644 index 0000000..af54609 --- /dev/null +++ b/app/templates/components/comments/comment-view.hbs @@ -0,0 +1,12 @@ +
+ + +
+{{#if comment.text}} +
{{comment.text}}
+{{/if}} diff --git a/app/templates/components/comments/comments-list.hbs b/app/templates/components/comments/comments-list.hbs new file mode 100644 index 0000000..d30a488 --- /dev/null +++ b/app/templates/components/comments/comments-list.hbs @@ -0,0 +1,13 @@ +{{#if (not expanded)}} +
+ expand comments +
+{{else}} +
+ collapse comments +
+ {{#each comments as |comment|}} + {{comments/comment-view comment=comment}} + {{/each}} +{{/if}} +{{comments/comment-compose post=post insertComment=(action "insertComment")}} diff --git a/app/templates/components/posts/post-view.hbs b/app/templates/components/posts/post-view.hbs index 3c28c0a..42249fb 100644 --- a/app/templates/components/posts/post-view.hbs +++ b/app/templates/components/posts/post-view.hbs @@ -17,3 +17,4 @@ {{#if post.text}}
{{post.text}}
{{/if}} +{{comments/comments-list post=post}} From 4827ddae5761208df8f312120b27b689f5046051 Mon Sep 17 00:00:00 2001 From: William Lin Date: Thu, 13 Jul 2017 02:51:38 -0500 Subject: [PATCH 2/6] only show expand button if there are commments --- .../components/comments/comments-list.hbs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/templates/components/comments/comments-list.hbs b/app/templates/components/comments/comments-list.hbs index d30a488..8b01270 100644 --- a/app/templates/components/comments/comments-list.hbs +++ b/app/templates/components/comments/comments-list.hbs @@ -1,13 +1,15 @@ -{{#if (not expanded)}} -
- expand comments -
-{{else}} -
- collapse comments -
- {{#each comments as |comment|}} - {{comments/comment-view comment=comment}} - {{/each}} +{{#if comments}} + {{#if (not expanded)}} +
+ expand comments +
+ {{else}} +
+ collapse comments +
+ {{#each comments as |comment|}} + {{comments/comment-view comment=comment}} + {{/each}} + {{/if}} {{/if}} {{comments/comment-compose post=post insertComment=(action "insertComment")}} From 09e6eece45a32f62ae452a2ddf6856ec71e8cb71 Mon Sep 17 00:00:00 2001 From: William Lin Date: Sun, 16 Jul 2017 00:46:47 -0500 Subject: [PATCH 3/6] users can now delete and edit their posts --- app/components/posts/post-edit.js | 19 +++++++++++++++++++ app/components/posts/post-view.js | 17 ++++++++++++++++- app/styles/posts.scss | 5 ++++- app/templates/components/posts/post-edit.hbs | 17 +++++++++++++++++ app/templates/components/posts/post-view.hbs | 20 +++++++++++++++++--- 5 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 app/components/posts/post-edit.js create mode 100644 app/templates/components/posts/post-edit.hbs diff --git a/app/components/posts/post-edit.js b/app/components/posts/post-edit.js new file mode 100644 index 0000000..027da39 --- /dev/null +++ b/app/components/posts/post-edit.js @@ -0,0 +1,19 @@ +import Component from 'ember-component'; +import set from 'ember-metal/set'; + +export default Component.extend({ + classNames: ['post-edit'], + + actions: { + save(post) { + post.save().then(() => set(this, 'editing', false)); + }, + + cancel(post) { + // I don't think ember-change-set is needed here + post.rollbackAttributes(); + set(this, 'editing', false); + } + } + +}); diff --git a/app/components/posts/post-view.js b/app/components/posts/post-view.js index 83b59b7..7c8b7d3 100644 --- a/app/components/posts/post-view.js +++ b/app/components/posts/post-view.js @@ -1,5 +1,20 @@ import Component from 'ember-component'; +import get from 'ember-metal/get'; +import computed from 'ember-computed'; export default Component.extend({ - classNames: ['post-view'] + classNames: ['post-view'], + editing: false, + + owner: computed('session', 'post', function() { + return get(this, 'session.account.id') == get(this, 'post.user.id'); + }).readOnly(), + + actions: { + delete_post(post) { + // add confirmation popup? + post.destroyRecord(); + } + } + }); diff --git a/app/styles/posts.scss b/app/styles/posts.scss index d73b172..0c52710 100644 --- a/app/styles/posts.scss +++ b/app/styles/posts.scss @@ -1,4 +1,4 @@ -.post-compose { +.post-compose, .post-edit { width: 100%; max-width: 512px; margin: 16px auto; @@ -57,6 +57,9 @@ padding: 16px; border-radius: 8px; background: $dark-gray; + .post-header { + text-align: right; + } .post-subject { iframe { width: 100%; diff --git a/app/templates/components/posts/post-edit.hbs b/app/templates/components/posts/post-edit.hbs new file mode 100644 index 0000000..3f4cd4d --- /dev/null +++ b/app/templates/components/posts/post-edit.hbs @@ -0,0 +1,17 @@ +{{#click-outside + action=(action "cancel" post) + except-selector=".post-view"}} + {{textarea + class="post-edit-text" + placeholder="caption..." + value=post.text + autoresize=true + rows=2 + max-rows=15}} + + +{{/click-outside}} diff --git a/app/templates/components/posts/post-view.hbs b/app/templates/components/posts/post-view.hbs index 42249fb..83b2b53 100644 --- a/app/templates/components/posts/post-view.hbs +++ b/app/templates/components/posts/post-view.hbs @@ -1,3 +1,13 @@ +{{#if owner}} +
+ + delete + + + edit + +
+{{/if}}
{{#if post.linkFormatted}} {{html-safe post.linkFormatted}} @@ -14,7 +24,11 @@ {{moment-from-now post.createdAt interval=60000 class="timestamp"}}
-{{#if post.text}} -
{{post.text}}
+{{#if (not editing)}} + {{#if post.text}} +
{{post.text}}
+ {{/if}} + {{comments/comments-list post=post}} +{{else}} + {{posts/post-edit post=post editing=editing}} {{/if}} -{{comments/comments-list post=post}} From f6929c2883694e7d7efe5fd415b93dd95ff8b666 Mon Sep 17 00:00:00 2001 From: William Lin Date: Sun, 16 Jul 2017 01:09:30 -0500 Subject: [PATCH 4/6] users can now delete and edit their comments --- app/components/comments/comment-edit.js | 19 +++++++++++++++++++ app/components/comments/comment-view.js | 17 ++++++++++++++++- app/styles/comments.scss | 5 ++++- .../components/comments/comment-edit.hbs | 17 +++++++++++++++++ .../components/comments/comment-view.hbs | 18 ++++++++++++++++-- 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 app/components/comments/comment-edit.js create mode 100644 app/templates/components/comments/comment-edit.hbs diff --git a/app/components/comments/comment-edit.js b/app/components/comments/comment-edit.js new file mode 100644 index 0000000..10d10ea --- /dev/null +++ b/app/components/comments/comment-edit.js @@ -0,0 +1,19 @@ +import Component from 'ember-component'; +import set from 'ember-metal/set'; + +export default Component.extend({ + classNames: ['comment-edit'], + + actions: { + save(comment) { + comment.save().then(() => set(this, 'editing', false)); + }, + + cancel(comment) { + // I don't think ember-change-set is needed here + comment.rollbackAttributes(); + set(this, 'editing', false); + } + } + +}); diff --git a/app/components/comments/comment-view.js b/app/components/comments/comment-view.js index 158566f..ea44b3c 100644 --- a/app/components/comments/comment-view.js +++ b/app/components/comments/comment-view.js @@ -1,5 +1,20 @@ import Component from 'ember-component'; +import get from 'ember-metal/get'; +import computed from 'ember-computed'; export default Component.extend({ - classNames: ['comment-view'] + classNames: ['comment-view'], + editing: false, + + owner: computed('session', 'comment', function() { + return get(this, 'session.account.id') == get(this, 'comment.user.id'); + }).readOnly(), + + actions: { + delete_comment(comment) { + // add confirmation popup? + comment.destroyRecord(); + } + } + }); diff --git a/app/styles/comments.scss b/app/styles/comments.scss index b9b5615..f1e3328 100644 --- a/app/styles/comments.scss +++ b/app/styles/comments.scss @@ -1,4 +1,4 @@ -.comment-compose { +.comment-compose, .comment-edit { width: 100%; max-width: 512px; margin: 16px auto; @@ -65,6 +65,9 @@ padding: 16px; border-radius: 8px; background: $darker-gray; + .comment-header { + text-align: right; + } .comment-subject { iframe { width: 100%; diff --git a/app/templates/components/comments/comment-edit.hbs b/app/templates/components/comments/comment-edit.hbs new file mode 100644 index 0000000..6142af8 --- /dev/null +++ b/app/templates/components/comments/comment-edit.hbs @@ -0,0 +1,17 @@ +{{#click-outside + action=(action "cancel" comment) + except-selector=".comment-view"}} + {{textarea + class="comment-edit-text" + placeholder="caption..." + value=comment.text + autoresize=true + rows=2 + max-rows=15}} + + +{{/click-outside}} diff --git a/app/templates/components/comments/comment-view.hbs b/app/templates/components/comments/comment-view.hbs index af54609..3929727 100644 --- a/app/templates/components/comments/comment-view.hbs +++ b/app/templates/components/comments/comment-view.hbs @@ -1,3 +1,13 @@ +{{#if owner}} + +{{/if}}
-{{#if comment.text}} -
{{comment.text}}
+{{#if (not editing)}} + {{#if comment.text}} +
{{comment.text}}
+ {{/if}} +{{else}} + {{comments/comment-edit comment=comment editing=editing}} {{/if}} From 652d0525bdb9d7604ca35273d2010f17f0093eb0 Mon Sep 17 00:00:00 2001 From: William Lin Date: Fri, 4 Aug 2017 03:08:27 -0500 Subject: [PATCH 5/6] added friend system --- .../friendships/friendship-entry.js | 20 +++++++++ app/components/users/user-entry.js | 21 +++++++++ app/models/friendship.js | 10 +++++ app/models/user.js | 13 +++++- app/router.js | 3 +- app/routes/search.js | 45 +++++++++++++++++++ app/routes/users.js | 6 +-- app/templates/application.hbs | 4 ++ .../friendships/friendship-entry.hbs | 18 ++++++++ app/templates/components/users/user-entry.hbs | 8 ++++ app/templates/search.hbs | 32 +++++++++++++ app/templates/users.hbs | 14 +++++- 12 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 app/components/friendships/friendship-entry.js create mode 100644 app/components/users/user-entry.js create mode 100644 app/models/friendship.js create mode 100644 app/routes/search.js create mode 100644 app/templates/components/friendships/friendship-entry.hbs create mode 100644 app/templates/components/users/user-entry.hbs create mode 100644 app/templates/search.hbs diff --git a/app/components/friendships/friendship-entry.js b/app/components/friendships/friendship-entry.js new file mode 100644 index 0000000..e44ca3d --- /dev/null +++ b/app/components/friendships/friendship-entry.js @@ -0,0 +1,20 @@ +import Component from 'ember-component'; +import service from 'ember-service/inject'; +import set from 'ember-metal/set'; + +export default Component.extend({ + classNames: ['friendship-entry'], + store: service(), + + actions: { + accept(friendship) { + friendship.set('confirmed', true); + friendship.save(); + }, + + // TODO: save decline status instead of deleting record + remove(friendship) { + friendship.destroyRecord(); + } + } +}); diff --git a/app/components/users/user-entry.js b/app/components/users/user-entry.js new file mode 100644 index 0000000..7f4dc73 --- /dev/null +++ b/app/components/users/user-entry.js @@ -0,0 +1,21 @@ +import Component from 'ember-component'; +import get from 'ember-metal/get'; +import service from 'ember-service/inject'; + +export default Component.extend({ + classNames: ['user-entry'], + store: service(), + + actions: { + befriend(friend) { + const user = get(this, 'session.account'); + const store = get(this, 'store'); + const friendship = store.createRecord('friendship', { + user: user, + friend: friend, + confirmed: false + }); + friendship.save(); + } + } +}); diff --git a/app/models/friendship.js b/app/models/friendship.js new file mode 100644 index 0000000..dafba08 --- /dev/null +++ b/app/models/friendship.js @@ -0,0 +1,10 @@ +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; +import { belongsTo } from 'ember-data/relationships'; + +export default Model.extend({ + createdAt: attr('utc'), + confirmed: attr('boolean'), + user: belongsTo('user', { inverse: 'sent_friendships' }), + friend: belongsTo('user', { inverse: 'received_friendships' }) +}); diff --git a/app/models/user.js b/app/models/user.js index 6250458..cad162c 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -39,5 +39,16 @@ export default Model.extend(Validations, { email: attr('string'), password: attr('string'), posts: hasMany('post'), - subscriptions: hasMany('subscription') + subscriptions: hasMany('subscription'), + friendships: hasMany('friendship'), + friends: hasMany('user', { + inverse: null}), + confirmed_friends: hasMany('user', { + inverse: null}), + // not in use but don't know how to remove without breaking ember + sent_friendships: hasMany('friendship', { + inverse: 'user'}), + // not in use but don't know how to remove without breaking ember + received_friendships: hasMany('friendship', { + inverse: 'friend'}) }); diff --git a/app/router.js b/app/router.js index 0dba0fa..af0b9c1 100644 --- a/app/router.js +++ b/app/router.js @@ -20,9 +20,10 @@ Router.map(function() { this.route('new'); }); - this.route('users', { path: '/users/:name' }); + this.route('users', { path: '/users/:id' }); this.route('404', { path: '*' }); + this.route('search'); }); export default Router; diff --git a/app/routes/search.js b/app/routes/search.js new file mode 100644 index 0000000..b0a0c25 --- /dev/null +++ b/app/routes/search.js @@ -0,0 +1,45 @@ +import Ember from 'ember'; +import Route from 'ember-route'; +import get from 'ember-metal/get'; + +export default Route.extend({ + model() { + const users = get(this, 'store').query('user', { + filter: { + available_users_to_friend: true + } + }); + + const pending_friends = get(this, 'store').query('friendship', { + filter: { + confirmed_friends: false + } + }); + + const sent = get(this, 'store').query('friendship', { + filter: { + sent: true + } + }); + + const received = get(this, 'store').query('friendship', { + filter: { + received: true + } + }); + + const friends = get(this, 'store').query('friendship', { + filter: { + confirmed_friends: true + } + }); + + return Ember.RSVP.hash({ + users: users, + sent: sent, + received: received, + friends: friends, + pending_friends: pending_friends + }); + } +}); diff --git a/app/routes/users.js b/app/routes/users.js index 5d29eed..d98f570 100644 --- a/app/routes/users.js +++ b/app/routes/users.js @@ -3,11 +3,11 @@ import get from 'ember-metal/get'; export default Route.extend({ model(params) { - const { name } = params; + const { id } = params; return get(this, 'store') .query('user', { - filter: { name } + filter: { id } }) - .then(records => get(records, 'firstObject')) + .then(records => get(records, 'firstObject')); } }); diff --git a/app/templates/application.hbs b/app/templates/application.hbs index e3d3f96..a0fa084 100644 --- a/app/templates/application.hbs +++ b/app/templates/application.hbs @@ -2,8 +2,12 @@