diff --git a/src/Ombi/ClientApp/src/app/auth/auth.service.ts b/src/Ombi/ClientApp/src/app/auth/auth.service.ts index 3eace43a4..afc0a2491 100644 --- a/src/Ombi/ClientApp/src/app/auth/auth.service.ts +++ b/src/Ombi/ClientApp/src/app/auth/auth.service.ts @@ -74,6 +74,10 @@ export class AuthService extends ServiceHelpers { return false; } + public isAdmin() { + return this.hasRole("Admin") || this.hasRole("PowerUser"); + } + public logout() { this.store.remove("id_token"); } diff --git a/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html b/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html index 7c3c04fdd..9738e24c7 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html +++ b/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html @@ -1,6 +1,5 @@

Issues for {{data.title}}

-
diff --git a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html index b8e8dc1f6..08cb2dc44 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html +++ b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html @@ -3,6 +3,7 @@

Issues for {{details.title}}

Has Request {{hasRequest}} +
@@ -16,7 +17,7 @@

- +
-
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts index ed00bc041..accc99e60 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts +++ b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts @@ -1,11 +1,12 @@ 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 { TranslateService } from "@ngx-translate/core"; import { AuthService } from "../../../auth/auth.service"; import { IIssues, IIssueSettings, IIssuesSummary, IssueStatus, RequestType } from "../../../interfaces"; import { IssuesService, NotificationService, SettingsService } from "../../../services"; import { IssuesV2Service } from "../../../services/issuesv2.service"; +import { IssueChatComponent } from "../issue-chat/issue-chat.component"; export interface IssuesDetailsGroupData { @@ -33,7 +34,8 @@ export class IssuesDetailsComponent implements OnInit { constructor(private authService: AuthService, private settingsService: SettingsService, 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) => { if (typeof params.providerId === 'string' || params.providerId instanceof String) { 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; }); } + public openChat(issue: IIssues) { + this.dialog.open(IssueChatComponent, { width: "80vh", data: { issueId: issue.id }, panelClass: 'modal-panel' }) + } public navToMedia() { const firstIssue = this.details.issues[0]; diff --git a/src/Ombi/ClientApp/src/app/issues/components/index.ts b/src/Ombi/ClientApp/src/app/issues/components/index.ts index 86ceefe89..99a07492b 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/index.ts +++ b/src/Ombi/ClientApp/src/app/issues/components/index.ts @@ -1,15 +1,17 @@ -import { AuthGuard } from "../../auth/auth.guard"; -import { Routes } from "@angular/router"; import { IssuesV2Service } from "../../services/issuesv2.service"; import { IdentityService, SearchService } from "../../services"; import { DetailsGroupComponent } from "./details-group/details-group.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[] = [ DetailsGroupComponent, IssuesDetailsComponent, + IssueChatComponent, + ChatBoxComponent, ]; export const providers: any[] = [ diff --git a/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.html b/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.html new file mode 100644 index 000000000..fe6c13ce7 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.html @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.scss b/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.scss new file mode 100644 index 000000000..d6dcd67de --- /dev/null +++ b/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.scss @@ -0,0 +1,9 @@ +@import "~styles/variables.scss"; + +::ng-deep .mat-card { + background: $ombi-background-primary-accent; +} + +.top-spacing { + margin-top:2%; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.ts b/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.ts new file mode 100644 index 000000000..a1ecf27f3 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/issues/components/issue-chat/issue-chat.component.ts @@ -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, + @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 + }); + }); + } + + +} diff --git a/src/Ombi/ClientApp/src/app/issues/issues.module.ts b/src/Ombi/ClientApp/src/app/issues/issues.module.ts index 08896a086..4bf232f25 100644 --- a/src/Ombi/ClientApp/src/app/issues/issues.module.ts +++ b/src/Ombi/ClientApp/src/app/issues/issues.module.ts @@ -6,7 +6,7 @@ import { OrderModule } from "ngx-order-pipe"; 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 { IssuesComponent } from "./issues.component"; @@ -27,7 +27,7 @@ const routes: Routes = [ RouterModule.forChild(routes), OrderModule, PipeModule, - OmbiShared, + SharedModule, // NbChatModule, ], declarations: [ diff --git a/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html new file mode 100644 index 000000000..85463dea1 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html @@ -0,0 +1,23 @@ +
+
+
+

Users

+
+

{{user}}

+
+
+
+
+
+

{{m.username}}

+
+

{{m.message}}

+

{{m.date | amLocal | amDateFormat: 'l LT'}}

+
+
+
+
+ + +
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.scss b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.scss new file mode 100644 index 000000000..d64de471b --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.scss @@ -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; +// } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.ts b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.ts new file mode 100644 index 000000000..697977563 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.ts @@ -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 = new EventEmitter(); + @Output() onDeleteMessage: EventEmitter = new EventEmitter(); + + 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 = ''; + } + } +}