Started implementing Chatenium Pictures
This commit is contained in:
@@ -9,6 +9,7 @@ import {Privacy} from './privacy/privacy';
|
||||
import {TOS} from './tos/tos';
|
||||
import {Network} from './chat/network/network';
|
||||
import {Text} from './chat/network/channel/text/text';
|
||||
import {See} from './chat/picture/see/see';
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: '', component: Homepage},
|
||||
@@ -18,6 +19,7 @@ export const routes: Routes = [
|
||||
{
|
||||
path: 'chat', component: Chat, canActivate: [authNeededGuard], children: [
|
||||
{path: 'dm/:chatid', component: Dm},
|
||||
{path: 'picture/:uploaderId', component: See},
|
||||
{
|
||||
path: 'network/:networkId', component: Network, children: [
|
||||
{path: ":categoryId/:channelId", component: Text}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<button>
|
||||
<tui-icon icon="@tui.network"/>
|
||||
</button>
|
||||
<button disabled style="pointer-events: none; opacity: 0.5">
|
||||
<button>
|
||||
<tui-icon icon="@tui.image"/>
|
||||
</button>
|
||||
</tui-segmented>
|
||||
@@ -47,13 +47,17 @@
|
||||
<network-list [token]="serviceManager.currentSession()!.token"
|
||||
[userid]="serviceManager.currentSession()!.userData.userid"></network-list>
|
||||
}
|
||||
@case (2) {
|
||||
<app-picture-list [token]="serviceManager.currentSession()!.token"
|
||||
[userid]="serviceManager.currentSession()!.userData.userid"></app-picture-list>
|
||||
}
|
||||
}
|
||||
</main>
|
||||
</aside>
|
||||
|
||||
<main id="content">
|
||||
<div id="content_tint">
|
||||
@if (router.url.startsWith("/chat/dm")) {
|
||||
@if (router.url.startsWith("/chat/dm") && router.url.startsWith("/chat/picture")) {
|
||||
<!-- To ensure data is loaded -->
|
||||
@defer (when serviceManager.chatsStatus() != LoadStatus.loading) {
|
||||
<router-outlet/>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {TranslatePipe, TranslateService} from '@ngx-translate/core';
|
||||
import {environment} from '../../environments/environment';
|
||||
import {TuiTabBarComponent, TuiTabBarItem} from '@taiga-ui/addon-mobile';
|
||||
import {NetworkList} from './network-list/network-list';
|
||||
import {PictureList} from './picture-list/picture-list';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat',
|
||||
@@ -29,7 +30,8 @@ import {NetworkList} from './network-list/network-list';
|
||||
TranslatePipe,
|
||||
TuiTabBarComponent,
|
||||
TuiTabBarItem,
|
||||
NetworkList
|
||||
NetworkList,
|
||||
PictureList
|
||||
],
|
||||
templateUrl: './chat.html',
|
||||
styleUrl: './chat.scss',
|
||||
@@ -72,6 +74,8 @@ export class Chat implements OnInit {
|
||||
async ngOnInit() {
|
||||
if (this.router.url.startsWith("/chat/network")) {
|
||||
this.navigationActiveIndex = 1
|
||||
} else if (this.router.url.startsWith("/chat/picture")) {
|
||||
this.navigationActiveIndex = 2
|
||||
}
|
||||
|
||||
this.indexedDb.openDatabase().then(async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DmList } from './dm-list';
|
||||
|
||||
describe('DmList', () => {
|
||||
describe('PictureList', () => {
|
||||
let component: DmList;
|
||||
let fixture: ComponentFixture<DmList>;
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
:host {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
height: 100%;
|
||||
|
||||
img {
|
||||
::ng-deep img {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
16
src/app/chat/picture-list/picture-list.html
Normal file
16
src/app/chat/picture-list/picture-list.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<button disabled tuiButton appearance="secondary" iconStart="@tui.mail-plus">
|
||||
{{"chat.chatnav.dmList.newChat"|translate}}
|
||||
</button>
|
||||
|
||||
@for (chat of serviceManager.chats(); track chat.chatid) {
|
||||
<button [class.enlarge]="breakpoint() == 'mobile'" tuiButton [appearance]="router.url == '/chat/picture/' + chat.userid ? 'primary' : 'flat'" [routerLink]="'/chat/picture/' + chat.userid">
|
||||
<oimg [src]="chat.pfp" height="35px" width="35px" [radius]="10"></oimg>
|
||||
<div class="info">
|
||||
@if (chat.displayName == "") {
|
||||
<span>{{'@'+chat.username}}</span>
|
||||
} @else {
|
||||
<span>{{chat.displayName}}</span>
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
}
|
||||
28
src/app/chat/picture-list/picture-list.scss
Normal file
28
src/app/chat/picture-list/picture-list.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
font-weight: 600;
|
||||
|
||||
&.enlarge {
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: start;
|
||||
|
||||
.latest_message {
|
||||
margin-top: -5px;
|
||||
font-size: 12px;
|
||||
opacity: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/app/chat/picture-list/picture-list.spec.ts
Normal file
22
src/app/chat/picture-list/picture-list.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PictureList } from './picture-list';
|
||||
|
||||
describe('PictureList', () => {
|
||||
let component: PictureList;
|
||||
let fixture: ComponentFixture<PictureList>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PictureList],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PictureList);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
41
src/app/chat/picture-list/picture-list.ts
Normal file
41
src/app/chat/picture-list/picture-list.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
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 {TUI_BREAKPOINT, 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-picture-list',
|
||||
imports: [
|
||||
TuiButton,
|
||||
Oimg,
|
||||
RouterLink,
|
||||
TranslatePipe
|
||||
],
|
||||
templateUrl: './picture-list.html',
|
||||
styleUrl: './picture-list.scss',
|
||||
})
|
||||
export class PictureList implements OnInit {
|
||||
userid = input<string>("")
|
||||
token = input<string>("")
|
||||
|
||||
indexedDb = inject(IndexedDB)
|
||||
router = inject(Router)
|
||||
serviceManager = inject(ServiceManager)
|
||||
breakpoint = inject(TUI_BREAKPOINT)
|
||||
|
||||
async ngOnInit() {
|
||||
this.serviceManager.chatService = new ChatService(this.userid(), this.token(), this.indexedDb.getApi(), () => {})
|
||||
try {
|
||||
this.serviceManager.chats.set(await this.serviceManager.chatService.get())
|
||||
this.serviceManager.chatsStatus.set(LoadStatus.loaded)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.serviceManager.chatsStatus.set(LoadStatus.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/app/chat/picture/see/see.html
Normal file
35
src/app/chat/picture/see/see.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<main>
|
||||
@defer (when store) {
|
||||
<navbar backButtonDest="/chat">
|
||||
<div class="data">
|
||||
<oimg [src]="store.uploaderData().pfp" height="50px" width="50px" [radius]="15"></oimg>
|
||||
<div class="uploader-data">
|
||||
@if (store.uploaderData().displayName == "") {
|
||||
<span class="main-name">{{'@'+store.uploaderData().username}}</span>
|
||||
} @else {
|
||||
<span class="main-name">{{store.uploaderData().displayName}}</span>
|
||||
<span class="alt-name">{{'@'+store.uploaderData().username}}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="items-right">
|
||||
</div>
|
||||
</navbar>
|
||||
|
||||
<main>
|
||||
@for (album of store.albums(); track album) {
|
||||
<div class="album">
|
||||
<masonry [maxColSize]="3" style="height: 300px; pointer-events: none;">
|
||||
@for (file of album.images; track file) {
|
||||
<img [src]="file.path+'_thumbnail.png'" style="filter: blur(5px)"/>
|
||||
}
|
||||
</masonry>
|
||||
<div class="album-name">
|
||||
<h2>{{album.name}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</main>
|
||||
}
|
||||
</main>
|
||||
69
src/app/chat/picture/see/see.scss
Normal file
69
src/app/chat/picture/see/see.scss
Normal file
@@ -0,0 +1,69 @@
|
||||
main {
|
||||
height: 98svh;
|
||||
display: grid;
|
||||
grid-template-rows: 70px minmax(0, 1fr) auto;
|
||||
padding: 15px;
|
||||
|
||||
navbar {
|
||||
.uploader-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
|
||||
.album {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: var(--tui-background-base-alt);
|
||||
border: 2px solid var(--tui-border-normal);
|
||||
border-radius: 30px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.album-name {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: color-mix(in srgb, var(--tui-background-base) 50%, transparent);
|
||||
padding: 10px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/app/chat/picture/see/see.spec.ts
Normal file
22
src/app/chat/picture/see/see.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { See } from './see';
|
||||
|
||||
describe('See', () => {
|
||||
let component: See;
|
||||
let fixture: ComponentFixture<See>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [See],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(See);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
66
src/app/chat/picture/see/see.ts
Normal file
66
src/app/chat/picture/see/see.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import {Component, inject, signal} from '@angular/core';
|
||||
import {DmStorage, PictureStorage, ServiceManager} from '../../../service-manager';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {IndexedDB} from '../../../storage/indexed-db';
|
||||
import {TUI_BREAKPOINT, TuiButton, TuiIcon} from '@taiga-ui/core';
|
||||
import {PictureService} from '@chatenium/chatenium-sdk/services/pictureService';
|
||||
import {Navbar} from '../../elements/navbar/navbar';
|
||||
import {Oimg} from '../../elements/oimg/oimg';
|
||||
import {Masonry} from '../../elements/masonry/masonry';
|
||||
|
||||
@Component({
|
||||
selector: 'app-see',
|
||||
imports: [
|
||||
Navbar,
|
||||
Oimg,
|
||||
TuiButton,
|
||||
TuiIcon,
|
||||
Masonry
|
||||
],
|
||||
templateUrl: './see.html',
|
||||
styleUrl: './see.scss',
|
||||
})
|
||||
export class See {
|
||||
serviceManager = inject(ServiceManager)
|
||||
route = inject(ActivatedRoute)
|
||||
indexedDb = inject(IndexedDB)
|
||||
breakpoint = inject(TUI_BREAKPOINT)
|
||||
|
||||
uploaderId = ""
|
||||
|
||||
get store(): PictureStorage {
|
||||
return this.serviceManager.pictureServices()[this.uploaderId]
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params.subscribe(async params => {
|
||||
const uploaderId = params['uploaderId'];
|
||||
this.uploaderId = uploaderId;
|
||||
|
||||
const session = this.serviceManager.currentSession();
|
||||
if (!session) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.serviceManager.pictureServices()[uploaderId]) {
|
||||
const newService = new PictureService(
|
||||
session.token,
|
||||
uploaderId,
|
||||
session.userData.userid,
|
||||
this.indexedDb.getApi(),
|
||||
);
|
||||
|
||||
const uploaderInfo = await newService.get()
|
||||
|
||||
this.serviceManager.pictureServices.update(services => ({
|
||||
...services,
|
||||
[uploaderId]: {
|
||||
albums: signal(uploaderInfo.pictures),
|
||||
uploaderData: signal(uploaderInfo.userData),
|
||||
service: newService,
|
||||
} as PictureStorage
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ import {NetworkService} from '@chatenium/chatenium-sdk/services/networkService';
|
||||
import {Network, NetworkCategory, NetworkChannel} from '@chatenium/chatenium-sdk/domain/networkService.schema';
|
||||
import {TextChannelServiceService} from '@chatenium/chatenium-sdk/services/textChannelService';
|
||||
import {Message as NetworkMessage} from '@chatenium/chatenium-sdk/domain/textChannelService.schema';
|
||||
import {PictureService} from '@chatenium/chatenium-sdk/services/pictureService';
|
||||
import {Album} from '@chatenium/chatenium-sdk/domain/pictureService.schema';
|
||||
import {PublicUserData} from '@chatenium/chatenium-sdk/domain/common.schema';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -25,7 +28,7 @@ export class ServiceManager {
|
||||
sessionManager = new SessionManager(this.database.getApi(), this.keyring.getApi(), this.keyValue.getApi())
|
||||
currentSession = signal<Session | null>(null)
|
||||
|
||||
chatService: ChatService | null = null // Initialized in dm-list.ts
|
||||
chatService: ChatService | null = null // Initialized in picture-list.ts
|
||||
chatsStatus = signal<LoadStatus>(LoadStatus.loading)
|
||||
chats = signal<Chat[]>([])
|
||||
|
||||
@@ -36,6 +39,7 @@ export class ServiceManager {
|
||||
networkServices = signal<Record<string, NetworkStorage>>({})
|
||||
|
||||
dmServices = signal<Record<string, DmStorage>>({})
|
||||
pictureServices = signal<Record<string, PictureStorage>>({})
|
||||
}
|
||||
|
||||
export enum LoadStatus {
|
||||
@@ -53,6 +57,12 @@ export interface DmStorage {
|
||||
wsListener: (action: string, message: string) => void
|
||||
}
|
||||
|
||||
export interface PictureStorage {
|
||||
service: PictureService
|
||||
albums: WritableSignal<Album[]>
|
||||
uploaderData: WritableSignal<PublicUserData>
|
||||
}
|
||||
|
||||
export interface NetworkStorage {
|
||||
service: NetworkService
|
||||
networkData: WritableSignal<Network>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {DatabaseAPI} from '@chatenium/chatenium-sdk/storage/database';
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IndexedDB {
|
||||
private dbVersion = 2
|
||||
private dbVersion = 3
|
||||
private db: IDBDatabase | null = null
|
||||
|
||||
getApi(): DatabaseAPI {
|
||||
@@ -36,6 +36,7 @@ export class IndexedDB {
|
||||
db.createObjectStore('files', { keyPath: 'id' })
|
||||
db.createObjectStore('messages', { keyPath: 'id' })
|
||||
db.createObjectStore('networkmessages', { keyPath: 'id' })
|
||||
db.createObjectStore('pictures', { keyPath: 'id' })
|
||||
}
|
||||
|
||||
request.onsuccess = (event: Event) => {
|
||||
|
||||
Reference in New Issue
Block a user