diff --git a/public/Onest-SemiBold.ttf b/public/Onest-SemiBold.ttf new file mode 100644 index 0000000..c7e8a3d Binary files /dev/null and b/public/Onest-SemiBold.ttf differ diff --git a/public/i18n/en.json b/public/i18n/en.json index e41de95..293fdee 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -16,5 +16,17 @@ "passwordRequired": "Password is required", "passwordIncorrect": "Password is incorrect" } + }, + "chat": { + "chatnav": { + "dmList": { + "newChat": "Start new chat", + "messageBox": { + "latestMessage": { + "you": "You: " + } + } + } + } } } diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index efb39d8..29b9d27 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,8 +1,13 @@ -import { Routes } from '@angular/router'; +import {Routes} from '@angular/router'; import {SignIn} from './signin/signin'; import {Chat} from './chat/chat'; +import {Dm} from './chat/dm/dm'; export const routes: Routes = [ - { path: 'signin', component: SignIn }, - { path: 'chat', component: Chat }, + {path: 'signin', component: SignIn}, + { + path: 'chat', component: Chat, children: [ + {path: 'dm/:chatid', component: Dm}, + ] + }, ]; diff --git a/src/app/chat/chat.html b/src/app/chat/chat.html index 903faf6..083e3a7 100644 --- a/src/app/chat/chat.html +++ b/src/app/chat/chat.html @@ -3,7 +3,7 @@ } @else { - + diff --git a/src/app/chat/chat.scss b/src/app/chat/chat.scss index a314cdb..9ef4735 100644 --- a/src/app/chat/chat.scss +++ b/src/app/chat/chat.scss @@ -1,11 +1,11 @@ #layout { display: grid; - grid-template-columns: 20% 80%; + grid-template-columns: 350px minmax(0, 1fr); height: 100svh; #chatnav { display: grid; - grid-template-columns: 25% 75%; + grid-template-columns: 70px minmax(0, 1fr); aside { padding: 15px; @@ -36,18 +36,22 @@ } main { - padding: 25px; + padding-top: 65px; + background: var(--tui-background-base-alt); + border-radius: 20px 0 0 20px; + margin: 10px 0 10px 10px; + padding: 15px; } } #content { width: 100%; - padding: 10px; + padding: 10px 10px 10px 0; #content_tint { + border-radius: 0 20px 20px 0; height: 100%; - border-radius: 20px; - background: var(--tui-background-base-alt); + background: var(--tui-background-neutral-2); padding: 15px; } } diff --git a/src/app/chat/chat.ts b/src/app/chat/chat.ts index 3184b5e..275f7e5 100644 --- a/src/app/chat/chat.ts +++ b/src/app/chat/chat.ts @@ -1,9 +1,9 @@ import {Component, inject, OnInit} from '@angular/core'; import {RouterOutlet} from '@angular/router'; import {TuiSegmented} from '@taiga-ui/kit'; -import {TuiButton, TuiIcon, TuiLoader} from '@taiga-ui/core'; +import {TuiAppearance, TuiButton, TuiGroup, TuiIcon, TuiLoader} from '@taiga-ui/core'; import {SessionManager} from '@chatenium/chatenium-sdk/services/sessionManager'; -import {ServiceManager} from '../service-manager'; +import {LoadStatus, ServiceManager} from '../service-manager'; import {IndexedDB} from '../storage/indexed-db'; import {DmList} from './dm-list/dm-list'; import {JsonPipe} from '@angular/common'; @@ -17,7 +17,9 @@ import {JsonPipe} from '@angular/common'; TuiButton, TuiLoader, DmList, - JsonPipe + JsonPipe, + TuiAppearance, + TuiGroup ], templateUrl: './chat.html', styleUrl: './chat.scss', @@ -31,4 +33,6 @@ export class Chat implements OnInit { this.serviceManager.currentSession.set(await this.serviceManager.sessionManager.loadPreferredSession()) }) } + + protected readonly LoadStatus = LoadStatus; } diff --git a/src/app/chat/dm-list/dm-list.html b/src/app/chat/dm-list/dm-list.html index de3073c..06ae07a 100644 --- a/src/app/chat/dm-list/dm-list.html +++ b/src/app/chat/dm-list/dm-list.html @@ -1,3 +1,24 @@ -@for (chat of chats(); track chat.chatid) { - {{chat.chatid}} + + {{"chat.chatnav.dmList.newChat"|translate}} + + +@for (chat of serviceManager.chats(); track chat.chatid) { + + + + @if (chat.displayName == "") { + {{'@'+chat.username}} + } @else { + {{chat.displayName}} + } + @if (chat.latestMessage) { + + @if (chat.latestMessage.isAuthor) { + {{"chat.chatnav.dmList.messageBox.latestMessage.you"|translate}} + } + {{chat.latestMessage.message}} + + } + + } diff --git a/src/app/chat/dm-list/dm-list.scss b/src/app/chat/dm-list/dm-list.scss index e69de29..c04f676 100644 --- a/src/app/chat/dm-list/dm-list.scss +++ b/src/app/chat/dm-list/dm-list.scss @@ -0,0 +1,24 @@ +:host { + display: flex; + flex-direction: column; + gap: 5px; + + button { + width: 100%; + display: flex; + justify-content: start; + font-weight: 600; + + .info { + display: flex; + flex-direction: column; + text-align: start; + + .latest_message { + margin-top: -5px; + font-size: 12px; + opacity: 50%; + } + } + } +} diff --git a/src/app/chat/dm-list/dm-list.ts b/src/app/chat/dm-list/dm-list.ts index d6d0c05..43a4191 100644 --- a/src/app/chat/dm-list/dm-list.ts +++ b/src/app/chat/dm-list/dm-list.ts @@ -2,12 +2,19 @@ import {Component, inject, input, OnInit, signal} from '@angular/core'; import {ChatService} from '@chatenium/chatenium-sdk/services/chatService'; import {IndexedDB} from '../../storage/indexed-db'; import {Chat} from '@chatenium/chatenium-sdk/domain/chatService.schema'; -import {JsonPipe} from '@angular/common'; +import {TuiButton} from '@taiga-ui/core'; +import {Oimg} from '../elements/oimg/oimg'; +import {Router, RouterLink} from '@angular/router'; +import {TranslatePipe} from '@ngx-translate/core'; +import {LoadStatus, ServiceManager} from '../../service-manager'; @Component({ selector: 'app-dm-list', imports: [ - JsonPipe + TuiButton, + Oimg, + RouterLink, + TranslatePipe ], templateUrl: './dm-list.html', styleUrl: './dm-list.scss', @@ -17,19 +24,17 @@ export class DmList implements OnInit { token = input("") indexedDb = inject(IndexedDB) - - service: ChatService | null = null - chats = signal([]) - chatsStatus = 0 + router = inject(Router) + serviceManager = inject(ServiceManager) async ngOnInit() { - this.service = new ChatService(this.userid(), this.token(), this.indexedDb.getApi(), () => {}) + this.serviceManager.chatService = new ChatService(this.userid(), this.token(), this.indexedDb.getApi(), () => {}) try { - this.chats.set(await this.service.get()) - this.chatsStatus = 1 + this.serviceManager.chats.set(await this.serviceManager.chatService.get()) + this.serviceManager.chatsStatus.set(LoadStatus.loaded) } catch (e) { console.error(e) - this.chatsStatus = 2 + this.serviceManager.chatsStatus.set(LoadStatus.error) } } } diff --git a/src/app/chat/dm/dm.html b/src/app/chat/dm/dm.html new file mode 100644 index 0000000..8745cd8 --- /dev/null +++ b/src/app/chat/dm/dm.html @@ -0,0 +1,27 @@ +@defer (when store) { + + + + + @if (store.chatData.displayName == "") { + {{'@'+store.chatData.username}} + } @else { + {{store.chatData.displayName}} + {{'@'+store.chatData.username}} + } + + + + + + + + + + + + + + + +} diff --git a/src/app/chat/dm/dm.scss b/src/app/chat/dm/dm.scss new file mode 100644 index 0000000..970c1fd --- /dev/null +++ b/src/app/chat/dm/dm.scss @@ -0,0 +1,33 @@ +:host { + height: 100%; + display: grid; + grid-template-rows: 70px minmax(0, 1fr) auto; + + navbar { + .chat-data { + display: flex; + flex-direction: column; + + .main-name { + font-size: 18px; + font-weight: bold; + } + + .alt-name { + margin-top: -5px; + color: gray; + font-size: 12px; + } + } + + .items-right { + margin-top: -10px; + + button { + width: 35px; + height: 35px; + } + } + } + +} diff --git a/src/app/chat/dm/dm.spec.ts b/src/app/chat/dm/dm.spec.ts new file mode 100644 index 0000000..2ddf08a --- /dev/null +++ b/src/app/chat/dm/dm.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Dm } from './dm'; + +describe('Dm', () => { + let component: Dm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Dm], + }).compileComponents(); + + fixture = TestBed.createComponent(Dm); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat/dm/dm.ts b/src/app/chat/dm/dm.ts new file mode 100644 index 0000000..a852efa --- /dev/null +++ b/src/app/chat/dm/dm.ts @@ -0,0 +1,56 @@ +import {Component, inject, OnInit} from '@angular/core'; +import {DmStorage, ServiceManager} from '../../service-manager'; +import {ActivatedRoute} from '@angular/router'; +import {DMService} from '@chatenium/chatenium-sdk/services/dmService'; +import {IndexedDB} from '../../storage/indexed-db'; +import {Navbar} from '../elements/navbar/navbar'; +import {Oimg} from '../elements/oimg/oimg'; +import {TuiButton, TuiIcon} from '@taiga-ui/core'; +import {MessageBox} from '../elements/message-box/message-box'; + +@Component({ + selector: 'app-dm', + imports: [ + Navbar, + Oimg, + TuiButton, + TuiIcon, + MessageBox + ], + templateUrl: './dm.html', + styleUrl: './dm.scss', +}) +export class Dm implements OnInit { + serviceManager = inject(ServiceManager) + route = inject(ActivatedRoute) + indexedDb = inject(IndexedDB) + + chatid = "" + + get store(): DmStorage { + return this.serviceManager.dmServices()[this.chatid] + } + + ngOnInit() { + this.route.params.subscribe(params => { + const chatid = params['chatid'] + this.chatid = chatid + console.log(this.serviceManager.chats()) + const session = this.serviceManager.currentSession(); + const chatData = this.serviceManager.chats().find(chat => chat.chatid == chatid) + + if (!this.serviceManager.dmServices()[chatid] && session != null && chatData != null) { + this.serviceManager.dmServices()[chatid] = { + service: new DMService( + session.userData.userid, + session.token, + chatid, + this.indexedDb.getApi(), + () => {} + ), + chatData: chatData + } + } + }) + } +} diff --git a/src/app/chat/elements/message-box/message-box.html b/src/app/chat/elements/message-box/message-box.html new file mode 100644 index 0000000..9dbb25a --- /dev/null +++ b/src/app/chat/elements/message-box/message-box.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/app/chat/elements/message-box/message-box.scss b/src/app/chat/elements/message-box/message-box.scss new file mode 100644 index 0000000..94b515d --- /dev/null +++ b/src/app/chat/elements/message-box/message-box.scss @@ -0,0 +1,29 @@ +:host { + width: 100%; + display: flex; + justify-content: center; + + #message-box { + width: 60%; + background: var(--tui-background-base-alt); + height: 75px; + border-radius: 200px; + border: 2px solid var(--tui-border-normal); + display: grid; + grid-template-columns: 10% 80% 10%; + align-items: center; + padding: 0 10px; + + .items-middle { + textarea { + width: 100%; + height: 100%; + background: transparent; + border: none; + outline: none; + resize: none; + color: var(--tui-text-01); + } + } + } +} diff --git a/src/app/chat/elements/message-box/message-box.spec.ts b/src/app/chat/elements/message-box/message-box.spec.ts new file mode 100644 index 0000000..2c25206 --- /dev/null +++ b/src/app/chat/elements/message-box/message-box.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MessageBox } from './message-box'; + +describe('MessageBox', () => { + let component: MessageBox; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MessageBox], + }).compileComponents(); + + fixture = TestBed.createComponent(MessageBox); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat/elements/message-box/message-box.ts b/src/app/chat/elements/message-box/message-box.ts new file mode 100644 index 0000000..f4d17a4 --- /dev/null +++ b/src/app/chat/elements/message-box/message-box.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import {TuiAppearance} from '@taiga-ui/core'; + +@Component({ + selector: 'message-box', + imports: [ + TuiAppearance + ], + templateUrl: './message-box.html', + styleUrl: './message-box.scss', +}) +export class MessageBox {} diff --git a/src/app/chat/elements/navbar/navbar.html b/src/app/chat/elements/navbar/navbar.html new file mode 100644 index 0000000..74b1ac3 --- /dev/null +++ b/src/app/chat/elements/navbar/navbar.html @@ -0,0 +1,3 @@ + + + diff --git a/src/app/chat/elements/navbar/navbar.scss b/src/app/chat/elements/navbar/navbar.scss new file mode 100644 index 0000000..d9bdd95 --- /dev/null +++ b/src/app/chat/elements/navbar/navbar.scss @@ -0,0 +1,15 @@ +nav { + display: grid; + grid-template-columns: 1fr 1fr; + + ::ng-deep .items-right, ::ng-deep .items-left { + display: flex; + gap: 10px; + align-items: center; + } + + ::ng-deep .items-right { + display: flex; + justify-content: end; + } +} diff --git a/src/app/chat/elements/navbar/navbar.spec.ts b/src/app/chat/elements/navbar/navbar.spec.ts new file mode 100644 index 0000000..ea91146 --- /dev/null +++ b/src/app/chat/elements/navbar/navbar.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Navbar } from './navbar'; + +describe('Navbar', () => { + let component: Navbar; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Navbar], + }).compileComponents(); + + fixture = TestBed.createComponent(Navbar); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat/elements/navbar/navbar.ts b/src/app/chat/elements/navbar/navbar.ts new file mode 100644 index 0000000..b4512cb --- /dev/null +++ b/src/app/chat/elements/navbar/navbar.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'navbar', + imports: [], + templateUrl: './navbar.html', + styleUrl: './navbar.scss', +}) +export class Navbar {} diff --git a/src/app/chat/elements/oimg/oimg.html b/src/app/chat/elements/oimg/oimg.html new file mode 100644 index 0000000..37bf80b --- /dev/null +++ b/src/app/chat/elements/oimg/oimg.html @@ -0,0 +1 @@ + diff --git a/src/app/chat/elements/oimg/oimg.scss b/src/app/chat/elements/oimg/oimg.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/chat/elements/oimg/oimg.spec.ts b/src/app/chat/elements/oimg/oimg.spec.ts new file mode 100644 index 0000000..a2b6932 --- /dev/null +++ b/src/app/chat/elements/oimg/oimg.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Oimg } from './oimg'; + +describe('Oimg', () => { + let component: Oimg; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Oimg], + }).compileComponents(); + + fixture = TestBed.createComponent(Oimg); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat/elements/oimg/oimg.ts b/src/app/chat/elements/oimg/oimg.ts new file mode 100644 index 0000000..91a4858 --- /dev/null +++ b/src/app/chat/elements/oimg/oimg.ts @@ -0,0 +1,18 @@ +import {Component, input, Input, signal} from '@angular/core'; + +@Component({ + selector: 'oimg', + imports: [], + templateUrl: './oimg.html', + styleUrl: './oimg.scss', + host: { + "[style.height]": "height()", + "[style.width]": "width()", + } +}) +export class Oimg { + height = input("") + width = input("") + src = input("") + radius = input(15) +} diff --git a/src/app/service-manager.ts b/src/app/service-manager.ts index c2cc209..63cfd8a 100644 --- a/src/app/service-manager.ts +++ b/src/app/service-manager.ts @@ -4,6 +4,9 @@ import {Keyring} from './storage/keyring'; import {KeyValue} from './storage/key-value'; import {SessionManager} from '@chatenium/chatenium-sdk/services/sessionManager'; import {Session} from '@chatenium/chatenium-sdk/domain/sessionManager.schema'; +import {ChatService} from '@chatenium/chatenium-sdk/services/chatService'; +import {Chat} from '@chatenium/chatenium-sdk/domain/chatService.schema'; +import {DMService} from '@chatenium/chatenium-sdk/services/dmService'; @Injectable({ providedIn: 'root', @@ -15,4 +18,22 @@ export class ServiceManager { sessionManager = new SessionManager(this.database.getApi(), this.keyring.getApi(), this.keyValue.getApi()) currentSession = signal(null) + + chatService: ChatService | null = null // Initialized in dm-list.ts + chatsStatus = signal(LoadStatus.loading) + chats = signal([]) + + dmServices = signal>({}) +} + +export enum LoadStatus { + loading = 0, + loaded = 1, + error = 2, + updating = 3, +} + +export interface DmStorage { + service: DMService + chatData: Chat } diff --git a/src/styles.scss b/src/styles.scss index b51f3d6..bb60e14 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -20,3 +20,9 @@ body { src: url("/Onest-ExtraBold.ttf"); font-weight: bold; } + +@font-face { + font-family: "Onest"; + src: url("/Onest-SemiBold.ttf"); + font-weight: 600; +}