Added the basic comment functionality

pull/4083/head
tidusjar 4 years ago
parent 6683f8070c
commit f777e5d171

@ -74,6 +74,10 @@ export class AuthService extends ServiceHelpers {
return false; return false;
} }
public isAdmin() {
return this.hasRole("Admin") || this.hasRole("PowerUser");
}
public logout() { public logout() {
this.store.remove("id_token"); this.store.remove("id_token");
} }

@ -1,6 +1,5 @@
<h1 mat-dialog-title>Issues for {{data.title}}</h1> <h1 mat-dialog-title>Issues for {{data.title}}</h1>
<div mat-dialog-content> <div mat-dialog-content>
<div class="row"> <div class="row">
<div class="col-6 top-spacing" *ngFor="let issue of data.issues"> <div class="col-6 top-spacing" *ngFor="let issue of data.issues">
<mat-card color="accent"> <mat-card color="accent">

@ -3,6 +3,7 @@
<h1>Issues for {{details.title}}</h1> <h1>Issues for {{details.title}}</h1>
<div> <div>
<span>Has Request {{hasRequest}}</span> <span>Has Request {{hasRequest}}</span>
<div class="row"> <div class="row">
<div class="col-6 top-spacing" *ngFor="let issue of details.issues"> <div class="col-6 top-spacing" *ngFor="let issue of details.issues">
<div color="accent"> <div color="accent">
@ -16,7 +17,7 @@
</p> </p>
</div> </div>
<div> <div>
<button mat-raised-button color="accent">{{'Issues.Chat' | translate }}</button> <button mat-raised-button (click)="openChat(issue)" color="accent">{{'Issues.Chat' | translate }}</button>
<span *ngIf="isAdmin && settings"> <span *ngIf="isAdmin && settings">
<button mat-raised-button color="accent" <button mat-raised-button color="accent"
*ngIf="issue.status === IssueStatus.Pending && settings.enableInProgress" *ngIf="issue.status === IssueStatus.Pending && settings.enableInProgress"
@ -32,7 +33,7 @@
</div> </div>
</div> </div>
</div> </div>
<div> <div class="row">
<button mat-raised-button color="accent" (click)="navToMedia()">{{'Common.ViewDetails' | translate }}</button> <button mat-raised-button color="accent" (click)="navToMedia()">{{'Common.ViewDetails' | translate }}</button>
</div> </div>
</div> </div>

