Skip to content
This repository has been archived by the owner on May 16, 2023. It is now read-only.

Commit

Permalink
Merge pull request #33 from rpaschoal/development
Browse files Browse the repository at this point in the history
[1.0.5] Merge from Development
  • Loading branch information
rpaschoal authored Feb 26, 2018
2 parents 1b3d037 + 038eee4 commit 625748d
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ script:
# FIX (Tests): node_modules/@angular/core/src/render3/ng_dev_mode.d.ts(9,11): error TS2451: Cannot redeclare block-scoped variable 'ngDevMode'.
- rm -f node_modules/@angular/core/src/render3/ng_dev_mode.d.ts

- ng test --code-coverage --watch=false
- ng test --sourcemaps=false --code-coverage --watch=false

after_success:
- ./node_modules/.bin/codecov
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ __Additional Settings__
* [linkfyEnabled]{boolean}: Transforms links within the messages to valid HTML links. Default is true.
* [audioEnabled]{boolean}: Enables audio notifications on received messages. Default is true.
* [audioSource]{string}: WAV source of the audio notification. Default is a RAW github WAV content from ng-chat repository.
* [persistWindowsState]{boolean}: Saves the state of current open windows on the local storage. Default is true.

#### Implement your ChatAdapter:

Expand Down
2 changes: 1 addition & 1 deletion demo/aspnetcore_signalr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"jquery": "3.2.1",
"ng-chat": "1.0.4",
"ng-chat": "1.0.5",
"ng2-loading-bar": "0.0.6",
"reflect-metadata": "^0.1.10",
"rxjs": "5.4.2",
Expand Down
2 changes: 1 addition & 1 deletion demo/offline_bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"core-js": "^2.5.3",
"rxjs": "^5.5.6",
"zone.js": "^0.8.20",
"ng-chat": "1.0.4"
"ng-chat": "1.0.5"
},
"devDependencies": {
"@angular/cli": "^1.6.4",
Expand Down
50 changes: 29 additions & 21 deletions src/ng-chat/assets/ng-chat.component.default.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@
max-width: 85%;
float:left;
}
.ng-chat-title > .ng-chat-user-status
{
float: left;
margin-left: 5px;
}
#ng-chat-search_friend
{
display: block;
Expand Down Expand Up @@ -129,27 +134,30 @@
#ng-chat-users li > .ng-chat-user-status
{
float: right;
border-radius: 25px;
width: 8px;
height: 8px;
margin-top:10px;
}
#ng-chat-users li > .ng-chat-user-status.online
{
background-color: #92A400;
}
#ng-chat-users li > .ng-chat-user-status.busy
{
background-color: #F91C1E;
}
#ng-chat-users li > .ng-chat-user-status.away
{
background-color: #F7D21B;
}
#ng-chat-users li > .ng-chat-user-status.offline
{
background-color: #BABABA;
}
.ng-chat-user-status
{
border-radius: 25px;
width: 8px;
height: 8px;
margin-top:10px;
}
.ng-chat-user-status.online
{
background-color: #92A400;
}
.ng-chat-user-status.busy
{
background-color: #F91C1E;
}
.ng-chat-user-status.away
{
background-color: #F7D21B;
}
.ng-chat-user-status.offline
{
background-color: #BABABA;
}
.ng-chat-unread-messages-count
{
background-color: #E3E3E3;
Expand Down Expand Up @@ -242,4 +250,4 @@
border: 3px solid #E3E3E3;
margin-top: 0;
margin-bottom: 5px;
}
}
2 changes: 2 additions & 0 deletions src/ng-chat/ng-chat.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<strong title="{{window.chattingTo.displayName}}">
{{window.chattingTo.displayName}}
</strong>
<span [ngClass]="{'ng-chat-user-status': true, 'online': window.chattingTo.status == UserStatus.Online, 'busy': window.chattingTo.status == UserStatus.Busy, 'away': window.chattingTo.status == UserStatus.Away, 'offline': window.chattingTo.status == UserStatus.Offline}" title="{{UserStatus[window.chattingTo.status]}}"></span>
<span *ngIf="unreadMessagesTotal(window).length > 0" class="ng-chat-unread-messages-count">{{unreadMessagesTotal(window)}}</span>
<a href="javascript:void(0);" class="ng-chat-close" (click)="onCloseChatWindow(window)">X</a>
</div>
Expand All @@ -32,6 +33,7 @@
<strong title="{{window.chattingTo.displayName}}">
{{window.chattingTo.displayName}}
</strong>
<span [ngClass]="{'ng-chat-user-status': true, 'online': window.chattingTo.status == UserStatus.Online, 'busy': window.chattingTo.status == UserStatus.Busy, 'away': window.chattingTo.status == UserStatus.Away, 'offline': window.chattingTo.status == UserStatus.Offline}" title="{{UserStatus[window.chattingTo.status]}}"></span>
<span *ngIf="unreadMessagesTotal(window).length > 0" class="ng-chat-unread-messages-count">{{unreadMessagesTotal(window)}}</span>
<a href="javascript:void(0);" class="ng-chat-close" (click)="onCloseChatWindow(window)">X</a>
</div>
Expand Down
63 changes: 58 additions & 5 deletions src/ng-chat/ng-chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,17 @@ export class NgChat implements OnInit {
@Input() // TODO: This might need a better content strategy
public audioSource: string = 'https://raw.githubusercontent.com/rpaschoal/ng-chat/master/src/ng-chat/assets/notification.wav';

@Input()
public persistWindowsState: boolean = true;

private audioFile: HTMLAudioElement;

public searchInput: string = '';

private users: User[];

private localStorageKey: string = "ng-chat-users";

get filteredUsers(): User[]
{
if (this.searchInput.length > 0){
Expand Down Expand Up @@ -125,13 +130,13 @@ export class NgChat implements OnInit {
// Loading current users list
if (this.pollFriendsList){
// Setting a long poll interval to update the friends list
this.fetchFriendsList();
setInterval(() => this.fetchFriendsList(), this.pollingInterval);
this.fetchFriendsList(true);
setInterval(() => this.fetchFriendsList(false), this.pollingInterval);
}
else
{
// Since polling was disabled, a friends list update mechanism will have to be implemented in the ChatAdapter.
this.fetchFriendsList();
this.fetchFriendsList(true);
}

this.bufferAudioFile();
Expand All @@ -152,12 +157,17 @@ export class NgChat implements OnInit {
}

// Sends a request to load the friends list
private fetchFriendsList(): void
private fetchFriendsList(isBootstrapping: boolean): void
{
this.adapter.listFriends()
.map((users: User[]) => {
this.users = users;
}).subscribe();
}).subscribe(() => {
if (isBootstrapping)
{
this.restoreWindowsState();
}
});
}

// Updates the friends list via the event handler
Expand Down Expand Up @@ -221,6 +231,8 @@ export class NgChat implements OnInit {
this.windows.pop();
}

this.updateWindowsState(this.windows);

return [newChatWindow, true];
}
else
Expand Down Expand Up @@ -270,6 +282,45 @@ export class NgChat implements OnInit {
}
}

