Skip to content

Commit

Permalink
chat window improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
roncodes committed Apr 8, 2024
1 parent 297ba88 commit 5aa0c0d
Show file tree
Hide file tree
Showing 24 changed files with 349 additions and 20 deletions.
31 changes: 14 additions & 17 deletions addon/components/chat-tray.hbs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
<div class="next-user-button" ...attributes>
<BasicDropdown
@registerAPI={{this.registerAPI}}
@defaultClass={{@wrapperClass}}
@onOpen={{@onOpen}}
@onClose={{@onClose}}
@verticalPosition={{@verticalPosition}}
@horizontalPosition={{@horizontalPosition}}
@renderInPlace={{true}}
@initiallyOpened={{@initiallyOpened}}
as |dd|
>
<BasicDropdown @registerAPI={{this.registerAPI}} @defaultClass={{@wrapperClass}} @onOpen={{@onOpen}} @onClose={{@onClose}} @verticalPosition={{@verticalPosition}} @horizontalPosition={{@horizontalPosition}} @renderInPlace={{true}} @initiallyOpened={{@initiallyOpened}} as |dd|>
<dd.Trigger class={{@triggerClass}}>
<div class="next-org-button-trigger flex-shrink-0 {{if dd.isOpen 'is-open'}}">
<FaIcon @icon="message" />
Expand All @@ -26,13 +16,20 @@
<div class="flex flex-col space-y-2 px-4 py-2">
{{#each this.channels as |channel|}}
<div class="flex items-center">
<button type="button" class="rounded-lg border border-black text-white bg-gray-700 px-4 py-2 shadow-sm w-full" {{on "click" (fn this.openChannel channel)}}>
<div class="font-bold">{{n-a channel.name "Untitled Chat"}}</div>
<div class="text-sm truncate">{{channel.lastMessage.content}}</div>
</button>
<button type="button" {{on "click" (fn this.removeChannel channel)}}>
<FaIcon @icon="times" @size="sm" />
<button type="button" class="flex flex-col rounded-lg border border-black text-white bg-gray-700 px-2 py-1 shadow-sm w-full" {{on "click" (fn this.openChannel channel)}}>
<div class="font-bold mb-2">{{n-a channel.title "Untitled Chat"}}</div>
<div class="flex flex-row items-center">
<div class="w-14">
<Image src={{channel.lastMessage.sender.avatar_url}} @fallbackSrc={{config "defaultValues.userImage"}} alt={{channel.lastMessage.sender.name}} class="rounded-full shadow-sm w-12 h-12" />
</div>
<div class="text-sm truncate text-black">{{channel.lastMessage.content}}</div>
</div>
</button>
<div class="px-4">
<button type="button" {{on "click" (fn this.removeChannel channel)}}>
<FaIcon @icon="times" @size="sm" />
</button>
</div>
</div>
{{/each}}
</div>
Expand Down
21 changes: 20 additions & 1 deletion addon/components/chat-window.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,30 @@
</div>
</div>
<div class="chat-window-input-container">
<div class="chat-window-attachments-container">
<div class="chat-window-attachment-input">
<FileUpload @name={{this.customField.name}} @for={{this.customField.name}} @accept={{join "," this.acceptedFileTypes}} @multiple={{true}} @onFileAdded={{this.onFileAddedHandler}}>
<a tabindex={{0}} class="btn btn-default btn-xs cursor-pointer">
<FaIcon @icon="paperclip" @size="sm" class="mr-2" />
<span>Add Attachment</span>
</a>
</FileUpload>
{{#if this.pendingAttachmentFile}}
<div class="ml-2 flex items-center text-sm">
<Spinner class="dark:text-blue-400 text-blue-900" />
<span class="ml-2 text-xs dark:text-blue-400 text-blue-900">{{round this.pendingAttachmentFile.progress}}%</span>
</div>
{{/if}}
</div>
{{#each this.pendingAttachmentFiles as |pendingFile|}}
<ChatWindow::PendingAttachment @file={{pendingFile}} @onRemove={{fn this.removePendingAttachmentFile pendingFile}} />
{{/each}}
</div>
<div class="chat-window-input-box">
<Textarea @value={{this.pendingMessageContent}} placeholder="Type your message here" class="chat-window-input" rows="3" />
</div>
<div class="chat-window-submit-container">
<Button @type="primary" @icon="paper-plane" @text="Send" @onClick={{this.sendMessage}} />
<Button @type="primary" @icon="paper-plane" @text="Send" @size="xs" @onClick={{this.sendMessage}} />
</div>
</div>
</div>
61 changes: 60 additions & 1 deletion addon/components/chat-window.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@ export default class ChatWindowComponent extends Component {
@tracked senderIsCreator;
@tracked availableUsers = [];
@tracked pendingMessageContent = '';
@tracked pendingAttachmentFile;
@tracked pendingAttachmentFiles = [];
acceptedFileTypes = [
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/msword',
'application/pdf',
'application/x-pdf',
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'video/mp4',
'video/quicktime',
'video/x-msvideo',
'video/x-flv',
'video/x-ms-wmv',
'audio/mpeg',
'video/x-msvideo',
'application/zip',
'application/x-tar',
];

constructor(owner, { channel }) {
super(...arguments);
Expand All @@ -34,13 +57,49 @@ export default class ChatWindowComponent extends Component {
this.channel.reloadParticipants();
break;
case 'chat_message.created':
// this.channel.reloadParticipants();
this.chat.insertMessageFromSocket(this.channel, socketEvent.data);
break;
}
});
}

@action onFileAddedHandler(file) {
// since we have dropzone and upload button within dropzone validate the file state first
// as this method can be called twice from both functions
if (['queued', 'failed', 'timed_out', 'aborted'].indexOf(file.state) === -1) {
return;
}

// set file for progress state
this.pendingAttachmentFile = file;

// Queue and upload immediatley
this.fetch.uploadFile.perform(
file,
{
path: `uploads/chat/${this.channel.id}/attachments`,
type: 'chat_attachment',
subject_uuid: this.channel.id,
subject_type: 'chat_channel',
},
(uploadedFile) => {
this.pendingAttachmentFiles.pushObject(uploadedFile);
this.pendingAttachmentFile = undefined;
},
() => {
// remove file from queue
if (file.queue && typeof file.queue.remove === 'function') {
file.queue.remove(file);
}
this.pendingAttachmentFile = undefined;
}
);
}

@action removePendingAttachmentFile(pendingFile) {
this.pendingAttachmentFiles.removeObject(pendingFile);
}

@action sendMessage() {
this.chat.sendMessage(this.channel, this.sender, this.pendingMessageContent);
this.pendingMessageContent = '';
Expand Down
1 change: 1 addition & 0 deletions addon/components/chat-window/attachment.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{yield}}
3 changes: 3 additions & 0 deletions addon/components/chat-window/attachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Component from '@glimmer/component';

export default class ChatWindowAttachmentComponent extends Component {}
1 change: 1 addition & 0 deletions addon/components/chat-window/feed.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{yield}}
3 changes: 3 additions & 0 deletions addon/components/chat-window/feed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Component from '@glimmer/component';

export default class ChatWindowFeedComponent extends Component {}
1 change: 1 addition & 0 deletions addon/components/chat-window/log.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{yield}}
3 changes: 3 additions & 0 deletions addon/components/chat-window/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Component from '@glimmer/component';

export default class ChatWindowLogComponent extends Component {}
1 change: 1 addition & 0 deletions addon/components/chat-window/message.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{yield}}
3 changes: 3 additions & 0 deletions addon/components/chat-window/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Component from '@glimmer/component';

export default class ChatWindowMessageComponent extends Component {}
35 changes: 35 additions & 0 deletions addon/components/chat-window/pending-attachment.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<div class="chat-window-pending-attachment">
<div class="chat-window-pending-attachment-preview">
{{#if this.isImage}}
<Image src={{@file.url}} alt={{@file.original_filename}} class="x-fleetbase-file-preview rounded-md shadow-sm" />
{{else}}
<div class="x-fleetbase-file-preview">
<FileIcon @file={{@file}} @hideExtension={{true}} @iconSize="2xl" />
</div>
{{/if}}
</div>
<div class="chat-window-pending-attachment-name">
{{truncate-filename @file.original_filename}}
</div>
<div class="chat-window-pending-attachment-actions">
<DropdownButton @dropdownId="x-fleetbase-file-actions-dropdown" @icon="ellipsis" @iconSize="xs" @iconPrefix={{@dropdownButtonIconPrefix}} @text={{@dropdownButtonText}} @size="xs" @horizontalPosition="left" @calculatePosition={{@dropdownButtonCalculatePosition}} @renderInPlace={{or @dropdownButtonRenderInPlace true}} @wrapperClass={{concat @dropdownButtonWrapperClass " " "next-nav-item-dropdown-button"}} @triggerClass={{@dropdownButtonTriggerClass}} @registerAPI={{@registerAPI}} @onInsert={{this.onDropdownButtonInsert}} as |dd|>
<div class="next-dd-menu mt-0i" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
<div class="px-1">
<div class="text-sm flex flex-row items-center px-3 py-1 rounded-md my-1 text-gray-800 dark:text-gray-300">
{{t "component.file.dropdown-label"}}
</div>
</div>
<div class="next-dd-menu-seperator"></div>
<div role="group" class="px-1">
{{!-- template-lint-disable no-nested-interactive --}}
<a href="javascript:;" role="menuitem" class="next-dd-item text-danger" {{on "click" (fn this.onDropdownItemClick "onRemove" dd)}}>
<span class="mr-1">
<FaIcon @icon="trash" @prefix={{@dropdownButtonIconPrefix}} />
</span>
{{t "common.delete"}}
</a>
</div>
</div>
</DropdownButton>
</div>
</div>
43 changes: 43 additions & 0 deletions addon/components/chat-window/pending-attachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class ChatWindowPendingAttachmentComponent extends Component {
@tracked file;
@tracked isImage = false;

constructor(owner, { file }) {
super(...arguments);

this.file = file;
this.isImage = this.isImageFile(file);
}

@action onDropdownItemClick(action, dd) {
if (typeof dd.actions === 'object' && typeof dd.actions.close === 'function') {
dd.actions.close();
}

if (typeof this.args[action] === 'function') {
this.args[action](this.file);
}
}

isImageFile(file) {
if (!file || (!file.original_filename && !file.url && !file.path)) {
return false;
}

const filename = file.original_filename || file.url || file.path;
const extensionMatch = filename.match(/\.(.+)$/);

if (!extensionMatch) {
return false;
}

const extension = extensionMatch[1].toLowerCase();
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp'];

return imageExtensions.includes(extension);
}
}
27 changes: 26 additions & 1 deletion addon/styles/components/chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,25 @@
box-shadow: none;
}

.chat-window-submit-container button.btn.btn-sm,
.chat-window-attachment-input a.btn.btn-xs,
.chat-window-attachment-input a,
.chat-window-submit-container button.btn.btn-xs,
.chat-window-submit-container button {
background-color: #64748b;
border: 1px solid #64748b;
color: #f9fafb;
}

.chat-window-attachment-input a.btn.btn-xs:hover,
.chat-window-attachment-input a:hover,
.chat-window-submit-container button.btn.btn-xs:hover,
.chat-window-submit-container button:hover {
background-color: #64748b;
border: 1px solid #64748b;
color: #f9fafb;
opacity: 0.5;
}

.chat-window-controls-container {
display: flex;
flex-direction: row;
Expand Down Expand Up @@ -275,3 +287,16 @@
white-space: nowrap;
color: #475569;
}

.chat-window-attachments-container {
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
padding: 0.5rem;
}

.chat-window-attachment-input {
display: flex;
flex-direction: row;
}
1 change: 1 addition & 0 deletions app/components/chat-window/attachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/ember-ui/components/chat-window/attachment';
1 change: 1 addition & 0 deletions app/components/chat-window/feed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/ember-ui/components/chat-window/feed';
1 change: 1 addition & 0 deletions app/components/chat-window/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/ember-ui/components/chat-window/log';
1 change: 1 addition & 0 deletions app/components/chat-window/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/ember-ui/components/chat-window/message';
1 change: 1 addition & 0 deletions app/components/chat-window/pending-attachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@fleetbase/ember-ui/components/chat-window/pending-attachment';
26 changes: 26 additions & 0 deletions tests/integration/components/chat-window/attachment-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'dummy/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Component | chat-window/attachment', function (hooks) {
setupRenderingTest(hooks);

test('it renders', async function (assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });

await render(hbs`<ChatWindow::Attachment />`);

assert.dom().hasText('');

// Template block usage:
await render(hbs`
<ChatWindow::Attachment>
template block text
</ChatWindow::Attachment>
`);

assert.dom().hasText('template block text');
});
});
26 changes: 26 additions & 0 deletions tests/integration/components/chat-window/feed-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'dummy/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Component | chat-window/feed', function (hooks) {
setupRenderingTest(hooks);

test('it renders', async function (assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });

await render(hbs`<ChatWindow::Feed />`);

assert.dom().hasText('');

// Template block usage:
await render(hbs`
<ChatWindow::Feed>
template block text
</ChatWindow::Feed>
`);

assert.dom().hasText('template block text');
});
});
Loading

0 comments on commit 5aa0c0d

Please sign in to comment.