@ -1,11 +1,12 @@
import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core"; import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ActivatedRoute, ActivatedRouteSnapshot, Router } from "@angular/router"; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from "@angular/router";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { IIssues, IIssueSettings, IIssuesSummary, IssueStatus, RequestType } from "../../../interfaces"; import { IIssues, IIssueSettings, IIssuesSummary, IssueStatus, RequestType } from "../../../interfaces";
import { IssuesService, NotificationService, SettingsService } from "../../../services"; import { IssuesService, NotificationService, SettingsService } from "../../../services";
import { IssuesV2Service } from "../../../services/issuesv2.service"; import { IssuesV2Service } from "../../../services/issuesv2.service";
import { IssueChatComponent } from "../issue-chat/issue-chat.component";
export interface IssuesDetailsGroupData { export interface IssuesDetailsGroupData {
@ -33,7 +34,8 @@ export class IssuesDetailsComponent implements OnInit {
constructor(private authService: AuthService, private settingsService: SettingsService, constructor(private authService: AuthService, private settingsService: SettingsService,
private issueServiceV2: IssuesV2Service, private route: ActivatedRoute, private router: Router, private issueServiceV2: IssuesV2Service, private route: ActivatedRoute, private router: Router,
private issuesService: IssuesService, private translateService: TranslateService, private notificationService: NotificationService) { private issuesService: IssuesService, private translateService: TranslateService, private notificationService: NotificationService,
private dialog: MatDialog) {
this.route.params.subscribe(async (params: any) => { this.route.params.subscribe(async (params: any) => {
if (typeof params.providerId === 'string' || params.providerId instanceof String) { if (typeof params.providerId === 'string' || params.providerId instanceof String) {
this.providerId = params.providerId; this.providerId = params.providerId;
@ -67,6 +69,9 @@ export class IssuesDetailsComponent implements OnInit {
this.details.issues = this.details.issues.filter((el) => { return el.id !== issue.id; }); this.details.issues = this.details.issues.filter((el) => { return el.id !== issue.id; });
} }
public openChat(issue: IIssues) {
this.dialog.open(IssueChatComponent, { width: "80vh", data: { issueId: issue.id }, panelClass: 'modal-panel' })
}
public navToMedia() { public navToMedia() {
const firstIssue = this.details.issues[0]; const firstIssue = this.details.issues[0];

@ -1,15 +1,17 @@
import { AuthGuard } from "../../auth/auth.guard";
import { Routes } from "@angular/router";
import { IssuesV2Service } from "../../services/issuesv2.service"; import { IssuesV2Service } from "../../services/issuesv2.service";
import { IdentityService, SearchService } from "../../services"; import { IdentityService, SearchService } from "../../services";
import { DetailsGroupComponent } from "./details-group/details-group.component"; import { DetailsGroupComponent } from "./details-group/details-group.component";
import { IssuesDetailsComponent } from "./details/details.component"; import { IssuesDetailsComponent } from "./details/details.component";
import { IssueChatComponent } from "./issue-chat/issue-chat.component";
import { ChatBoxComponent } from "../../shared/chat-box/chat-box.component";
export const components: any[] = [ export const components: any[] = [
DetailsGroupComponent, DetailsGroupComponent,
IssuesDetailsComponent, IssuesDetailsComponent,
IssueChatComponent,
ChatBoxComponent,
]; ];
export const providers: any[] = [ export const providers: any[] = [

@ -0,0 +1,6 @@
<ombi-chat-box *ngIf="loaded"
[messages]="messages"
(onAddMessage)="addComment($event)"
>
</ombi-chat-box>

@ -0,0 +1,9 @@
@import "~styles/variables.scss";
::ng-deep .mat-card {
background: $ombi-background-primary-accent;
}
.top-spacing {
margin-top:2%;
}

@ -0,0 +1,87 @@
import { Component, Inject, OnInit } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { AuthService } from "../../../auth/auth.service";
import { ILocalUser } from "../../../auth/IUserLogin";
import { IIssuesChat, IIssueSettings, IssueStatus } from "../../../interfaces";
import { IssuesService, SettingsService } from "../../../services";
import { ChatMessages, ChatType } from "../../../shared/chat-box/chat-box.component";
export interface ChatData {
issueId: number;
title: string;
}
@Component({
selector: "issue-chat",
templateUrl: "issue-chat.component.html",
styleUrls: ["issue-chat.component.scss"],
})
export class IssueChatComponent implements OnInit {
public isAdmin: boolean;
public comments: IIssuesChat[];
public IssueStatus = IssueStatus;
public settings: IIssueSettings;
public messages: ChatMessages[] = [];
public loaded: boolean;
private user: ILocalUser;
constructor(public dialogRef: MatDialogRef<IssueChatComponent>,
@Inject(MAT_DIALOG_DATA) public data: ChatData,
private authService: AuthService, private settingsService: SettingsService,
private issueService: IssuesService) { }
public ngOnInit() {
this.isAdmin = this.authService.isAdmin();
this.user = this.authService.claims();
this.settingsService.getIssueSettings().subscribe(x => this.settings = x);
this.issueService.getComments(this.data.issueId).subscribe(x => {
this.comments = x;
this.mapMessages();
this.loaded = true;
});
}
public deleteComment(commentId: number) {
}
public addComment(comment: string) {
this.issueService.addComment({
comment: comment,
issueId: this.data.issueId
}).subscribe(comment => {
this.messages.push({
chatType: ChatType.Sender,
date: comment.date,
id: -1,
message: comment.comment,
username: comment.user.userName
});
});
}
public close() {
this.dialogRef.close();
}
private mapMessages() {
this.comments.forEach((m: IIssuesChat) => {
this.messages.push({
chatType: m.username === this.user.name ? ChatType.Sender : ChatType.Reciever,
date: m.date,
id: m.id,
message: m.comment,
username: m.username
});
});
}
}

@ -6,7 +6,7 @@ import { OrderModule } from "ngx-order-pipe";
import { AuthGuard } from "../auth/auth.guard"; import { AuthGuard } from "../auth/auth.guard";
import { SharedModule as OmbiShared } from "../shared/shared.module"; import { SharedModule } from "../shared/shared.module";
import { IssueDetailsComponent } from "./issueDetails.component"; import { IssueDetailsComponent } from "./issueDetails.component";
import { IssuesComponent } from "./issues.component"; import { IssuesComponent } from "./issues.component";
@ -27,7 +27,7 @@ const routes: Routes = [
RouterModule.forChild(routes), RouterModule.forChild(routes),
OrderModule, OrderModule,
PipeModule, PipeModule,
OmbiShared, SharedModule,
// NbChatModule, // NbChatModule,
], ],
declarations: [ declarations: [

@ -0,0 +1,23 @@
<div class='container' *ngIf="messages.length > 0">
<div class='chatbox'>
<div class='chatbox__user-list'>
<h1>Users</h1>
<div class='chatbox__user--active' *ngFor="let user of userList">
<p>{{user}}</p>
</div>
</div>
<div class="chatbox__messages" *ngFor="let m of messages">
<div class="chatbox__messages__user-message">
<div class="chatbox__messages__user-message--ind-message" [ngClass]="{'sender': m.chatType === ChatType.Sender, 'reciever':m.chatType === ChatType.Reciever }">
<p class="name" *ngIf="m?.username">{{m.username}}</p>
<br/>
<p class="message">{{m.message}}</p>
<p class="timestamp">{{m.date | amLocal | amDateFormat: 'l LT'}}</p>
</div>
</div>
</div>
<div class="form">
<input type="text" [(ngModel)]="currentMessage" placeholder="Enter your message">
<button mat-raised-button class="add-message" (click)="addMessage()">Send</button>
</div>
</div>

@ -0,0 +1,329 @@
// Variables
$primary: rgba(23, 190, 187, 1);
$secondary: rgba(240, 166, 202, 1);
$active: rgba(23, 190, 187, 0.8);
$busy: rgba(252, 100, 113, 0.8);
$away: rgba(255, 253, 130, 0.8);
// Triangle Mixin
@mixin triangle($color, $size, $direction) {
width: 0;
height: 0;
@if $direction == "up" {
border-right: ($size + px) solid transparent;
border-left: ($size + px) solid transparent;
border-bottom: ($size + px) solid $color;
}
@if $direction == "down" {
border-right: ($size + px) solid transparent;
border-left: ($size + px) solid transparent;
border-top: ($size + px) solid $color;
}
@if $direction == "right" {
border-top: ($size + px) solid transparent;
border-bottom: ($size + px) solid transparent;
border-left: ($size + px) solid $color;
}
@if $direction == "left" {
border-top: ($size + px) solid transparent;
border-bottom: ($size + px) solid transparent;
border-right: ($size + px) solid $color;
}
}
* {
margin: 0; padding: 0;
box-sizing: border-box;
font-family: 'Nunito', sans-serif;
}
html,body {
background: linear-gradient(120deg, $primary, $secondary);
overflow: hidden;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 70vh;
h1 {
margin: 0.5em auto;
color: #FFF;
text-align: center;
}
}
.chatbox {
background: rgba(255, 255, 255, 0.05);
width: 600px;
height: 75%;
border-radius: 0.2em;
position: relative;
box-shadow: 1px 1px 12px rgba(0, 0, 0, 0.1);
.sender {
float: right;
&:after {
content: '';
position: absolute;
margin: -1.5em -18.98em;
@include triangle(rgba(255, 255, 255, 0.2), 10, left);
}
}
.reciever {
float: left;
&:after {
content: '';
position: absolute;
margin: -1.5em 2.65em;
@include triangle(rgba(255, 255, 255, 0.2), 10, right);
}
}
&__messages__user-message {
width: 450px;
}
&__messages__user-message--ind-message {
background: rgba(255, 255, 255, 0.2);
padding: 1em 0;
height: auto;
width: 65%;
border-radius: 5px;
margin: 2em 1em;
overflow: auto;
& > p.name {
color: #FFF;
font-size: 1em;
}
& > p.message {
color: #FFF;
font-size: 0.7em;
margin: 0 2.8em;
}& > p.timestamp {
color: #FFF;
font-size: 0.7em;
margin: 0 2.8em;
}
}
&__user-list {
background: rgba(255, 255, 255, 0.1);
width: 25%;
height: 100%;
float: right;
border-top-right-radius: 0.2em;
border-bottom-right-radius: 0.2em;
h1 {
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.9);
font-size: 0.9em;
padding: 1em;
margin: 0;
font-weight: 300;
text-align: center;
}
}
&__user {
width: 0.5em;
height: 0.5em;
border-radius: 100%;
margin: 1em 0.7em;
&--active {
@extend .chatbox__user;
background: $active;
}
&--busy {
@extend .chatbox__user;
background: $busy;
}
&--away {
@extend .chatbox__user;
background: $away;
}
}
p {
float: left;
text-align: left;
margin: -0.25em 2em;
font-size: 0.7em;
font-weight: 300;
color: #FFF;
width: 200px;
}
.form {
background: #222;
input {
background: rgba(255, 255, 255, 0.03);
position: absolute;
bottom: 0;
left: 0;
border: none;
width: 75%;
padding: 1.2em;
outline: none;
color: rgba(255, 255, 255, 0.9);
font-weight: 300;
}
.add-message {
background: rgba(255, 255, 255, 0.03);
position: absolute;
bottom: 1.5%;
right: 26%;
border: none;
outline: none;
color: rgba(255, 255, 255, 0.9);
font-weight: 300;
}
}
}
// Placeholder Styling
::-webkit-input-placeholder {
color: rgba(255, 255, 255, 0.9);
}
:-moz-placeholder {
color: rgba(255, 255, 255, 0.9);
}
::-moz-placeholder {
color: rgba(255, 255, 255, 0.9);
}
:-ms-input-placeholder {
color: rgba(255, 255, 255, 0.9);
}
// ::-webkit-scrollbar {
// width: 4px;
// }
// ::-webkit-scrollbar-thumb {
// background-color: #4c4c6a;
// border-radius: 2px;
// }
// .chatbox {
// width: 300px;
// height: 400px;
// max-height: 400px;
// display: flex;
// flex-direction: column;
// overflow: hidden;
// box-shadow: 0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);
// }
// .chat-window {
// flex: auto;
// max-height: calc(100% - 60px);
// background: #2f323b;
// overflow: auto;
// }
// .chat-input {
// flex: 0 0 auto;
// height: 60px;
// background: #40434e;
// border-top: 1px solid #2671ff;
// box-shadow: 0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);
// }
// .chat-input input {
// height: 59px;
// line-height: 60px;
// outline: 0 none;
// border: none;
// width: calc(100% - 60px);
// color: white;
// text-indent: 10px;
// font-size: 12pt;
// padding: 0;
// background: #40434e;
// }
// .chat-input button {
// float: right;
// outline: 0 none;
// border: none;
// background: rgba(255,255,255,.25);
// height: 40px;
// width: 40px;
// border-radius: 50%;
// padding: 2px 0 0 0;
// margin: 10px;
// transition: all 0.15s ease-in-out;
// }
// .chat-input input[good] + button {
// box-shadow: 0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
// background: #2671ff;
// }
// .chat-input input[good] + button:hover {
// box-shadow: 0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
// }
// .chat-input input[good] + button path {
// fill: white;
// }
// .msg-container {
// position: relative;
// display: inline-block;
// width: 100%;
// margin: 0 0 10px 0;
// padding: 0;
// }
// .msg-box {
// display: flex;
// background: #5b5e6c;
// padding: 10px 10px 0 10px;
// border-radius: 0 6px 6px 0;
// max-width: 80%;
// width: auto;
// float: left;
// box-shadow: 0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
// }
// .user-img {
// display: inline-block;
// border-radius: 50%;
// height: 40px;
// width: 40px;
// background: #2671ff;
// margin: 0 10px 10px 0;
// }
// .flr {
// flex: 1 0 auto;
// display: flex;
// flex-direction: column;
// width: calc(100% - 50px);
// }
// .messages {
// flex: 1 0 auto;
// }
// .msg {
// display: inline-block;
// font-size: 11pt;
// line-height: 13pt;
// color: rgba(255,255,255,.7);
// margin: 0 0 4px 0;
// }
// .msg:first-of-type {
// margin-top: 8px;
// }
// .timestamp {
// color: rgba(0,0,0,.38);
// font-size: 8pt;
// margin-bottom: 10px;
// }
// .username {
// margin-right: 3px;
// }
// .posttime {
// margin-left: 3px;
// }
// .msg-self .msg-box {
// border-radius: 6px 0 0 6px;
// background: #2671ff;
// float: right;
// }
// .msg-self .user-img {
// margin: 0 0 10px 10px;
// }
// .msg-self .msg {
// text-align: right;
// }
// .msg-self .timestamp {
// text-align: right;
// }

@ -0,0 +1,46 @@
import { AfterContentChecked, AfterViewInit, Component, EventEmitter, Inject, Input, OnInit, Output } from "@angular/core";
export interface ChatMessages {
id: number;
message: string;
date: Date;
username: string;
chatType: ChatType;
}
export enum ChatType {
Sender,
Reciever
}
@Component({
selector: "ombi-chat-box",
templateUrl: "chat-box.component.html",
styleUrls: ["chat-box.component.scss"],
})
export class ChatBoxComponent implements OnInit {
@Input() messages: ChatMessages[];
@Output() onAddMessage: EventEmitter<string> = new EventEmitter<string>();
@Output() onDeleteMessage: EventEmitter<number> = new EventEmitter<number>();
public currentMessage: string;
public userList: string[];
public ChatType = ChatType;
public ngOnInit(): void {
const allUsernames = this.messages.map(x => x.username);
this.userList = allUsernames.filter((v, i, a) => a.indexOf(v) === i);
}
public deleteMessage(id: number) {
this.onDeleteMessage.emit(id);
}
public addMessage() {
if (this.currentMessage) {
this.onAddMessage.emit(this.currentMessage);
this.currentMessage = '';
}
}
}
Loading…
Cancel
Save