Skip to content

Commit

Permalink
moved locale selector as tray componentm, created basic aht components
Browse files Browse the repository at this point in the history
  • Loading branch information
roncodes committed Apr 3, 2024
1 parent e28b758 commit ec1fa31
Show file tree
Hide file tree
Showing 30 changed files with 709 additions and 114 deletions.
5 changes: 2 additions & 3 deletions addon/components/chat-container.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<div class="absolute fixed right-0 bottom-0 bg-gray-900 w-12 h-12 rounded-full cursor-pointer border-white border-2 border-gray-600 p-2">
<a>Chat</a>
{{#each this.chatChannels as |chatChannel|}}
<div class="chat-container" ...attributes>
{{#each this.chat.openChannels as |chatChannel|}}
<ChatWindow @channel={{chatChannel}} />
{{/each}}
</div>
18 changes: 4 additions & 14 deletions addon/components/chat-container.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class ChatContainerComponent extends Component {
@tracked chatChannels = [{
title: "test",
public_id: "test",
chats: [
{
owner: "Doljko",
message: "Hello"
}
]
}];
@tracked isVisible = false;

@action openChatbox () {
this.isVisible = true
@service chat;
constructor(owner) {
super(...arguments);
}
}
27 changes: 27 additions & 0 deletions addon/components/chat-tray.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<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|>
<dd.Trigger class={{@triggerClass}}>
<div class="next-org-button-trigger flex-shrink-0 {{if dd.isOpen 'is-open'}}">
<FaIcon @icon="message" />
{{!-- {{#if this.notifications.length}}
<div class="chat-tray-unread-notifications-badge">{{this.notifications.length}}</div>
{{/if}} --}}
</div>
</dd.Trigger>
<dd.Content class="chat-tray-panel-container">
<div class="chat-tray-panel">
<div class="p-4">
<Button @type="primary" @text="Start Chat" @onClick={{this.startChat}} />
</div>
<div class="flex flex-col space-y-2 px-4 py-2">
{{#each this.channels as |channel|}}
<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>
{{/each}}
</div>
</div>
</dd.Content>
</BasicDropdown>
</div>
29 changes: 29 additions & 0 deletions addon/components/chat-tray.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task } from 'ember-concurrency';

export default class ChatTrayComponent extends Component {
@service chat;
@service socket;
@tracked channels = [];

constructor(owner) {
super(...arguments);
this.chat.loadChannels.perform({
withChannels: (channels) => {
this.channels = channels;
},
});
}

@action openChannel(chatChannelRecord) {
this.chat.openChannel(chatChannelRecord);
}

@action startChat() {
const chatChannelRecord = this.chat.createChatChannel();
this.openChannel(chatChannelRecord);
}
}
55 changes: 43 additions & 12 deletions addon/components/chat-window.hbs
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
{{! template-lint-disable no-inline-styles }}
<div class="bg-gray-400 absolute right-0 bottom-0" style="right: 54px;">
<div class="flex justify-center h-64 w-80 mt-2" style="height: 400px; right: 54px; bottom:8px">Lanna</div>
<div class="ml-4 mb-4">
hi,how are you?
</div>
<div class="flex space-x-4 ml-2">
<div class="flex-1">
<InputGroup />
</div>
<div class="mr-4">
<Button @type="primary" @buttonType="button" @icon="save" @size="xs" @iconSize="xs" />
<div class="chat-window-container" ...attributes>
<div class="chat-window-controls-container">
<div class="chat-window-name">
{{n-a this.channel.name "Untitled Chat"}}
</div>
<div class="chat-window-controls">
<button type="button" class="chat-window-button" {{on "click" this.addParticipant}}>
<FaIcon @icon="user-plus" @size="sm" />
</button>
<button type="button" class="chat-window-button chat-window-close-button" {{on "click" this.closeChannel}}>
<FaIcon @icon="times" @size="sm" />
</button>
</div>
</div>
<div class="chat-window-participants-container">
{{#each this.channel.participants as |chatParticipant|}}
<div class="chat-window-participant-name">{{chatParticipant.name}}</div>
{{/each}}
</div>
<div class="chat-window-messages-container">
<div class="chat-window-messages">
{{#each this.channel.messages as |chatMessage|}}
<div class="chat-message-container">
<div class="chat-message-sender-bubble">
<Image src={{chatMessage.sender.avatar_url}} @fallbackSrc={{config "defaultValues.userImage"}} alt={{this.user.name}} />
<div class="chat-message-sender-name">{{chatMessage.sender.name}}</div>
</div>
<div class="chat-message-content-bubble-container">
<div class="chat-message-content-bubble {{if (eq chatMessage.sender_uuid this.currentUser.id) "sender-bubble"}}">
{{chatMessage.content}}
</div>
<div class="chat-message-created-at">{{chatMessage.createdAgo}}</div>
</div>
</div>
{{/each}}
</div>
</div>
<div class="chat-window-input-container">
<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}} />
</div>
</div>
</div>
38 changes: 37 additions & 1 deletion addon/components/chat-window.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class ChatWindowComponent extends Component {}
export default class ChatWindowComponent extends Component {
@service chat;
@service currentUser;
@tracked channel;
@tracked sender;
@tracked pendingMessageContent = '';

constructor(owner, { channel }) {
super(...arguments);
this.channel = channel;
this.sender = this.getSenderFromParticipants(channel);
}

@action sendMessage() {
this.chat.sendMessage(this.channel, this.sender, this.pendingMessageContent);
this.pendingMessageContent = '';
}

@action closeChannel() {
this.chat.closeChannel(this.channel);
}

@action addParticipant() {}

getSenderFromParticipants(channel) {
const participants = channel.participants ?? [];
console.log('#participants', participants);
const sender = participants.find((chatParticipant) => {
return chatParticipant.user_uuid === this.currentUser.id;
});

return sender;
}
}
2 changes: 2 additions & 0 deletions addon/components/layout/header.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
<div id="view-header-actions"></div>
<div class="flex items-center justify-between">
<div class="flex-1 flex items-center pr-1">
<LocaleSelectorTray class="mr-0.5" />
<NotificationTray @registerAPI={{@registerNotificationTrayApi}} @onClickNotification={{@onClickNotification}} />
<ChatTray />
</div>
<div class="flex-1 flex items-center pr-1">
<Layout::Header::Dropdown @items={{this.organizationNavigationItems}} @onAction={{@onAction}} class="flex-shrink-0" @triggerClass="flex-shrink-0" as |dd|>
Expand Down
30 changes: 30 additions & 0 deletions addon/components/locale-selector-tray.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="next-user-button" ...attributes>
<BasicDropdown @defaultClass={{@wrapperClass}} @onOpen={{@onOpen}} @onClose={{@onClose}} @verticalPosition={{@verticalPosition}} @horizontalPosition={{@horizontalPosition}} @renderInPlace={{or @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="globe" @size="sm" />
</div>
</dd.Trigger>
<dd.Content class={{@contentClass}}>
<div class="next-dd-menu {{@dropdownMenuClass}} {{if dd.isOpen 'is-open'}}">
{{#each-in this.availableLocales as |key country|}}
<div class="px-1">
<a href="javascript:;" class="next-dd-item" {{on "click" (fn this.changeLocale key)}}>
<div class="flex flex-row items-center justify-between w-full">
<div class="flex-1">
<span class="mr-1">{{country.emoji}}</span>
<span>{{country.language}}</span>
</div>
{{#if (eq this.currentLocale key)}}
<div>
<FaIcon @icon="check" class="text-green-400" />
</div>
{{/if}}
</div>
</a>
</div>
{{/each-in}}
</div>
</dd.Content>
</BasicDropdown>
</div>
144 changes: 144 additions & 0 deletions addon/components/locale-selector-tray.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task } from 'ember-concurrency-decorators';

export default class LocaleSelectorTrayComponent extends Component {
/**
* Inject the intl service.
*
* @memberof LocaleSelectorComponent
*/
@service intl;

/**
* Inject the intl service.
*
* @memberof LocaleSelectorComponent
*/
@service fetch;

/**
* Tracks all the available locales.
*
* @memberof LocaleSelectorComponent
*/
@tracked locales = [];

/**
* All available countries data.
*
* @memberof LocaleSelectorComponent
*/
@tracked countries = [];

/**
* The current locale in use.
*
* @memberof LocaleSelectorComponent
*/
@tracked currentLocale;

/**
* Creates an instance of LocaleSelectorComponent.
* @memberof LocaleSelectorComponent
*/
constructor() {
super(...arguments);

this.locales = this.intl.locales;
this.currentLocale = this.intl.primaryLocale;
this.loadAvailableCountries.perform();

// Check for locale change
this.intl.onLocaleChanged(() => {
this.currentLocale = this.intl.primaryLocale;
});
}

/**
* Handles the change of locale.
* @param {string} selectedLocale - The selected locale.
* @returns {void}
* @memberof LocaleSelectorComponent
* @method changeLocale
* @instance
* @action
*/
@action changeLocale(selectedLocale) {
this.currentLocale = selectedLocale;
this.intl.setLocale(selectedLocale);
// Persist to server
this.saveUserLocale.perform(selectedLocale);
}

/**
* Loads available countries asynchronously.
* @returns {void}
* @memberof LocaleSelectorComponent
* @method loadAvailableCountries
* @instance
* @task
* @generator
*/
@task *loadAvailableCountries() {
this.countries = yield this.fetch.get('lookup/countries', { columns: ['name', 'cca2', 'flag', 'emoji', 'languages'] });
this.availableLocales = this._createAvailableLocaleMap();
}

/**
* Saves the user's selected locale to the server.
* @param {string} locale - The user's selected locale.
* @returns {void}
* @memberof LocaleSelectorComponent
* @method saveUserLocale
* @instance
* @task
* @generator
*/
@task *saveUserLocale(locale) {
yield this.fetch.post('users/locale', { locale });
}

/**
* Creates a map of available locales.
* @private
* @returns {Object} - The map of available locales.
* @memberof LocaleSelectorComponent
* @method _createAvailableLocaleMap
* @instance
*/
_createAvailableLocaleMap() {
const localeMap = {};

for (let i = 0; i < this.locales.length; i++) {
const locale = this.locales.objectAt(i);

localeMap[locale] = this._findCountryDataForLocale(locale);
}

return localeMap;
}

/**
* Finds country data for a given locale.
* @private
* @param {string} locale - The locale to find country data for.
* @returns {Object|null} - The country data or null if not found.
* @memberof LocaleSelectorComponent
* @method _findCountryDataForLocale
* @instance
*/
_findCountryDataForLocale(locale) {
const localeCountry = locale.split('-')[1];
const country = this.countries.find((country) => country.cca2.toLowerCase() === localeCountry);

if (country) {
// get the language
country.language = Object.values(country.languages)[0];
}

return country;
}
}
Loading

0 comments on commit ec1fa31

Please sign in to comment.