// Saves current windows state into local storage if persistence is enabled
private updateWindowsState(windows: Window[]): void
{
if (this.persistWindowsState)
{
let usersIds = windows.map((w) => {
return w.chattingTo.id;
});

localStorage.setItem(this.localStorageKey, JSON.stringify(usersIds));
}
}

private restoreWindowsState(): void
{
try
{
if (this.persistWindowsState)
{
let stringfiedUserIds = localStorage.getItem(this.localStorageKey);

if (stringfiedUserIds && stringfiedUserIds.length > 0)
{
let userIds = <number[]>JSON.parse(stringfiedUserIds);

let usersToRestore = this.users.filter(u => userIds.indexOf(u.id) >= 0);

usersToRestore.forEach((user) => {
this.openChatWindow(user);
});
}
}
}
catch (ex)
{
console.log(`An error occurred while restoring ng-chat windows state. Details: ${ex}`);
}
}

// Returns the total unread messages from a chat window. TODO: Could use some Angular pipes in the future
unreadMessagesTotal(window: Window): string
{
Expand Down Expand Up @@ -321,6 +372,8 @@ export class NgChat implements OnInit {
let index = this.windows.indexOf(window);

this.windows.splice(index, 1);

this.updateWindowsState(this.windows);
}

// Toggle friends list visibility
Expand Down
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ng-chat",
"version": "1.0.4",
"version": "1.0.5",
"peerDependencies": {
"@angular/common": "*",
"@angular/core": "*",
Expand Down
108 changes: 107 additions & 1 deletion src/spec/ng-chat.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ describe('NgChat', () => {
expect(this.subject.audioSource).not.toBeUndefined();
});

it('Persistent windows state must be enabled by default', () => {
expect(this.subject.persistWindowsState).toBeTruthy();
});

it('Exercise users filter', () => {
this.subject.users = [{
id: 1,
Expand Down Expand Up @@ -114,11 +118,29 @@ describe('NgChat', () => {
it('Must invoke adapter on fetchFriendsList', () => {
spyOn(MockableAdapter.prototype, 'listFriends').and.returnValue(Observable.of([]));

this.subject.fetchFriendsList();
this.subject.fetchFriendsList(false);

expect(MockableAdapter.prototype.listFriends).toHaveBeenCalledTimes(1);
});

it('Must invoke restore windows state on fetchFriendsList when bootstrapping', () => {
spyOn(MockableAdapter.prototype, 'listFriends').and.returnValue(Observable.of([]));
spyOn(this.subject, 'restoreWindowsState');

this.subject.fetchFriendsList(true);

expect(this.subject.restoreWindowsState).toHaveBeenCalledTimes(1);
});

it('Must not invoke restore windows state on fetchFriendsList when not bootstrapping', () => {
spyOn(MockableAdapter.prototype, 'listFriends').and.returnValue(Observable.of([]));
spyOn(this.subject, 'restoreWindowsState');

this.subject.fetchFriendsList(false);

expect(this.subject.restoreWindowsState).not.toHaveBeenCalled();
});

it('Must update users property when onFriendsListChanged is invoked', () => {
expect(this.subject.users).toBeUndefined();

Expand Down Expand Up @@ -262,4 +284,88 @@ describe('NgChat', () => {

expect(this.subject.emitMessageSound).toHaveBeenCalledTimes(1);
});

it('Should not use local storage persistency if persistWindowsState is disabled', () => {
let windows = [new Window()];

this.subject.persistWindowsState = false;

spyOn(localStorage, 'setItem');

this.subject.updateWindowsState(windows);

expect(localStorage.setItem).not.toHaveBeenCalled();
});

it('Update windows state exercise', () => {
let persistedValue = null;

spyOn(localStorage, 'setItem').and.callFake((key, value) =>{
persistedValue = value;
});

let firstUser = new User();
let secondUser = new User();

firstUser.id = 88;
secondUser.id = 99;

let firstWindow = new Window();
let secondWindow = new Window();

firstWindow.chattingTo = firstUser;
secondWindow.chattingTo = secondUser;

let windows = [firstWindow, secondWindow];

this.subject.updateWindowsState(windows);

expect(localStorage.setItem).toHaveBeenCalledTimes(1);
expect(persistedValue).toBe(JSON.stringify([88, 99]));
});

it('Should not restore windows state from local storage if persistWindowsState is disabled', () => {
this.subject.persistWindowsState = false;

spyOn(this.subject, 'openChatWindow');

this.subject.restoreWindowsState();

expect(this.subject.openChatWindow).not.toHaveBeenCalled();
});

it('Restore windows state exercise', () => {
let firstUser = new User();
let secondUser = new User();

firstUser.id = 88;
secondUser.id = 99;

localStorage.setItem(this.subject.localStorageKey, JSON.stringify([firstUser.id, secondUser.id]));

this.subject.users = [firstUser, secondUser];
let pushedUsers = [];

spyOn(this.subject, 'openChatWindow').and.callFake((user) => {
pushedUsers.push(user);
});

this.subject.restoreWindowsState();

expect(this.subject.openChatWindow).toHaveBeenCalledTimes(2);
expect(pushedUsers).not.toBeNull();
expect(pushedUsers.length).toBe(2);
expect(pushedUsers[0]).toBe(firstUser);
expect(pushedUsers[1]).toBe(secondUser);
});

it('Must invoke window state update when closing a chat window', () => {
this.subject.windows = [new Window()];

spyOn(this.subject, 'updateWindowsState');

let result = this.subject.onCloseChatWindow(this.subject.windows[0]);

expect(this.subject.updateWindowsState).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 625748d

Please sign in to comment.