-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from fleetbase/dev-v0.2.10
v0.2.10
- Loading branch information
Showing
26 changed files
with
674 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<div class="flex flex-col mb-4" ...attributes> | ||
<Textarea @value={{this.input}} class="form-input w-full" placeholder={{t "component.comment-thread.comment-input-placeholder"}} rows={{3}} disabled={{not this.publishComment.isIdle}} /> | ||
<div class="flex flex-row items-center justify-end mt-2"> | ||
<Button @type="primary" @buttonType="button" @icon="paper-plane" @text={{t "component.comment-thread.publish-comment-button-text"}} @onClick={{perform this.publishComment}} @disabled={{or (not this.publishComment.isIdle) (not this.input)}} /> | ||
</div> | ||
</div> | ||
<div> | ||
{{#each this.comments as |comment|}} | ||
{{#if (has-block)}} | ||
{{yield (component "comment-thread/comment" comment=comment contextApi=this.context) comment}} | ||
{{else}} | ||
<CommentThread::Comment @comment={{comment}} @contextApi={{this.context}} /> | ||
{{/if}} | ||
{{/each}} | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { inject as service } from '@ember/service'; | ||
import { task } from 'ember-concurrency-decorators'; | ||
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default'; | ||
import getModelName from '@fleetbase/ember-core/utils/get-model-name'; | ||
|
||
/** | ||
* Component to handle a thread of comments. | ||
*/ | ||
export default class CommentThreadComponent extends Component { | ||
/** | ||
* Service to handle data store operations. | ||
* @service | ||
*/ | ||
@service store; | ||
|
||
/** | ||
* Service for handling notifications. | ||
* @service | ||
*/ | ||
@service notifications; | ||
|
||
/** | ||
* Service for internationalization. | ||
* @service | ||
*/ | ||
@service intl; | ||
|
||
/** | ||
* The subject related to the comments. | ||
* @tracked | ||
*/ | ||
@tracked subject; | ||
|
||
/** | ||
* Array of comments related to the subject. | ||
* @tracked | ||
*/ | ||
@tracked comments = []; | ||
|
||
/** | ||
* The text input for publishing a new comment. | ||
* @tracked | ||
*/ | ||
@tracked input = ''; | ||
|
||
/** | ||
* Context object containing utility functions. | ||
*/ | ||
context = { | ||
isCommentInvalid: this.isCommentInvalid.bind(this), | ||
reloadComments: () => { | ||
return this.reloadComments.perform(); | ||
}, | ||
}; | ||
|
||
/** | ||
* Constructor for the comment thread component. | ||
* @param owner - The owner of the component. | ||
* @param subject - The subject of the comment thread. | ||
* @param subjectType - The type of the subject. | ||
*/ | ||
constructor(owner, { subject, subjectType }) { | ||
super(...arguments); | ||
|
||
this.subject = subject; | ||
this.comments = getWithDefault(subject, 'comments', []); | ||
this.subjectType = subjectType ? subjectType : getModelName(subject); | ||
} | ||
|
||
/** | ||
* Asynchronous task to publish a new comment. | ||
* @task | ||
*/ | ||
@task *publishComment() { | ||
if (this.isCommentInvalid(this.input)) { | ||
return; | ||
} | ||
|
||
let comment = this.store.createRecord('comment', { | ||
content: this.input, | ||
subject_uuid: this.subject.id, | ||
subject_type: this.subjectType, | ||
}); | ||
|
||
yield comment.save(); | ||
yield this.reloadComments.perform(); | ||
|
||
this.input = ''; | ||
} | ||
|
||
/** | ||
* Asynchronous task to reload the comments related to the subject. | ||
* @task | ||
*/ | ||
@task *reloadComments() { | ||
this.comments = yield this.store.query('comment', { subject_uuid: this.subject.id, withoutParent: 1, sort: '-created_at' }); | ||
} | ||
|
||
/** | ||
* Checks if a comment is invalid. | ||
* @param {string} comment - The comment to validate. | ||
* @returns {boolean} True if the comment is invalid, false otherwise. | ||
*/ | ||
isCommentInvalid(comment) { | ||
if (!comment) { | ||
this.notifications.warning(this.intl.t('component.comment-thread.comment-input-empty-notification')); | ||
return true; | ||
} | ||
|
||
// make sure comment is at least 2 characters | ||
if (typeof comment === 'string' && comment.length <= 1) { | ||
this.notifications.warning(this.intl.t('component.comment-thread.comment-min-length-notification')); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<div class="thread-comment flex flex-row p-1 space-x-3" ...attributes> | ||
<div class="thread-comment-avatar-wrapper w-18 flex flex-col items-center"> | ||
<Image src={{this.comment.author.avatar_url}} @fallbackSrc={{config "defaultValues.userImage"}} alt={{this.comment.author.name}} class="w-8 h-8 rounded-full" /> | ||
</div> | ||
<div class="thread-comment-content-wrapper flex-1"> | ||
<div class="thread-comment-author flex flex-row items-center"> | ||
<div class="thread-comment-author-name text-sm dark:text-white text-black font-bold mr-1.5">{{this.comment.author.name}}</div> | ||
<div class="thread-comment-created-at dark:text-gray-300 text-gray-600 text-xs">{{t "component.comment-thread.comment-published-ago" createdAgo=this.comment.createdAgo}}</div> | ||
</div> | ||
<div class="thread-comment-conent-paragraph-wrapper mt-2"> | ||
{{#if this.editing}} | ||
<Textarea @value={{this.comment.content}} class="form-input w-full" placeholder={{t "component.comment-thread.comment-reply-placeholder"}} rows={{2}} disabled={{not this.updateComment.isIdle}} /> | ||
<div class="flex flex-row items-center justify-end space-x-2 mt-2"> | ||
<Button @type="link" @buttonType="button" @size="xs" @text={{t "common.cancel"}} @onClick={{this.cancelEdit}} @disabled={{not this.updateComment.isIdle}} /> | ||
<Button @type="primary" @buttonType="button" @icon="save" @size="xs" @iconSize="xs" @iconClass="text-xs" @text={{t "common.save"}} @onClick={{perform this.updateComment}} @disabled={{or (not this.updateComment.isIdle) (not this.comment.content)}} /> | ||
</div> | ||
{{else}} | ||
<p class="thread-comment-conent-paragraph text-xs text-gray-900 dark:text-gray-100">{{this.comment.content}}</p> | ||
{{/if}} | ||
</div> | ||
<div class="thread-comment-conent-actions-wrapper flex flex-row items-center mt-2 space-x-4"> | ||
<Button @wrapperClass="thread-comment-conent-actions-reply" @type="link" @buttonType="button" @size="xs" @iconSize="xs" @textClass="text-xs" @icon="reply" @text={{t "component.comment-thread.reply-comment-button-text"}} @onClick={{this.reply}} /> | ||
{{#if this.comment.editable}} | ||
<Button @wrapperClass="thread-comment-conent-actions-edit" @type="link" @buttonType="button" @size="xs" @iconSize="xs" @textClass="text-xs" @icon="edit" @text={{t "component.comment-thread.edit-comment-button-text"}} @onClick={{this.edit}} /> | ||
<Button @wrapperClass="thread-comment-conent-actions-delete" @type="link" @buttonType="button" @size="xs" @iconSize="xs" @iconClass="text-xs text-danger" @textClass="text-xs text-danger" @icon="trash" @text={{t "component.comment-thread.delete-comment-button-text"}} @onClick={{this.delete}} /> | ||
{{/if}} | ||
</div> | ||
{{#if this.replying}} | ||
<div class="flex flex-col mt-3"> | ||
<Textarea @value={{this.input}} class="form-input w-full" placeholder={{t "component.comment-thread.comment-reply-placeholder"}} rows={{2}} disabled={{not this.publishReply.isIdle}} /> | ||
<div class="flex flex-row items-center justify-end space-x-2 mt-2"> | ||
<Button @type="link" @buttonType="button" @size="xs" @text={{t "common.cancel"}} @onClick={{this.cancelReply}} @disabled={{not this.publishReply.isIdle}} /> | ||
<Button @type="primary" @buttonType="button" @icon="reply" @size="xs" @iconSize="xs" @iconClass="text-xs" @text={{t "component.comment-thread.publish-reply-button-text"}} @onClick={{perform this.publishReply}} @disabled={{or (not this.publishReply.isIdle) (not this.input)}} /> | ||
</div> | ||
</div> | ||
{{/if}} | ||
<div class="thread-comment-replies mt-3"> | ||
{{#each this.comment.replies as |reply|}} | ||
{{#if (has-block)}} | ||
{{yield (component "comment-thread/comment" comment=reply) reply}} | ||
{{else}} | ||
<CommentThread::Comment @comment={{reply}} /> | ||
{{/if}} | ||
{{/each}} | ||
</div> | ||
</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { action } from '@ember/object'; | ||
import { inject as service } from '@ember/service'; | ||
import { task } from 'ember-concurrency-decorators'; | ||
|
||
/** | ||
* Component to handle individual comments in a comment thread. | ||
*/ | ||
export default class CommentThreadCommentComponent extends Component { | ||
/** | ||
* Service to handle data store operations. | ||
* @service | ||
*/ | ||
@service store; | ||
|
||
/** | ||
* The text input for replying or editing comments. | ||
* @tracked | ||
*/ | ||
@tracked input = ''; | ||
|
||
/** | ||
* Flag to indicate if the reply interface is active. | ||
* @tracked | ||
*/ | ||
@tracked replying = false; | ||
|
||
/** | ||
* Flag to indicate if the edit interface is active. | ||
* @tracked | ||
*/ | ||
@tracked editing = false; | ||
|
||
/** | ||
* The constructor for the comment component. | ||
* @param owner - The owner of the component. | ||
* @param comment - The comment data for the component. | ||
*/ | ||
constructor(owner, { comment, contextApi }) { | ||
super(...arguments); | ||
|
||
this.comment = comment; | ||
this.contextApi = contextApi; | ||
} | ||
|
||
/** | ||
* Activates the reply interface. | ||
* @action | ||
*/ | ||
@action reply() { | ||
this.replying = true; | ||
} | ||
|
||
/** | ||
* Deactivates the reply interface. | ||
* @action | ||
*/ | ||
@action cancelReply() { | ||
this.replying = false; | ||
} | ||
|
||
/** | ||
* Activates the edit interface. | ||
* @action | ||
*/ | ||
@action edit() { | ||
this.editing = true; | ||
} | ||
|
||
/** | ||
* Deactivates the edit interface. | ||
* @action | ||
*/ | ||
@action cancelEdit() { | ||
this.editing = false; | ||
} | ||
|
||
/** | ||
* Deletes the current comment. | ||
* @action | ||
*/ | ||
@action delete() { | ||
this.comment.destroyRecord(); | ||
} | ||
|
||
/** | ||
* Asynchronous task to update the current comment. | ||
* @task | ||
*/ | ||
@task *updateComment() { | ||
if (this.contextApi && this.contextApi.isCommentInvalid(this.comment.content)) { | ||
return; | ||
} | ||
|
||
yield this.comment.save(); | ||
this.editing = false; | ||
} | ||
|
||
/** | ||
* Asynchronous task to publish a reply to the current comment. | ||
* @task | ||
*/ | ||
@task *publishReply() { | ||
if (this.contextApi && this.contextApi.isCommentInvalid(this.input)) { | ||
return; | ||
} | ||
|
||
let comment = this.store.createRecord('comment', { | ||
content: this.input, | ||
parent_comment_uuid: this.comment.id, | ||
subject_uuid: this.comment.subject_uuid, | ||
subject_type: this.comment.subject_type, | ||
}); | ||
|
||
yield comment.save(); | ||
yield this.reloadReplies.perform(); | ||
|
||
this.replying = false; | ||
this.input = ''; | ||
} | ||
|
||
/** | ||
* Asynchronous task to reload replies to the current comment. | ||
* @task | ||
*/ | ||
@task *reloadReplies() { | ||
this.comment = yield this.comment.reload(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.